目錄
9.1 特殊條件排序
第七章中我們用 中的 sort 函數來排序。但是它只能依數值由小到大排序。可是如果遇到特殊條件的排序,Sort 就不知道要如何去排了。
d750. 11321 - Sort! Sort!! and Sort!!!
這題的排序條件很詭異。
//11321 - Sort! Sort!! and Sort!!! (by Snail)
#include <iostream>
#include <algorithm> // for sort
using namespace std;
int m; // cmp 中要用到 m
// 故宣告為全域
bool cmp (int a, int b) {
if (a%m != b%m) // 若餘數不等
return a%m < b%m; // 按餘數排序
if ((a+b)%2) // 若一奇一偶
return (a&1) > (b&1); // 奇數在前
if (a%2) // 同為奇數
return a > b; // 大在前
return a < b; // 否則小在前
}
int main () {
int i, n, a[10000];
while (cin >> n >> m, n) {
for (i=0; i<n; i++)
cin >> a[i];
sort (a, a+n, cmp);
cout << n << ' ' << m << endl;
for (i=0; i<n; i++)
cout << a[i] << endl;
}
cout << "0 0\n";
}
d731. 11039 - Building designing
d385. 10905 - Children’s Game
2005 NPSC 國中組決賽 E. 誰先晚餐 (高中組決賽 pE)
9.2 字典序
d829. 146 - ID Codes
給你一串陣列,要你找出下一個排列
規則
- 找出最右邊一組 a[x] 小於 a[x+1]
- 找不到時已經到最大排列,停止搜尋
- 否則找出在 a[x] 右邊大於 a[x] 的最小值 a[y]
- 交換 a[x] 和 a[y]
- 翻轉 a[x] 的序列
- 所得排列即得所求
以下是範例程式碼
#include <iostream>
using namespace std;
#define N 5
#define INF 100000
int a[N] = {1, 2, 3, 4, 5};
void reserve(int i, int j) {
int b[N], x, y;
for (x = 0; x <= j - i; x++)
b[i + x] = a[j - x];
for (y = i; y <= j; y++)
a[y] = b[y];
return;
}
bool find_next() {
int i, j, t;
for (i = N - 2; i >= 0; i--)
if (a[i] < a[i + 1])break;
if (i == -1)return 0;
int min_value = INF, min_pos = -1;
for (j = i + 1; j < N; j++) {
if (a[j]>a[i] && a[j] < min_value) {
min_pos = j;
min_value = a[j];
}
}
t = a[i], a[i] = a[min_pos], a[min_pos] = t; // swap
reserve(i + 1, min_pos);
}
int main() {
while (find_next()) {
for (int i = 0; i < N; i++) {
if (i)cout << ' ';
cout << a[i];
}
cout << endl;
}
}
next_permutation
c++ 裡已有內建字典序排列,是在的 next_permutation
範例如下
#include <iostream>
#include <algorithm>
using namespace std;
#define N 5
int a[N] = {1, 2, 3, 4, 5};
int main() {
while (next_permutation(a, a+N)) {
for (int i = 0; i < N; i++) {
if (i)cout << ' ';
cout << a[i];
}
cout << endl;
}
}
是不是簡潔許多?
9.3 排序演算法
在第七章使用 “algorithm” 裡的 sort 解題目時,你可能遇到 TLE ,這時你可能會懷疑是不是自己寫出無限迴圈,但其實是 sort 讓你超過時間了,在 Wiki 的排序演算法內有各種演算法,例如 sort 是內省排序。這些演算法各有優缺點,有的省時,有的省空間,有的很好寫,以下介紹幾種
氣泡排序
氣泡排序是一種最基本的排序,是兩兩元素做比較然後交換。時間複雜度為 O(n^2),空間複雜度為 O(n)。
範例程式碼
function bubble_sort (array, length) {
var i, j;
for (i from 0 to length-1) {
for (j from 0 to length-1-i) {
if (array[j] > array[j+1])
swap(array[j], array[j+1])
}
}
}
//from wiki
合併排序
合併排序法是使用分治法的概念,先將陣列分成兩半,再將兩個小陣列進行比大小,合併成一個陣列,以此類推,時間複雜度為 O(n log n),空間複雜度為 O(n)
範例程式碼
#define L 500010
int arr[L], buf[L];
function mergesort(int left, int right) {
if (right - left <= 1)return 0;
int middle = (right + left) / 2;
mergesort(left, middle);
mergesort(middle, right);
int i = left, j = middle, k = left;
while (i < middle || j < right) {
if (i >= middle)
buf[k] = arr[j++];
else if (j >= right)
buf[k] = arr[i++];
else {
if (arr[i]<=arr[j])
buf[k] = arr[i++];
else
buf[k] = arr[j++];
}
k++;
}
for (int k = left; k < right; k++)
arr[k] = buf[k];
}
計數排序
計數排序是一種線性時間排序算法,須先開一個陣列來計算各個數字的總數,再開另一個陣列,把數字由小(大)到大(小)填入,時間複雜度為 O(n+k),空間複雜度為 O(n+k),n 為陣列大小,k 為數字範圍。
範例程式碼
function countingsort() {
int arr[M], a[N]
memset(arr, 0, sizeof(arr));
int x, i = 0;
for (; i<n; i++) {
scanf("%d", &x);
arr[x]++;
}
for (i = x = 0; i<M; i++)
while (arr[i]--)
b[x++] = i;
}
補充資料
- 演算法筆記 - Sequence Manipulation
- Wikipedia 排序演算法