- 稳定性:指该算法是否会改变序列中相同元素的相对位置。(不稳定口诀:快[快排]些[希尔]选[选择]一堆[堆排])
- 就地性(内部排序):该算法最多额外创建几个辅助变量,不再申请过多的辅助空间。亦即,就地排序算法不会新创建一个有序序列,空间复杂度为 O(1)。
算法 | 稳定性 | 就地性 | 时间复杂度 | 空间复杂度 |
---|
冒泡排序 | 稳定 | ? | O(n2) | O(1) | 插入排序 | 稳定 | ? | O(n2) | O(1) | 希尔排序 | 不稳定 | ? | O(n^1.5) | O(1) | 选择排序 | 不稳定 | ? | O(n2) | O(1) | 归并排序 | 稳定 | × | O(N*logN) | O(N) | 快速排序 | 不稳定 | ? | O(N*logN) | O(logN) | 堆排序 | 不稳定 | × | O(N*logN) | O(1) | 基数排序 | 稳定 | × | O(n*k) | O(n+k) | 桶排序 | 稳定 | × | O(n+k) | O(n+k) | 计数排序 | 稳定 | × | O(n+k) | O(k) |
冒泡排序
- 不稳定 | 就地 | O(n2) | O(1)
- 相邻两数比较,较大数下沉,较小数冒起
void BubbleSort(int arr[], int size)
{
for (int i = 0; i < size-1; i++)
{
for (int j = 0; j < size-i-1; j++)
{
if (arr[j]>arr[j + 1])
{
int temp;
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
}
}
插入排序
- 稳定 | 就地 | O(n2) | O(1)
- 假设前N-1个数已经排好序,将第N个数插入到该有序数列中,反复操作直到排序完成。
void InsertSort(int arr[], int size)
{
for (int i = 1; i < size; i++)
{
int tmp = arr[i];
for (int j = i - 1; j >= 0; j--)
{
if (tmp < arr[j])
arr[j + 1] = arr[j];
else break;
}
arr[j + 1] = tmp;
}
}
void BinInsertSort(int arr[], int size)
{
for (int i = 1; i < size; i++)
{
int tmp = arr[i];
int left = 0, right = i-1;
while (left <= right) {
int mid = left + (right - left)/2;
if ( tmp < arr[mid] ) right= mid-1;
else left = mid + 1;
}
for (j = i - 1; j >= left; j--)
arr[j + 1] = arr[j];
arr[left] = tmp;
}
}
希尔排序
- 不稳定 | 就地 | O(n^1.5) | O(1)
- 选择某一增量(数组大小/2),将待排数列分为若干子序列(例如arr[8]选择增量为4,则arr[0]和arr[4]为一组,两两一组共4组),分别对每个子序列进行插入排序。逐渐减小增量,重复操作,直到增量为1,进行最后的插入排序,排序完成。
void ShellSort(int arr[], int size)
{
for (int gap = size / 2; gap > 0; gap /= 2)
{
for (int i = gap; i < size; i++)
{
int tmp = arr[i];
for (int j = i - gap; j>=0; j-=gap)
{
if (tmp < arr[j])
arr[j + gap] = arr[j];
else break;
}
arr[j + gap] = tmp;
}
}
}
选择排序
- 不稳定 | 就地 | O(n2) | O(1)
- 第一次遍历N-1个数,找到最小值与第一个元素交换;第二次遍历N-2个数,找到最小值与第二个元素交换;直到第N-1次遍历,找到最小值与第N-1个元素交换,排序完成。
- 或者每次把最大数当道最后一个位置,直到还剩下一个数。
void SelectSort(int arr[], int size)
{
for (int i = 0; i < size - 1; i++)
{
int minIdx = i;
for (int j = i+1; j < size; j++)
{
if (arr[j]<arr[minIdx])
minIdx = j;
}
int tmp = arr[i];
arr[i] = arr[minIdx];
arr[minIdx] = tmp;
}
}
void SelectSort(int arr[], int size)
{
for (int i = size; i > 1; i--)
{
int maxIdx = 0;
for (int j = 0; j < i; j++)
{
if (arr[j]>arr[maxIdx])
maxIdx = j;
}
int tmp = arr[i - 1];
arr[i - 1] = arr[maxIdx];
arr[maxIdx] = tmp;
}
}
归并排序
- 稳定 | 非就地 | O(N*logN) | O(N)
- 将初始序列的N个元素看成N个子序列,两两归并为有序的序列,此时序列的长度为2,再两两归并为有序的序列,此时序列的长度为4,…,直至得到一个长为N的序列,排序完成。
void MergeSort(int arr[], int begin, int end)
{
if(begin>= end) return;
int mid = (begin+ end) / 2;
MergeSort(arr, begin, mid);
MergeSort(arr, mid + 1, end);
MergeArr(arr, begin, mid, end);
}
void MergeArr(int arr[], int begin, int mid, int end)
{
int *tmpArr = new int[end - begin + 1];
int i = begin, j = mid + 1, k = 0;
while (i <= mid && j <= end) {
if ( arr[i] < arr[j] ) {
tmpArr[k] = arr[i];
i++;
}
else {
tmpArr[k] = arr[j];
j++;
}
k++;
}
while (i <= mid) {
tmpArr[k] = arr[i];
i++;
k++;
}
while (j <= end) {
tmpArr[k] = arr[j];
j++;
k++;
}
i = begin;
for (int tmp = 0; tmp < k && i <= end; tmp++) {
arr[i] = tmpArr[tmp];
i++;
}
delete[] tmpArr;
tmpArr = NULL;
}
快速排序
- 不稳定 | 就地 | O(N*logN) | O(logN)
- 从待排数列中取出一个数作为key(通常选第一个数);将比key小的数全部放在其左边,大于或等于的数全部放在其右边;对左右两个小数列重复上述操作,直到各区间只有1个数,排序完成。
void QuickSort(int arr[],int left, int right)
{
if (left >= right) return;
int begin = left, end = right;
int key = arr[left];
while (begin < end)
{
while (arr[end] >= key && begin<end) end--;
while (arr[begin] <= key && begin<end) begin++;
if (begin < end)
{
int tmp = arr[begin];
arr[begin] = arr[end];
arr[end] = tmp;
}
}
arr[left] = arr[begin];
arr[begin] = key;
QuickSort(arr,left, begin - 1);
QuickSort(arr, begin + 1, right);
}
int findKey(int arr[],int left, int right)
{
int key = arr[left];
int begin = left, end = right;
while (begin < end)
{
while (arr[end] >= key && begin<end) end--;
while (arr[begin] <= key && begin<end) begin++;
if (begin < end)
{
int tmp = arr[begin];
arr[begin] = arr[end];
arr[end] = tmp;
}
}
arr[left] = arr[begin];
arr[begin] = key;
return begin;
}
void QuickSort(int arr[],int left, int right)
{
stack<int> st;
if (left < right) {
int mid = findKey(arr, left, right);
if (mid - 1 > left) {
st.push(left);
st.push(mid - 1);
}
if (mid + 1 < right) {
st.push(mid + 1);
st.push(right);
}
while (!st.empty()) {
int r = st.top();
st.pop();
int l = st.top();
st.pop();
mid = findKey(arr, l, r);
if (mid - 1 > l) {
st.push(l);
st.push(mid - 1);
}
if (mid + 1 < r) {
st.push(mid + 1);
st.push(r);
}
}
}
}
堆排序
不稳定 | 非就地 | O(N*logN) | O(1)
- 把待排序的元素放在堆中,每个结点表示一个元素;
- 找到树的最后一个非叶节点,建立初堆;
- 将根结点与堆的最后一个结点交换,之后重建堆的对象为n-1个,再把次大的结点与倒数第二个结点交换,之后重建堆的对象为n-2个,…,以此类推,直至重建堆的对象为0,排序完成。
void display (int arr[], int size)
{
for (int i = 0; i < size; i++)
cout << arr[i] << " ";
cout << endl;
}
void AdjustDwon(int arr[], int size, int root) {
int parent = root;
int child = parent*2+1;
while (child<size) {
if (child + 1 < size && arr[child + 1] > arr[child])
child ++;
if (arr[child] > arr[parent]) {
int temp = arr[parent];
arr[parent] = arr[child];
arr[child] = temp;
parent=child;
child = parent * 2 + 1;
}
else break;
}
}
void HeapSort(int arr[], int size) {
if (size == 0) return;
for (int i = (size - 2) / 2; i >= 0; i--)
AdjustDwon(arr, size, i);
display (arr, size);
for (int i = size - 1; i >= 0; --i) {
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
AdjustDwon(arr, i, 0);
}
display (arr, size);
}
基数排序
稳定 | 非就地 | O(n*k) | O(n+k)
- 对于一组数,先按照个位排序,再按照十位排序,再按照百位排序,…,直到排序完成。
int maxbit(int data[], int n)
{
int d = 1;
int p = 10;
for(int i = 0; i < n; ++i)
{
while(data[i] >= p)
{
p *= 10;
++d;
}
}
return d;
}
void radixsort(int data[], int n)
{
int d = maxbit(data, n);
int tmp[n];
int radix = 1;
for(int i = 1; i <= d; i++)
{
int count[10] = {0};
for(int j = 0; j < n; j++)
{
int idx = (data[j] / radix) % 10;
count[idx]++;
}
for(int j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j];
for(int j = n - 1; j >= 0; j--)
{
int idx = (data[j] / radix) % 10;
tmp[count[idx] - 1] = data[j];
count[idx]--;
}
for(int j = 0; j < n; j++)
data[j] = tmp[j];
radix = radix * 10;
}
}
桶排序
稳定 | 非就地 | O(n+k) | O(n+k)
- 设置有限数量的空桶,并将数据依次放入对应的空桶中;
- 对每个不为空的桶再各自进行排序(可能使用别的排序算法,或以递归方式继续使用桶排序);
- 最后将每个非空桶中排好的元素放回到原数组中。
void insert(list<int>& bucket,int val)
{
auto iter = bucket.begin();
while(iter != bucket.end() && val >= *iter) ++iter;
bucket.insert(iter,val);
}
void BucketSort_1(vector<int>& arr)
{
int len = arr.size();
if(len <= 1)
return;
int min = arr[0],max = min;
for(int i=1;i<len;++i)
{
if(min>arr[i]) min = arr[i];
if(max<arr[i]) max = arr[i];
}
int k = 10;
int bucketsNum = (max - min)/k + 1;
vector<list<int>> buckets(bucketsNum);
for(int i=0;i<len;++i)
{
int value = arr[i];
insert(buckets[(value-min)/k],value);
}
int index = 0;
for(int i=0;i<bucketsNum;++i)
{
if(buckets[i].size())
{
for(auto& value:buckets[i])
arr[index++] = value;
}
}
}
计数排序
稳定 | 非就地 | O(n+k) | O(k)
- 找到待排序数组的最大值max和最小值min;
- 统计数组中每个数字出现的次数,存入辅助数组中;
- 对辅助数组总数累计(后面的值出现的位置为前面所有值出现的次数之和);
- 逆序输出辅助数组中的值。
void countSort(vector<int>& arr)
{
int len = arr.size();
if(len == 0) return;
vector<int> tempArr(arr.begin(),arr.end());
int min = tempArr[0],max = min;
for(int i=1;i<len;++i)
{
if(min>tempArr[i])
min = tempArr[i];
if(max<tempArr[i])
max = tempArr[i];
}
const int k = max-min+1;
int count[k]={0};
for(int i=0;i<len;++i)
++count[tempArr[i]-min];
for(int i=1;i<k;++i)
count[i] +=count[i-1];
for(int i=len-1;i>=0;--i)
arr[--count[tempArr[i] - min]] = tempArr[i];
}
|