目录
一、排序总结
1、排序的稳定性:
- 稳定:插、插、泡、归、基
- 不稳定:快、选、希、堆
2、每趟至少确定一个元素最终位置的排序有
- 选择排序、冒泡排序、堆排序、快排(确定枢轴)
3、顺序存储与链式存储:
- 仅顺序存储:折半插入排序、希尔排序、快速排序、堆排序
- 适合顺序存储和链式存储:直接插入排序、简单选择排序、归并排序、基数排序
4、排序的元素个数:
- n较小时:直接插入排序(比较次数少)、简单选择排序(移动次数少)
- n较大时:快速排序、堆排序、归并排序
5、选取排序需要考虑的因素:
- 待排序元素个数n、元素初始状态、关键字的结构及其分布状态
- 稳定性的要求、存储结构及辅助空间的大小限制等。
6、算法的稳定性与算法优劣无关。(稳定排序不一定优于不稳定)
|----------|---------------|---------------|---------------|------------|
| 排序 | 最好时间 | 平均时间 | 最坏时间 | 空间复杂度 |
| 插入排序 | O(n) | O(n*n) | O(n*n) | O(1) |
| 冒泡排序 | O(n) | O(n*n) | O(n*n) | O(1) |
| 选择排序 | O(n*n) | O(n*n) | O(n*n) | O(1) |
| 希尔排序 | O(n^1.3) ||| O(1) |
| 快速排序 | O(n*lgn) | O(n*lgn) | O(n*n) | O(lgn) |
| 堆排序 | O(n*lgn) | O(n*lgn) | O(n*lgn) | O(1) |
| 归并排序 | O(n*lgn) | O(n*lgn) | O(n*lgn) | O(n) |
| 基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(r) |
1、总比较次数与初始状态无关的是:
- 选择排序、归并排序、基数排序(不基于比较和移动排序)
- 折半插入排序的折半减少了比较次数,但这个折半与初始状态无关,仅取决于元素个数。
2、总移动次数与初始状态无关的是:
- 归并排序、基数排序(不基于比较和移动排序)
3、排序趟数与初始状态有关的排序:(注:排序趟数和时间复杂度的区别)
- 快速排序、冒泡排序**(直接插入排序无关,每趟插入一个元素,需要n-1趟)**
4、时间复杂度与初始状态无关的排序:
- 选择排序、堆排序、归并排序、基数排序
二、插入的排序
1、插入排序的三种情况:
- 直接插入排序、折半插入排序、希尔排序
2、直接插入排序:
- **适应于基本有序****O(N)**或排序记录较小。
- 适用于顺序存储和链式存储。
- 局部有序,不能提高直接插入的时间性能
3、折半插入排序:
- 减少了比较次数,但没有减少移动次数,平均仍为O(n^n).
- 仅适用于顺序存储**,涉及二分故不能链式。**
**4、希尔排序:**缩小增量排序
- 希尔排序开始时增量较大,每个子序列中的元素个数较少,从而排序速度快;
- 当增量较小时,虽然每个子序列中的元素个数较多,但元素已经基本有序故排序速度也较快。
三、交换的排序
1、交换排序的两种情况:
- 冒泡排序、快速排序
2、冒泡排序:
3、快速排序:分治思想
- 快排运行效率:取决于划分过程的对称性。(若能尽可能均分,树尽可能是对称的,则速度最快)
- 每次排序都可以将枢纽元素放入最终最终位置。
- 当元素已经有序或逆序时速度最慢。****(单支数最慢)
4、快排可能的排序的结果
- 若有头尾的最大或最小值相等,第二趟最少只需要2个相等的。
- 若不包含头尾的最大值最小值,则第二趟最少需要3个相等的。
cpp
#include <iostream>
using namespace std;
int partition(int a[], int left, int right) {
int pivot = a[left];
while (left < right) {
while (left < right && a[right] >= pivot) right--;
a[left] = a[right];
while (left < right && a[left] <= pivot) left++;
a[right] = a[left];
}
a[left] = pivot;
return left;
}
void quickSort(int a[], int left, int right) {
if (left < right) {
int pivotIndex = partition(a, left, right);
quickSort(a, left, pivotIndex - 1);
quickSort(a, pivotIndex + 1, right);
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
quickSort(a, 0, n - 1);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
四、选择的排序
1、交换排序的两种情况:
- 选择排序、堆排序
2、选择排序:
- 每次遍历一趟,找到最大或最小值的下标,遍历完一趟最后再对下标进行交换。
3、堆排序:
- 堆排序是完全二叉树,但不是二叉排序树。(左不一定比右大)
- 建堆O(n),删除结点O(lgn),插入节点O(lgn)。
4、堆的插入和删除:
- 插入(上浮):插入点依次与其根比较,一直往上走,不需要与其根的左孩子进行比较。(注:与删的区别)
- 删除(下落):先交换,然后拿根与左右孩子比较,找小的那棵树,然后依次比较。(注:插和删除的区别)
- 输出某一关键字后的剩余堆,需要的是重新排完序的一串数字。(最短路那个也一样)
cpp
#include <iostream>
using namespace std;
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void HeapAdjust(int a[], int n, int i) {//调整堆
int max = i;
int lchild = 2 * i + 1; // 左孩子
int rchild = 2 * i + 2; // 右孩子
if (lchild < n && a[lchild] > a[max]) max = lchild;
if (rchild < n && a[rchild] > a[max]) max = rchild;
if (max != i) {
swap(a[max], a[i]);
HeapAdjust(a, n, max);
}
}
void HeapSort(int a[], int n) {// 堆排序
for (int i = n / 2 - 1; i >= 0; i--) {
HeapAdjust(a, n, i);
}
for (int i = n - 1; i > 0; i--) {
swap(a[0], a[i]);
HeapAdjust(a, i, 0);
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
HeapSort(a, n);
for (int i = 0; i < n; i++)
cout << a[i] << " ";
return 0;
}
五、归并排序
1、归并排序:分治思想
- 将两个或两个以上的有序表合并成一个新的有序表。
- 每趟归并时间O(n),共需要lng趟。
六、基数排序
1、基数排序(计数排序):
- 借助多关键字排序的思想对逻辑关键字进行排序的方法。
- 不基于比较和移动,而仅仅基于关键字大小排序。
- 当n很大时,记录的关键字位数较少且可以分解,必须都为整数。
2、两种基数排序方法:
- 最低位优先(LSD):从最低有效位开始(个、十、百....),按顺序引入对应的数字队列中,连接成一个有序序列,以此类推。
- 最高位优先(MSD):从最高数字位开始。
3、性能分析:
- 辅助空间为需要的r个队列,即O(r)
- 需要d趟分配和收集,一趟分需要O(n),一趟收集需要O(r),所以时间为O(d(n+r))
七、计数排序(空缺)
- 欠
八、排序代码
1、插入的排序
cpp
#include <iostream>
using namespace std;
void insertionSort(int a[], int n){
int i, j, key;
for (i = 1; i < n; i++){
key = a[i];
for (j = i - 1; j >= 0 && a[j] > key; j--){
a[j + 1] = a[j];
}
a[j + 1] = key;
}
}
int main(){
int a[] = {12, 11, 13, 5, 6};
int n = sizeof(a) / sizeof(a[0]);
insertionSort(a, n);
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
return 0;
}
折半插入
cpp
#include <iostream>
using namespace std;
void InsertSort(int a[], int n) {
int i, left, right, mid, j;
for (i = 2; i <= n; i++) {
a[0] = a[i];
left = 1;
right = i - 1;
while (left <= right){
mid = (left + right) / 2;
if (a[mid] > a[0]) right = mid - 1;
else left = mid + 1;
}
for (j = i - 1; j >= left; j--) {
a[j + 1] = a[j];
}
a[left] = a[0];
}
}
int main() {
int a[] = {4, 3, 2, 10, 12, 1, 5, 6};
int n = sizeof(a) / sizeof(a[0]);
InsertSort(a, n);
for (int i = 1; i <= n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
希尔
cpp
#include <iostream>
using namespace std;
void shell_sort(int a[], int n) {
int i, j, temp, d;
for (d = n / 2; d >= 1; d /= 2) {
for (i = d; i < n; i++) {
temp = a[i];
// 直接插入排序
for (j = i - d; j >= 0 && a[j] > temp; j -= d) {
a[j + d] = a[j];
}
a[j + d] = temp;
}
}
}
int main() {
int a[] = {12, 34, 54, 2, 3};
int n = sizeof(a) / sizeof(a[0]);
shell_sort(a, n);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
2、交换的排序
冒泡
cpp
#include <iostream>
using namespace std;
void bubbleSort(int a[], int n) {
int i, j, temp;
for (i = 0; i < n-1; i++) {
int flag = 0;
for (j = 0; j < n-1-i; j++) {
if (a[j] > a[j+1]) {
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = 1;
}
}
if (flag == 0) return;
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
bubbleSort(a, n);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
快排
cpp
#include <iostream>
using namespace std;
int partition(int a[], int left, int right) {
int pivot = a[left];
while (left < right) {
while (left < right && a[right] >= pivot) right--;
a[left] = a[right];
while (left < right && a[left] <= pivot) left++;
a[right] = a[left];
}
a[left] = pivot;
return left;
}
void quickSort(int a[], int left, int right) {
if (left < right) {
int pivotIndex = partition(a, left, right);
quickSort(a, left, pivotIndex - 1);
quickSort(a, pivotIndex + 1, right);
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
quickSort(a, 0, n - 1);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
3、选择的排序
选择
cpp
#include <iostream>
using namespace std;
void swap1(int &a, int &b) {// 通过引用交换两个整数
int temp = a;
a = b;
b = temp;
}
void swap2(int *a, int *b) {// 通过指针交换两个整数
int temp = *a;
*a = *b;
*b = temp;
}
void selectSort(int a[], int n) {
int i, j, k;
for (i = 0; i < n; i++) {
k = i;
for (j = i + 1; j < n; j++) { // 找当前最小值的下标
if (a[j] < a[k]) {
k = j;
}
}
swap1(a[k],a[i]); //swap2(a[k],a[i]);
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
selectSort(a, n);
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
7、堆排序
cpp
#include <iostream>
using namespace std;
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void HeapAdjust(int a[], int n, int i) {//调整堆
int max = i;
int lchild = 2 * i + 1; // 左孩子
int rchild = 2 * i + 2; // 右孩子
if (lchild < n && a[lchild] > a[max]) max = lchild;
if (rchild < n && a[rchild] > a[max]) max = rchild;
if (max != i) {
swap(a[max], a[i]);
HeapAdjust(a, n, max);
}
}
void HeapSort(int a[], int n) {// 堆排序
for (int i = n / 2 - 1; i >= 0; i--) {
HeapAdjust(a, n, i);
}
for (int i = n - 1; i > 0; i--) {
swap(a[0], a[i]);
HeapAdjust(a, i, 0);
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
HeapSort(a, n);
for (int i = 0; i < n; i++)
cout << a[i] << " ";
return 0;
}
4、归并排序
cpp
#include <iostream>
using namespace std;
void merge(int a[], int temp[], int left, int mid, int right) {
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right) {
if (a[i] <= a[j]) temp[k++] = a[i++];
else temp[k++] = a[j++];
}
while (i <= mid) temp[k++] = a[i++];
while (j <= right) temp[k++] = a[j++];
for (int p = 0; p < k; p++)
a[left + p] = temp[p];
}
void mergeSort(int a[], int temp[], int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(a, temp, left, mid);
mergeSort(a, temp, mid + 1, right);
merge(a, temp, left, mid, right);
}
}
int main() {
int a[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(a) / sizeof(a[0]);
int* temp = new int[n];
mergeSort(a, temp, 0, n - 1);
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
delete[] temp;
return 0;
}