十种常见排序算法可以分为两大类:
0.3 相关概念
0.4 测试代码
// ====================测试代码==================== void Test(const char* testName, int* data, int n, int* expectedResult, int k) { if (testName != nullptr) printf("%s begins: ", testName); if (n != k) printf("Error input.\n"); Sort(data, n); bool flag = true; //默认排序正确 for (int i = 0; i < n; ++i) { if (data[i] != expectedResult[i]) //有一个不相等则排序错误 { flag = false; break; } } if (flag) printf("Passed.\n"); else printf("Failed.\n"); } // 正常序列 void Test1() { int data[] = { 4, 5, 1, 6, 2, 7, 3, 8 }; int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; Test("Test1", data, sizeof(data) / sizeof(int), expected, sizeof(expected) / sizeof(int)); } // 数组中有相同的数字 void Test2() { int data[] = { 4, 5, 1, 6, 1, 8, 1, 8 }; int expected[] = { 1, 1, 1, 4, 5, 6, 8, 8 }; Test("Test2", data, sizeof(data) / sizeof(int), expected, sizeof(expected) / sizeof(int)); } // 数组已经排好序 void Test3() { int data[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; Test("Test3", data, sizeof(data) / sizeof(int), expected, sizeof(expected) / sizeof(int)); } // 数组反向排序 void Test4() { int data[] = { 8, 7, 6, 5, 4, 3, 2, 1 }; int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; Test("Test4", data, sizeof(data) / sizeof(int), expected, sizeof(expected) / sizeof(int)); } // 数组所有数字相同 void Test5() { int data[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; int expected[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; Test("Test5", data, sizeof(data) / sizeof(int), expected, sizeof(expected) / sizeof(int)); } // 输入空指针 void Test6() { int* data = nullptr; int* expected = nullptr; Test("Test7", data, 0, expected, 0); } int main(int argc, char* argv[]) { Test1(); Test2(); Test3(); Test4(); Test5(); Test6(); return 0; }
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
1.2 动图演示
void Sort(int* arr, int n) //BubbleSort { for (int i = 0; i < n; ++i) { for (int j = 1; j < n - i; ++j) { //逆序时交换 if (arr[j - 1] > arr[j]) { int temp = arr[j - 1]; arr[j - 1] = arr[j]; arr[j] = temp; } } } }
void Sort(int* arr, int n) { //邓公,数据结构(C++语言版) Page5 bool isSorted = false; //排序完成标志 while (!isSorted) //借助标志位isSorted,可以提前退出 { isSorted = true; //假设排序完成 for (int i = 1; i < n; ++i) //从左到右检查 { //逆序时交换 if (arr[i - 1] > arr[i]) { int temp = arr[i - 1]; arr[i - 1] = arr[i]; arr[i] = temp; isSorted = false; //整体排序不能保证,清除标志位 } } --n; //末位元素已经就位,缩短排序序列长度 } }
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
void Sort(int* arr, int n) //SelectionSort { for (int i = 0; i < n; ++i) { int minIndex = i; //最小值索引 for (int j = i + 1; j < n; ++j) { if (arr[j] < arr[minIndex]) //发现更小值, 更新 minIndex = j; } if (minIndex == i) //如果当前值就是最小值, 则跳过 continue; else //否则交换当前值和最小值 { int temp; temp = arr[minIndex]; arr[minIndex] = arr[i]; arr[i] = temp; } } }
表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
void Sort(int* arr, int n) //InsertSort { for (int i = 1; i < n; ++i) { int preIndex; //如果当前值小于前一位值(已排序),则当前值需要插入 if (arr[i] < arr[i - 1]) { int temp = arr[i]; //暂存当前值 //如排序元素大于当前值, 则将该元素移到下一位置 for (preIndex = i - 1; preIndex >= 0 && temp <= arr[preIndex]; --preIndex) { arr[preIndex + 1] = arr[preIndex]; } arr[preIndex + 1] = temp; //回到插入位置 } } }
插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
补充:算法的基本思想是:先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。
void Sort(int* arr, int n) //ShellSort { //与动画一致, 分组执行 int gap = n; while (gap > 1) { // 确定分组的增量 gap = gap / 3 + 1; for (int i = 0; i < gap; ++i) //从0~gap-1分组进行排序 { //以下为插入排序 for (int j = i + gap; j < n; j += gap) { int preIndex; if (arr[j] < arr[j - gap]) { int temp = arr[j]; for (preIndex = j - gap; preIndex >= 0 && temp <= arr[preIndex]; preIndex -= gap) { arr[preIndex + gap] = arr[preIndex]; } arr[preIndex + gap] = temp; } } } } }
void Sort(int* arr, int n) //ShellSort { //与动画不一致, 多组交替执行 int gap = n; while (gap > 1) { gap = gap / 3 + 1; // 确定分组的增量 for (int i = gap; i < n; ++i) { int preIndex; int temp = arr[i]; for (preIndex = i - gap; preIndex >= 0 && temp <= arr[preIndex]; preIndex -= gap) { arr[preIndex + gap] = arr[preIndex]; } arr[preIndex + gap] = temp; } } }
4.4 算法分析
希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。动态定义间隔序列的算法是《算法(第4版)》的合著者Robert Sedgewick提出的。
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
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
|
function mergeSort(arr) { var len = arr.length; if (len < 2) { return arr; } var middle = Math.floor(len / 2), left = arr.slice(0, middle), right = arr.slice(middle); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right) { var result = []; while (left.length>0 && right.length>0) { if (left[0] <= right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } while (left.length) result.push(left.shift()); while (right.length) result.push(right.shift()); return result; } |
归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(nlogn)的时间复杂度。代价是需要额外的内存空间。
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
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
|
function quickSort(arr, left, right) { var len = arr.length, partitionIndex, left = typeof left != ‘number‘ ? 0 : left, right = typeof right != ‘number‘ ? len - 1 : right; if (left < right) { partitionIndex = partition(arr, left, right); quickSort(arr, left, partitionIndex-1); quickSort(arr, partitionIndex+1, right); } return arr; } function partition(arr, left ,right) { // 分区操作 var pivot = left, // 设定基准值(pivot) index = pivot + 1; for ( var i = index; i <= right; i++) { if (arr[i] < arr[pivot]) { swap(arr, i, index); index++; } } swap(arr, pivot, index - 1); return index-1; } function swap(arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } |
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
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
|
var len; // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量 function buildMaxHeap(arr) { // 建立大顶堆 len = arr.length; for ( var i = Math.floor(len/2); i >= 0; i--) { heapify(arr, i); } } function heapify(arr, i) { // 堆调整 var left = 2 * i + 1, right = 2 * i + 2, largest = i; if (left < len && arr[left] > arr[largest]) { largest = left; } if (right < len && arr[right] > arr[largest]) { largest = right; } if (largest != i) { swap(arr, i, largest); heapify(arr, largest); } } function swap(arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function heapSort(arr) { buildMaxHeap(arr); for ( var i = arr.length - 1; i > 0; i--) { swap(arr, 0, i); len--; heapify(arr, 0); } return arr; } |
原文:https://www.cnblogs.com/ZSY-blog/p/12739975.html