简单选择排序 思路
把数组分成两部分:前面是已排序部分,后面是未排序部分。
一开始,已排序部分为空,未排序部分是整个数组。
每一轮,从未排序部分里找到最小的数,设中间量为k,记录最小值的下标。
根据下标把这个最小的数和未排序部分的第一个数交换。
重复上面的过程,直到未排序部分只剩一个元素为止。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> void printArray (int *arr, int n) { for (int i = 0 ; i < n; i++) { printf ("%d " , *(arr + i)); } printf ("\n" ); } void selectionSort (int *arr, int n) { for (int i = 0 ; i < n - 1 ; i++) { int minIndex = i; for (int j = i + 1 ; j < n; j++) { if (*(arr + j) < *(arr + minIndex)) { minIndex = j; } } if (minIndex != i) { int temp = *(arr + i); *(arr + i) = *(arr + minIndex); *(arr + minIndex) = temp; } } } int main () { int arr[] = {5 , 3 , 8 , 4 , 2 , 7 , 1 , 6 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("排序前:" ); printArray(arr, n); selectionSort(arr, n); printf ("排序后:" ); printArray(arr, n); return 0 ; }
运行结果 1 2 排序前:5 3 8 4 2 7 1 6 排序后:1 2 3 4 5 6 7 8
直接插入排序 思路
把数组分成两部分:前面是已排序部分,后面是未排序部分。
一开始,已排序部分只有第一个元素。
从第二个元素开始,依次取出当前元素,当作“待插入的值”,将待插入的值用中间量key保存。
在已排序部分里,从后往前找合适的位置。
如果已排序部分里的元素比待插入的值大,就把它往后挪一位。
找到合适位置后,把待插入的值放进去。
重复上面的过程,直到所有元素都插入完成。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <stdio.h> void printArray (int *arr, int n) { for (int i = 0 ; i < n; i++) { printf ("%d " , *(arr + i)); } printf ("\n" ); } void insertionSort (int *arr, int n) { for (int i = 1 ; i < n; i++) { int key = *(arr + i); int j = i - 1 ; while (j >= 0 && *(arr + j) > key) { *(arr + j + 1 ) = *(arr + j); j--; } *(arr + j + 1 ) = key; } } int main () { int arr[] = {5 , 3 , 8 , 4 , 2 , 7 , 1 , 6 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("排序前:" ); printArray(arr, n); insertionSort(arr, n); printf ("排序后:" ); printArray(arr, n); return 0 ; }
运行结果 1 2 排序前:5 3 8 4 2 7 1 6 排序后:1 2 3 4 5 6 7 8
冒泡排序 思路
从第一个元素开始,比较相邻的两个元素。
如果前一个比后一个大,就交换它们。
继续比较下一对,直到这一轮结束。这一轮结束后,最大的数会跑到最后。
重复上面的过程,每轮都会把当前未排序部分最大的数放到后面。
一共进行 n - 1 轮,数组就有序了。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <stdio.h> void printArray (int *arr, int n) { for (int i = 0 ; i < n; i++) { printf ("%d " , *(arr + i)); } printf ("\n" ); } void bubbleSort (int *arr, int n) { int i, j; int temp; for (i = 0 ; i < n - 1 ; i++) { for (j = 0 ; j < n - 1 - i; j++) { if (*(arr + j) > *(arr + j + 1 )) { temp = *(arr + j); *(arr + j) = *(arr + j + 1 ); *(arr + j + 1 ) = temp; } } } } int main () { int arr[] = {5 , 3 , 8 , 4 , 2 , 7 , 1 , 6 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("排序前:" ); printArray(arr, n); bubbleSort(arr, n); printf ("排序后:" ); printArray(arr, n); return 0 ; }
运行结果 1 2 排序前:5 3 8 4 2 7 1 6 排序后:1 2 3 4 5 6 7 8
快速排序 思路
选枢轴 在当前要排序的区间里,随便选一个数当枢轴。 这里选最左边的数:pivot = *left; 选完后,left 这个位置就相当于一个“坑”。
确定 left 和 right left 指向当前区间的第一个元素。right 指向当前区间的最后一个元素。
用两个指针从两边往中间走 i 从左边开始往右走。j 从右边开始往左走。
循环填坑 从右往左找,找到第一个小于枢轴的数,把它填到左边的坑里,然后 i 往右移。 从左往右找,找到第一个大于枢轴的数,把它填到右边的坑里,然后 j 往左移。
枢轴归位 把一开始保存的 pivot 填到 i 和 j 相遇的位置。 此时,枢轴左边都比它小,右边都比它大。
递归左右两边 对枢轴左边的区间再做一次快速排序。 对枢轴右边的区间再做一次快速排序。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <stdio.h> void printArray (int *arr, int n) { for (int i = 0 ; i < n; i++) printf ("%d " , arr[i]); printf ("\n" ); } void quickSort (int *left, int *right) { if (left >= right) return ; int pivot = *left; int *i = left; int *j = right; while (i < j) { while (i < j && *j >= pivot) j--; if (i < j) { *i = *j; i++; } while (i < j && *i <= pivot) i++; if (i < j) { *j = *i; j--; } } *i = pivot; quickSort(left, i - 1 ); quickSort(i + 1 , right); } int main () { int arr[] = {5 , 3 , 8 , 4 , 2 , 7 , 1 , 6 }; int n = sizeof (arr) / sizeof (arr[0 ]); printf ("排序前:" ); printArray(arr, n); quickSort(arr, arr + n - 1 ); printf ("排序后:" ); printArray(arr, n); return 0 ; }
运行结果 1 2 排序前:5 3 8 4 2 7 1 6 排序后:1 2 3 4 5 6 7 8
堆排序 思路 堆(Heap)是满足以下两个条件的完全二叉树:
结构条件 :是一棵完全二叉树;
堆序条件 :
大顶堆:父节点 ≥ 左右孩子(堆顶是最大值)
小顶堆:父节点 ≤ 左右孩子(堆顶是最小值) 升序排序用大顶堆,降序排序用小顶堆。 堆是逻辑上的二叉树,物理上用数组存储,不需要指针建树。
从下标 1 开始:数组与树的关系
数组开 n + 1 个;
arr[0] 不使用(空着或当哨兵);
有效元素为 arr[1] ~ arr[n]。 设当前节点下标为 i:
关系
公式
父节点
i / 2(整数除法)
左孩子
2 * i
右孩子
2 * i + 1
最后一个非叶子节点
n / 2
第一个叶子节点
n / 2 + 1
堆排序整体思路
建大顶堆 从最后一个非叶子节点 n / 2 开始,倒着到 1,依次对每个节点执行 heapify(向下调整):
1 2 for (int i = n / 2 ; i >= 1 ; i--) heapify(arr, n, i);
为什么从 n / 2 开始? 下标大于 n / 2 的节点全是叶子节点,叶子节点没有孩子,天然满足堆性质,无需调整。 调整完后,整个数组满足大顶堆性质,arr[1] 就是全局最大值。
排序(反复取堆顶) 循环 i 从 n 递减到 2:
交换 arr[1](堆顶,当前最大值)与 arr[i](堆的最后一个元素);
交换后 arr[i] 已经是本轮的最终位置,堆的大小减为 i - 1;
对新的堆顶元素 arr[1] 执行 heapify(arr, i - 1, 1),恢复大顶堆;
重复直到堆中只剩一个元素。
1 2 3 4 5 for (int i = n; i > 1 ; i--){ swap(&arr[1 ], &arr[i]); heapify(arr, i - 1 , 1 ); }
循环结束后,arr[1..n] 就是升序序列。
核心操作:heapify(向下调整) 含义 :假设以 i 为根的左右子树已经是大顶堆,只调整以 i 为根的子树,使其重新成为大顶堆。步骤 :
令 largest = i;
若左孩子 2i <= n 且 arr[2i] > arr[largest],则 largest = 2i;
若右孩子 2i + 1 <= n 且 arr[2i + 1] > arr[largest],则 largest = 2i + 1;
若 largest != i,交换 arr[i] 与 arr[largest],并递归/迭代对 largest 位置继续操作;
若 largest == i,说明 i 已经是三者最大,停止。递归版 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void heapify (int arr[], int n, int i) { int largest = i; int left = 2 * i; int right = 2 * i + 1 ; if (left <= n && arr[left] > arr[largest]) largest = left; if (right <= n && arr[right] > arr[largest]) largest = right; if (largest != i) { swap(&arr[i], &arr[largest]); heapify(arr, n, largest); } }
迭代版 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void heapify (int arr[], int n, int i) { while (1 ) { int largest = i; int left = 2 * i; int right = 2 * i + 1 ; if (left <= n && arr[left] > arr[largest]) largest = left; if (right <= n && arr[right] > arr[largest]) largest = right; if (largest == i) break ; swap(&arr[i], &arr[largest]); i = largest; } }
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <stdio.h> void swap (int *a, int *b) { int temp = *a; *a = *b; *b = temp; } void printArray (int arr[], int n) { for (int i = 1 ; i <= n; i++) printf ("%d " , arr[i]); printf ("\n" ); } void heapify (int arr[], int n, int i) { int largest = i; int left = 2 * i; int right = 2 * i + 1 ; if (left <= n && arr[left] > arr[largest]) largest = left; if (right <= n && arr[right] > arr[largest]) largest = right; if (largest != i) { swap(&arr[i], &arr[largest]); heapify(arr, n, largest); } } void heapSort (int arr[], int n) { for (int i = n / 2 ; i >= 1 ; i--) heapify(arr, n, i); for (int i = n; i > 1 ; i--) { swap(&arr[1 ], &arr[i]); heapify(arr, i - 1 , 1 ); } } int main () { int arr[] = {0 , 12 , 11 , 13 , 5 , 6 , 7 }; int n = sizeof (arr) / sizeof (arr[0 ]) - 1 ; printf ("排序前:" ); printArray(arr, n); heapSort(arr, n); printf ("排序后:" ); printArray(arr, n); return 0 ; }
运行结果 1 2 排序前:12 11 13 5 6 7 排序后:5 6 7 11 12 13