- 插入排序
- 直接插入排序
实现代码:
性能分析:void InsertSort(SqList &L) { for (int i = 2; i <= L.length; i++) { if (L.r[i].key < L.r[i - 1].key) { L.r[0] = L.r[i];//哨兵 for (int j = i - 1; L.r[0].key < L.r[j].key; j--) { L.r[j + 1] = L.r[j]; } L.r[j + 1] = L.r[0]; } } }
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定 - 二分插入排序(折半插入排序)
思想:在直接插入排序的基础上,将查找插入位置的方法由顺序查找改为折半查找
实现代码:
性能分析:void BinInsertSort(SqList &L) { for (int i = 2; i <= L.length; i++) { L.r[0] = L.r[i]; int low = 1, high = i - 1; while (low <= high) { int mid = (low + high) / 2; if (L.r[0].key < L.r[mid].key) { high = mid - 1; } else { low = mid + 1; } } for (int j = i - 1; j >= high + 1; j--) { L.r[j + 1] = L.r[j]; } L.r[high + 1] = L.r[0]; } }
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定 - 希尔排序
思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
实现代码:
性能分析:主程序: void ShellSort(SqList &L,int dlta[],int t) { for (int k = 0; k < t; k++) { ShellInsert(L, dlta[k]);//dlta为增量序列 } } 子程序: // 希尔插入排序算法 void ShellInsert(SqList &L, int dk) { // 从dk+1开始,依次将每个元素插入到已经排好序的序列中 for (int i = dk + 1; i <= L.length; i++) { // 如果当前元素小于前dk个元素中的最小值 if (L.r[i].key < L.r[i - dk].key) { // 将当前元素保存到L.r[0]中 L.r[0] = L.r[i]; // 从i-dk开始,依次向前比较,找到当前元素应该插入的位置 for (int j = i - dk; j > 0 && L.r[0].key < L.r[j].key; j -= dk) { // 将比当前元素大的元素向后移动 L.r[j + dk] = L.r[j]; } // 将当前元素插入到找到的位置 L.r[j + dk] = L.r[0]; } } }
时间复杂度:O(n^(1.3~2))
空间复杂度:O(1)
稳定性:不稳定
- 直接插入排序
- 交换排序
- 冒泡排序
代码实现:
改进的冒泡算法:void bubbleSort(SqList &L) { int m, i, j; RedType x; // 交换时临时存储 for (m = 1; m <= L.length - 1; m++) { // 总共需m趟 for (j = 1; j <= L.length - m; j++) { if (L.r[j].key > L.r[j + 1].key) { // 发生逆序 x = L.r[j]; // 交换 L.r[j] = L.r[j + 1]; L.r[j + 1] = x; } } } }
性能分析:void bubbleSort(SqList &L) { int m, i, j, flag = 1; // flag作为是否有交换的标记 RedType x; // 交换时临时存储 for (m = 1; m <= L.length - 1 && flag == 1; m++) { flag = 0; // 假设本趟没有发生交换 for (j = 1; j <= L.length - m; j++) { if (L.r[j].key > L.r[j + 1].key) { // 发生逆序 flag = 1; // 发生交换,flag置为1 x = L.r[j]; // 交换 L.r[j] = L.r[j + 1]; L.r[j + 1] = x; } } } }
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定 - 快速排序
基本思想:通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
代码实现:
性能分析:void QuickSort(SqList &L, int low, int high) { if (low < high) { int pivotpos = Partition(L, low, high); QuickSort(L, low, pivotpos - 1); QuickSort(L, pivotpos + 1, high); } } int Partition(SqList &L, int low, int high) { RedType pivot = L.r[low]; // 选择第一个元素作为枢轴 while (low < high) { while (low < high && L.r[high].key >= pivot.key) { high--; } L.r[low] = L.r[high]; // 将比枢轴小的元素移到左边 while (low < high && L.r[low].key <= pivot.key) { low++; } L.r[high] = L.r[low]; // 将比枢轴大的元素移到右边 } L.r[low] = pivot; // 将枢轴元素放到最终位置 return low; // 返回枢轴元素的位置 }
不是原地排序,递归调用需要用到系统栈
时间复杂度:O(nlogn)
空间复杂度:O(logn)
稳定性:不稳定
不适用于对原本有序或基本有序的序列进行排序
- 冒泡排序
- 选择排序
-
简单选择排序
思想:在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
代码实现:void SelectSort(SqList &L) { int min; for (int i = 0; i < L.length - 1; i++) { min = i; // 假设当前元素最小 for (int j = i + 1; j < L.length; j++) { if (L.r[j].key < L.r[min].key) { min = j; // 找到更小的元素 } } if (min != i) { // 交换当前元素和最小元素 RedType temp = L.r[i]; L.r[i] = L.r[min]; L.r[min] = temp; } } }
性能分析:
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定 -
堆排序
- 堆的定义:实质上是满足以下条件的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
-
堆的调整:将堆的根结点与堆的最后一个结点交换,然后将剩余的结点重新调整为堆。
-
筛选过程的算法为:
void HeapAdjust(SqList &L, int s, int m) { RedType rc = L.r[s]; // 将要调整的结点值保存 for (int j = 2 * s; j <= m; j *= 2) { // 从左孩子开始 if (j < m && L.r[j].key < L.r[j + 1].key) { j++; // 如果右孩子比左孩子大,则j指向右孩子 } if (rc >= L.r[j].key) { break; // 如果rc大于等于孩子结点,则调整结束 } L.r[s] = L.r[j]; // 否则,将孩子结点上移 s = j; // 继续向下筛选 } L.r[s] = rc; // 将保存的结点值放到最终位置 }
- 堆排序算法:
void HeapSort(elem R[]) { int n = sizeof(R) / sizeof(R[0]); for (int i = n / 2; i > 0; i--) { HeapAdjust(R, i, n); } for (int i = n; i > 1; i--) { swap(R[1], R[i]); HeapAdjust(R, 1, i - 1); } }
性能分析:
时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定 - 堆的定义:实质上是满足以下条件的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
-
- 归并排序
思想:将两个(或更多)有序表合并成一个新的有序表。
代码实现:
性能分析:void MergeSort(SqList &L) { MergePass(L.r, L.length, 1); } void MergePass(RedType R[], int n, int s) { int i; for (i = 1; i <= n - 2 * s + 1; i += 2 * s) { Merge(R, i, i + s - 1, i + 2 * s - 1); } if (i + s - 1 < n) { Merge(R, i, i + s - 1, n); } } void Merge(RedType R[], int low, int mid, int high) { RedType *temp = new RedType[high - low + 1]; int i = low, j = mid + 1, k = 0; while (i <= mid && j <= high) { if (R[i].key <= R[j].key) { temp[k++] = R[i++]; } else { temp[k++] = R[j++]; } } while (i <= mid) { temp[k++] = R[i++]; } while (j <= high) { temp[k++] = R[j++]; } for (int m = 0; m < k; m++) { R[low + m] = temp[m]; } delete[] temp; }
时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定性:稳定 - 基数排序
思想:将待排序的元素按关键码的某一位进行“分配”,分配到不同的“桶”中;然后,再按桶号从小到大顺序收集起来;最后,再对每个桶中的元素按同样的方法进行排序;重复以上过程,直到按关键码的最高位排序完成。 - 外部排序
06-06
6000
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
11-06
3122
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
10-25
1879
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)