排序算法(sorting)

学习到的排序算法的总结,包括对COMP20003中排序部分进行总结,部分图片来自COMP20003

有部分内容来自http://www.cnblogs.com/eniac12/p/5329396.html 

演示动画:https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html


 

概念:

stable sort: 相同的值排序后相对顺序不变。

Comparison-based sorting is Ω(nlogn).


Hash function ---

用数对list中的元素求余,根据余值放入对应的桶(bucket)中,使用素数(prime)能使得分配更为均匀。

Collisions冲突:两个元素的余值相同时发生。当buckets数量<元素数量,则一定发生。1个好的hash function应尽量少出现collison,但不能认为collision不会发生,换言之要做好应对。

Collision Resolution Methods

  1.Chaining

 

最坏情况:所有值都在同一bucket,实际上为linked list。

2.Open addressing methods

    • Linear probing

当插入位置已有数据时,插入下一空余位置(循环),若全满了,则collision

    • Double hashing

当插入位置已有数据时,进行第二次求余得出新的位置。注意第二次求余值要+1避免在仍在原地。

 

 

 


 

Distribution counting --- unusual approach to sorting

  计数排序(Counting sort):

requires: Key values to be within a certain range, lower to upper. 要排序的值在一定范围内。

通过记录所有数中,比该数小的有几个,来安排其位置。可以用辅助数组(auxiliary array)记录范围内比该值小(大)的有几个,也可以用for循环。用于整数排序

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 int *countSort(int *n, int size);
  5 int *countSortIterative(int *n, int size);
  6 void printArray(int *n, int size);
  7 int min(int *n, int size);
  8 int max(int *n, int size);
  9 
 10 int main(int argc, char **argv) {
 11     int size = 16;
 12     int unsortedArray[16] = {4,4,2,2,0,2,1,3,2,4,3,1,4,3,1,4};
 13     int *sortedArray = NULL;
 14     printArray(unsortedArray, size);
 15     //sortedArray = countSort(unsortedArray, size);
 16     sortedArray = countSortIterative(unsortedArray, size);
 17     printArray(sortedArray, size);
 18     free(sortedArray);
 19     return 0;
 20 } 
 21 
 22 void printArray(int *n, int size) {
 23     int i = 0;
 24     for (i = 0; i < size; i++) {
 25         printf("%d  ", n[i]);
 26     }
 27     printf("\n");
 28 }
 29 
 30 int *countSortIterative(int *n, int size) {
 31     int i = 0, j = 0;
 32     int *sortedArray = NULL;
 33     int count = 0;
 34     
 35     if((sortedArray = (int *) calloc(size, sizeof(int))) == NULL) {
 36         printf("calloc error\n");
 37         exit(EXIT_FAILURE);
 38     }
 39     
 40     for (i = 0; i < size; i++) {
 41         for (j = 0, count = 0; j < size; j++) {
 42             if (i == j) {
 43                 continue;
 44             }
 45             if (n[i] > n[j]) {
 46                 count++;
 47             }
 48             if (i > j && n[i] == n[j]) {
 49                 count++;
 50             }
 51         }
 52         sortedArray[count] = n[i];
 53     }
 54     return sortedArray;
 55 }
 56 
 57 int *countSort(int *n, int size) {
 58     int rangeFrom, rangeTo;
 59     int *cumulativeRecord = NULL;
 60     int *sortedArray = NULL;
 61     int i = 0;
 62     
 63     rangeFrom = min(n, size);
 64     rangeTo = max(n, size);
 65     printf("size is %d, min is %d, max is %d\n", size, rangeFrom, rangeTo);
 66     
 67     if((cumulativeRecord = (int *) calloc(rangeTo - rangeFrom + 1, sizeof(int))) == NULL) {
 68         printf("calloc error\n");
 69         exit(EXIT_FAILURE);
 70     }
 71     for (i = 0; i < size; i++) {
 72         cumulativeRecord[n[i] - rangeFrom]++;
 73     }
 74     for(i = 0; i < rangeTo - rangeFrom + 1; i++) {
 75         if (i == 0) {
 76             continue;
 77         }
 78         cumulativeRecord[i] = cumulativeRecord[i] + cumulativeRecord[i - 1];
 79     }
 80     //printArray(cumulativeRecord, rangeTo - rangeFrom + 1);
 81     if((sortedArray = (int *)malloc(size * sizeof(int))) == NULL) {
 82         printf("malloc error\n");
 83         exit(EXIT_FAILURE);
 84     } 
 85 
 86     for ( i = 0; i < size; i++) {
 87         sortedArray[cumulativeRecord[n[i] - rangeFrom]-1] = n[i];
 88         cumulativeRecord[n[i] - rangeFrom] --;
 89     }
 90     //printArray(sortedArray, size);
 91     free(cumulativeRecord);
 92     return sortedArray;
 93 }
 94 
 95 int min(int *n, int size) {
 96     int i = 0;
 97     int min = n[0];    
 98     for (i = 1; i < size; i++) {
 99         if (min > n[i]) {
100             min = n[i];
101         }
102     }
103     return min;
104 }
105 int max(int *n, int size) {
106     int i = 0;
107     int max = n[0];    
108     for (i = 1; i < size; i++) {
109         if (max < n[i]) {
110             max = n[i];
111         }
112     }
113     return max;
114 }
View Code

复杂度:O(n)   (non-comparison-based)


冒泡排序Bubble sort

使用双循环逐个对相邻元素进行比较,将较大(小)的元素放在后面,经过一次循环,最大(小)的元素被排到末尾。该方法就如名字一般,将较大(小)的浮上来。

worst case: O(n^2)
best case: O(n) 如果设一flag变量检测有无进行元素交换,则在已按顺序排好的list中,第一次循环时,没有进行交换而停止。
average case: O(n^2)
stable sort

 

 1 /* sort the array from min to max 
 2     worst case: O(n^2)
 3     best case: O(n) if set a flag to detect whether swap the elements
 4     average case: O(n^2)
 5     stable sort
 6 */
 7 
 8 #include <stdio.h>
 9 
10 void swap(int *array, int a, int b) {
11     int temp = array[a];
12     array[a] = array[b];
13     array[b] = temp;
14 }
15 
16 void bubbleSort(int *array, int size) {
17     int i, range, swapFlag = 0;
18 
19     for (range = size - 1; range > 0; range--) {
20         for (i = 0; i < range; i++) {
21             if (array[i] > array[i + 1]) {
22                 swap(array, i, i + 1);
23                 swapFlag = 1;
24             }
25         }
26         if (swapFlag == 0) {
27             break;
28         }
29     }
30 }
31 
32 int main(int argc, char **argv) {
33     int array[] = {4, -2, 2, 9, 10, 3};
34     int i;
35     bubbleSort(array, 6);
36     for (i = 0; i < 6; i++) {
37         printf("%d  ", array[i]);
38     }
39     printf("\n");
40     return 0;
41 }
View Code

 


 

 

选择排序Selection sort

选择排序是一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

  注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

worst case: O(n^2)
best case: O(n^2)
average case: O(n^2)
unstable sort:

  选择排序是不稳定的排序算法,不稳定发生在最小元素与A[i]交换的时刻。

  比如序列:{ 5, 8, 5, 2, 9 },一次选择的最小元素是2,然后把2和第一个5进行交换,从而改变了两个元素5的相对次序。

 1 #include <stdio.h>
 2 
 3 /* sort the array from min to max 
 4     worst case: O(n^2)
 5     best case: O(n^2)
 6     average case: O(n^2)
 7     unstable sort
 8 */
 9 void selectionSort(int *array, int size);
10 void swap(int *array, int a, int b);
11 void printArray(int *n, int size);
12 
13 int main(int argc, char **argv) {
14     int array[10] = {10,9,7,8,6,5,4,3,2,1};
15     printArray(array, 10);
16     selectionSort(array, 10);
17     printArray(array, 10);
18     return 0;
19 }
20 
21 /* sort the array from min to max */
22 void selectionSort(int *array, int size) {
23     int i, j, min;
24     
25     for (i = 0; i < size - 1; i++) {
26         min = i;
27         for (j = i + 1; j < size; j++) {
28             if (array[min] > array[j]) {
29                 min = j;
30             }
31         }
32         if (i != min) {
33             swap(array, i, min);
34         }
35     }
36 }
37 
38 void swap(int *array, int a, int b) {
39     int temp = array[a];
40     array[a] = array[b];
41     array[b] = temp;
42 }
43 
44 void printArray(int *n, int size) {
45     int i = 0;
46     for (i = 0; i < size; i++) {
47         printf("%d  ", n[i]);
48     }
49     printf("\n");
50 }
View Code

 

 插入排序Insertion sort

  类似于打扑克时抽牌时的操作,抽到第n张时,与前一张比较大小,直到前一张的牌比第n张小,则插入此位置。

worst case O(n^2)
average case O(n^2)
best case O(n): 当list已经拍好顺序时。
stable sort

 1 #include <stdio.h>
 2 /* sort the array from min to max
 3     worst case O(n^2)
 4     average case O(n^2)
 5     best case O(n)
 6     stable sort
 7 */
 8 void insertionSort(int *array, int size);
 9 void printArray(int *n, int size);
10 
11 int main(int argc, char **argv) {
12     int array[10] = {-9, 10, 2, 3, -4, 4, 8, 12, 15, 7};
13     printArray(array, 10);
14     insertionSort(array, 10);
15     printArray(array, 10);
16     return 0;
17 }
18 
19 void insertionSort(int *array, int size) {
20     int i = 1, j, insertionVal;
21     for (i = 1; i < size; i++) {
22         insertionVal = array[i];
23         j = i;
24         while( j - 1 >= 0 && array[j - 1] > insertionVal) {
25             array[j] = array[j - 1];
26             j--;
27         }
28         array[j] = insertionVal;
29     }
30 }
31 
32 void printArray(int *n, int size) {
33     int i = 0;
34     for (i = 0; i < size; i++) {
35         printf("%d  ", n[i]);
36     }
37     printf("\n");
38 }
View Code

 

分治策略(divide-and-conquer sorting algorithm)

快速排序(Quicksort)---Hard split, easy join:

  Partition array:

Pick Pivot, which it is in its final position   

Everything larger than pivot has higher index 

Everything less than pivot has lower index

选择一个pivot枢纽(?不知道中文该对应哪个),可以是最左边或最右边等由自己决定,根据pivot的选择不同,会有不同效果。比pivot大的排在pivot右边,小的排在左边,只是如此,在左边的和右边的序列并未按序。

Recursion:

Partition left-half(recursively)

Partition right-half(recursively)

Base case:singletons are already sorted

如此不断递归。

注意:在选择pivot时,最后将pivot放置到正确位置,如选择最左作为pivot,在partition时,最后将从右开始筛选的替换。

 

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void quicksort(int *n, int l, int r);
 5 void printArray(int *n, int i);
 6 int partition(int *n, int l, int r);
 7 int partitionLeft(int *n, int l, int r);
 8 int partitionRight(int *n, int l, int r);
 9 void swap(int *n, int i, int j);
10 
11 int main(int argc, char **argv) {
12     int *n = (int *) malloc(sizeof(int));
13     int i = 0;
14     int data;
15     while (scanf("%d", &data) == 1) {
16         n = realloc(n, sizeof(int) * (i + 1));
17         n[i] = data;
18         i++;
19     }
20     printArray(n, i);
21     quicksort(n, 0, i - 1);
22     
23     printArray(n, i);
24     free(n);
25     return 0;
26 }
27 
28 void printArray(int *n, int i) {
29     int j = 0;
30     for (j = 0; j < i; j++) {
31         printf("%d   ", n[j]);
32     }
33     printf("\n");
34 }
35 
36 void quicksort(int *n, int l, int r) {
37     int i;
38     if (l >= r) {
39         return;
40     }
41     i = partition(n, l, r);
42     quicksort(n, l, i - 1);
43     quicksort(n, i + 1, r);
44 }
45 
46 int partition(int *n, int l, int r) {
47     return partitionLeft(n, l, r);
48     //return partitionRight(n, l, r);
49 }
50 
51 int partitionLeft(int *n, int l, int r) {
52     int pivot = n[l];
53     int left = l;
54     int right = r + 1;
55     while(1) {
56         do{
57             left++;
58         } while(n[left] < pivot);
59         do{
60             right--;
61         } while(n[right] > pivot);
62         
63         if (left >= right) {
64             break;
65         }
66         swap(n, left, right);
67     }
68     swap(n, l, right);
69     return right;
70 }
71 
72 int partitionRight(int *n, int l, int r) {
73     int pivot = n[r];
74     int left = l - 1;
75     int right = r;
76     while(1) {
77         do{
78             left++;
79         } while(n[left] < pivot);
80         do{
81             right--;
82         } while(n[right] > pivot);
83         
84         if (left >= right) {
85             break;
86         }
87         swap(n, left, right);
88     }
89     swap(n, r, left);
90     return left;
91 }
92 
93 void swap(int *n, int i, int j) {
94     int temp = n[i];
95     n[i] = n[j];
96     n[j] = temp;
97 }
View Code

 

归并排序(Mergesort)---Easy split, hard join:

关键:当有两个已排好顺序的list(array or linked list)要合并(merge)成一个时,使用两个指针分别指向两个lists,比较其大小,小的放入新list,其对应指针指向下一个,直到两个lists都装入新list。

 1 /* merge two sorted arrays, first -- mid, mid + 1 -- last  */
 2 void merge(int *array, int first, int mid, int last) {
 3     int newArray[last - first + 1];
 4     int i, j, k;
 5     for (i = first, j = mid + 1, k = 0; k < last - first + 1; k++) {
 6         if (i > mid) {
 7             newArray[k] = array[j++];    
 8             continue;
 9         }
10         if (j > last) {
11             newArray[k] = array[i++];    
12             continue;
13         }
14         if (array[i] < array[j]) {
15             newArray[k] = array[i++];
16         } else {
17             newArray[k] = array[j++];
18         }
19     }
20     
21     /* paste newArray to array */ 
22     for (i = first, k = 0; i <= last ; i++, k++) {
23         array[i] = newArray[k];
24     }
25 }
View Code

 

 

分为两种:1.Top-down mergesort(recursive)

 将list不断一分为二,直到分成单个为止,然后开始合并,使用递归。

 1 void mergeSortRecursion(int *array, int first, int last) {
 2     int mid = (first + last) / 2;
 3     
 4     if (first == last) {
 5         return;
 6     }
 7     mergeSortRecursion(array, first, mid);
 8     mergeSortRecursion(array, mid + 1, last);
 9     merge(array, first, mid, last);
10 }
View Code

 

 

     2.Bottom-up mergesort(iterative)

将list看作是由单个元素的lists组成的,两两合并,使用迭代。

 1 void mergeSortIterative(int *array, int len) {
 2     int i, first, mid, last;
 3     for (i = 1; i < len; i *= 2) {
 4         first = 0;
 5         while(first + i < len) {
 6             mid = first + i - 1;
 7             last = mid + i < len ? mid + i : len - 1;
 8             merge(array, first, mid, last);
 9             printf("first = %d, mid = %d, last = %d\n", first, mid, last);
10             first = last + 1;
11         }
12         printArray(array, len);
13     }
14 }
View Code

 使用链表完成的bottom-up mergesort(iterative)放在了我的github:https://github.com/Will-Zhu-27/Algorithms-and-Data-Structures/tree/master/sorting/merge%20sort%20in%20linked%20list

posted @ 2018-12-22 18:45  落星无尘_Will  阅读(1219)  评论(0编辑  收藏  举报