插入排序是一种简单直观的排序算法,它的基本思想是将待排序的元素逐个插入已排序的序列中,直到所有元素都插入完毕为止。
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
arr
,初始时将arr[0]
视为已排序序列,arr[1]
到arr[n-1]
视为待排序序列。arr[i]
插入已排序序列中的正确位置。将arr[i]
与已排序序列中的元素从右往左依次比较,直到找到合适的位置插入。arr[i]
小于已排序序列中的某个元素arr[j]
,则将arr[j]
后移一位,给arr[i]
腾出插入位置。arr[i]
插入到正确位置后,已排序序列的长度增加1,待排序序列的长度减少1。以下是一个实例:
在每次插入过程中,待插入的元素会与已排序序列中的元素进行比较并移动位置,直到找到合适的位置插入。这样,通过逐步插入元素,最终实现了整个序列的排序。
void InsertSort(int* a, int n) {// [0,end]有序,把end+1位置的值插入,保持有序for (int i = 0; i < n - 1; ++i) {int end = i;int tmp = a[end + 1];while (end >= 0) {if (tmp < a[end]) {a[end + 1] = a[end];--end;}else {break;}}a[end + 1] = tmp;}
}
希尔排序法又称缩小增量法。
希尔排序是插入排序的一种改进算法,它的基本思想是将待排序的序列按照一定的间隔分组,对每个分组进行插入排序,然后缩小间隔,重复进行插入排序,直到间隔为1,最后进行一次直接插入排序。
希尔排序的关键是选择合适的间隔序列,不同的间隔序列会影响排序的效率。常用的间隔序列有希尔增量序列(gap = gap / 2)、Hibbard增量序列(gap = 2^k - 1)、Sedgewick增量序列等。选择不同的间隔序列会导致不同的时间复杂度和性能表现。
void ShellSort(int* a, int n) {int gap = n;while (gap > 1) {gap = gap / 3 + 1;for (int i = 0; i < n - gap; ++i) {int end = i;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]) {a[end + gap] = a[end];end -= gap;}else {break;}}a[end + gap] = tmp;}}
}
每一次从待排序的元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
array[i]~array[n-1]
中选择关键码最大(小)的数据元素array[i]~array[n-2](array[i+1]~array[n-1]
集合中,重复上述步骤,直到集合剩余1个元素void SelectSort(int* a, int n) {assert(a);int begin = 0, end = n - 1;while (begin < end){int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; ++i){if (a[i] < a[mini])mini = i;if (a[i] > a[maxi])maxi = i;}Swap(&a[begin], &a[mini]);if (begin == maxi) maxi = mini;Swap(&a[end], &a[maxi]);++begin;--end;}
}
将待排序数组先建成一个大根堆或小根堆,再堆该堆进行调整交换,end
初始为n-1
每次选择出堆顶最大(或最小)的数,与end--
位置的数交换。
排升序需要建大堆,排降序需要建小堆。
原因是,建大堆时,每次选择堆顶数(即最大数)与end–交换,这样数字就被从最大、次大、三大等顺序被交换到数组末尾开始往前的位置,即是升序排序,建小堆降序排列时同理。
void AdjustUp(int* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child < a[parent]]) {Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void AdjustDown(int* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (child + 1 < size && a[child + 1] < a[child]) {++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{// 向上调整建堆 O(N*logN) 比向下调整建堆慢//for (int i = 1; i < n; ++i)//{// AdjustUp(a, i);//}// 向下调整建堆 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}int end = n - 1;// O(N*logN)while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}
所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
冒泡排序是一种简单的排序算法,其核心思想是通过相邻元素的比较和交换来将最大(或最小)的元素逐渐移动到数组的一端。
void BubbleSort(int* a, int n)
{assert(a);for (int i = 0; i < n; ++i){for (int j = 1; j < n-i; ++j){if (a[j - 1] > a[j]) {Swap(&a[i - 1], &a[i]);}}}
}
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
// 三数取中
int GetMidIndex(int* a, int begin, int end)
{int mid = (begin + end) / 2;if (a[begin] < a[mid]){if (a[mid] < a[end]){return mid;}else if (a[begin] < a[end]){return end;}else{return begin;}}else // a[begin] > a[mid]){if (a[mid] > a[end]){return mid;}else if (a[begin] < a[end]) {return begin;}else {return end;}}
}// Hoare版本
int PartSort1(int* a, int begin, int end)
{int left = begin, right = end;int keyi = left;while (left < right){// 右边先走,升序找小while (left < right && a[right] >= a[keyi]){--right;}// 左边再走,找大while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;return keyi;
}
// 挖坑法
int PartSort2(int* a, int begin, int end)
{int key = a[begin];int piti = begin;while (begin < end){// 右边找小,填到左边的坑里面,这个位置形成新的坑while (begin < end && a[end] >= key){--end;}a[piti] = a[end];piti = end;// 左边找大,填到右边的坑里面,这个位置形成新的坑while (begin < end && a[begin] <= key){++begin;}a[piti] = a[begin];piti = begin;}a[piti] = key;return piti;
}
// 前后指针版本
int PartSort3(int* a, int begin, int end)
{int prev = begin;int cur = begin + 1;int keyi = begin;// 优化:加入三数取中int midi = GetMidIndex(a, begin, end);Swap(&a[keyi], &a[midi]);while (cur <= end){// cur位置的值小于keyi位置值if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}// 递归法快排
void QuickSort(int* a, int begin, int end)
{// 区间不存在,或只有一个值需要处理if (begin >= end){return;}int keyi = PartSort3(a, begin, end);// [begin, keyi-1] keyi [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}
// 递归法快排
void QuickSort(int* a, int begin, int end)
{// 区间不存在,或只有一个值需要处理if (begin >= end){return;}// 2. 小区间优化if (end - begin > 10) {int keyi = PartSort3(a, begin, end);// [begin, keyi-1] keyi [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}else{InsertSort(a + begin, end - begin + 1);}
}
void QuickSortNonR(int* a, int begin, int end)
{ST st;StackInit(&st);StackPush(&st, end);StackPush(&st, begin);while (!StackEmpty(&st)){int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);int keyi = PartSort3(a, left, right);// [left, keyi-1] keyi [keyi+1, right]if (left < keyi - 1){StackPush(&st, keyi - 1);StackPush(&st, left);}if (keyi + 1 < right){StackPush(&st, right);StackPush(&st, keyi);}}StackDestroy(&st);
}
归并排序是一种分治算法,基本思想是将待排序的序列不断划分成两个子序列,直到每个子序列只有一个元素,然后再将这些子序列合并成一个有序的序列。
void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (begin + end) / 2;// [begin, mid] [mid+1, end] 分支递归,让子区间有序_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid+1, end, tmp);// 归并[begin, mid] [mid+1, end]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];} while (begin2 <= end2){tmp[i++] = a[begin2++];}// 把归并的数据拷贝回原数组memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){printf("malloc failn");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){printf("malloc failn");exit(-1);}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){// [i, i+gap-1][i+gap, i+2*gap-1]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;if (end1 >= n || begin2 >= n){break;}else if (end2 >= n){end2 = n - 1;}int m = end2 - begin1 + 1;int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * m);}gap *= 2;}free(tmp);
}
计数排序又称鸽巢原理,是对哈希直接定址法的变形应用
使用相对映射减少空间,也可以比较负数。
void CountSort(int* a, int n)
{int min = a[0], max = a[0];for (int i = 1; i < n; ++i){if (a[i] < min){min = a[i];}if (a[i] > max){max = a[i];}}// 统计次数的数组int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);if (count == NULL){printf("malloc failn");exit(-1);}memset(count, 0, sizeof(int) * range);// 统计次数for (int i = 0; i < n; ++i){count[a[i] - min]++;}// 回写排序int j = -1;for (int i = 0; i < range; ++i){// 出现几次回写几个i+minwhile (count[i]--){a[++j] = i + min;}}
}
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
直接插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn)~O(n) | 不稳定 |
计数排序 | O(max(range, N)) | O(n) | O(range) | O(range) | 稳定 |
本文发布于:2024-02-01 19:01:49,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170678530838781.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |