数据结构与算法之十大排序
转载:https://blog.csdn.net/zhangshk_/article/details/82911093
01 术语:
- 稳定:假设排序前a在b前面且a=b,那么排序后a仍然在b前面,即排序过程中不会出现跳跃式交换数据,只能相邻的才交换数据;
- 不稳定:和上面相反;
- 内排序:比较排序,即所有的排序操作均在内存中完成,比如冒泡、选择、插入、希尔、堆、快排、归并;
- 外排序:非比较排序,即数据量太大,需要借助于磁盘来存储数据,比如计数、基数、桶;
- 时间复杂度:耗费的时间级别,比如O(lgN), O(N),O(NlgN),O(N^2)等;
- 空间复杂度:所需内存的大小,比如O(lgN), O(N),O(NlgN),O(N^2)等;
02 十大排序算法总结:
1. 冒泡排序
- 依次比较相邻两个元素,如果前面>后面则交换;
- 第一轮比较完成后,最大的元素排在了最后面;
- 重复步骤1-2,N-1轮遍历后则排序完成;
时间复杂度:O(N)~O(N^2),平均O(N^2);当元素基本有序时复杂度最好即O(N);
1 void bubble_sort(int arr[], int len) 2 { 3 bool is_exchange = false; 4 if(NULL == arr || len <= 1) 5 { 6 return ; 7 } 8 9 for(int i = 0; i < len-1; i++) 10 { 11 is_exchange = false; 12 for(int j = 0; j < len-i-1; j++) 13 { 14 if(arr[j] > arr[j+1]) 15 { 16 swap(&arr[j], &arr[j+1]); 17 is_exchange = true; 18 } 19 } 20 //not exhcange, it indicate that the rest of arr is sorted, so jump and sort has been completed. 21 if(false == is_exchange) 22 { 23 break; 24 } 25 } 26 }
2. 选择排序
- 第i轮遍历时,初始化minIndex=i,遍历N-i次寻找到最小数据的索引为k
- 交换minIndex和k对应的数据后,那么位置i上就是本轮遍历的最小数据;
- 重复1-2,N-1轮遍历后则排序完成;
时间复杂度:O(N^2);
1 void select_sort(int arr[], int len) 2 { 3 int min = 0; 4 if(NULL == arr || len <= 1) 5 { 6 return ; 7 } 8 9 for(int i = 0; i < len-1; i++) 10 { 11 min = i; 12 for(int j = i+1; j < len; j++) 13 { 14 if(arr[j] < arr[min]) 15 { 16 min = j; 17 } 18 } 19 if(min != i) 20 { 21 swap(&arr[min], &arr[i]); 22 } 23 } 24 }
3. 插入排序
- 进行第i轮遍历,此时前面i-1个数据已经排好序;
- 取出元素a[i],依次和前面的元素比较,找到比a[i]大的最小元素,假设下标是j,那么a[j-1] < a[i] < a[j];
- 将元素a[i]插入到a[j-1]和a[j]中间;
- 重复步骤1-3,N-1轮遍历后则排序完成;
时间复杂度:O(N)~O(N^2),平均O(N^2);当元素基本有序时复杂度最好即O(N);
1 void insert_sort(int arr[], int len) 2 { 3 int value = 0, j = 0; 4 if(NULL == arr || len <= 1) 5 { 6 return ; 7 } 8 9 for(int i = 1; i < len; i++) 10 { 11 value = arr[i]; 12 for(j = i-1; j >= 0 && arr[j] > value; j--) 13 { 14 arr[j+1] = arr[j]; 15 } 16 arr[j+1] = value; 17 } 18 }
4.希尔排序---升级版的插入排序
- 选择一个递减的增量序列,t1,t2,……tk,且tk=1;
- 假设增量为gap,即把排序元素分为gap组,同事对gap组进行插入排序
- 重复步骤1-2,gap逐级递减,当gap=1时完全等同于插入排序,则排序完成
时间复杂度:O(NlogNloN)???
1 void shell_sort(int arr[], int len) 2 { 3 int gap = len, value = 0, j = 0; 4 if(NULL == arr || len <= 1) 5 { 6 return ; 7 } 8 9 while(gap > 1) 10 { 11 gap = gap/3 + 1; 12 for(int i = gap; i < len; i++) 13 { 14 value = arr[i]; 15 for(j = i-gap; j >= 0 && arr[j] > value; j -= gap) 16 { 17 arr[j+gap] = arr[j]; 18 } 19 arr[j+gap] = value; 20 } 21 } 22 }
5.堆排序---升级版的选择排序
堆是一种完全二叉树的数据结构,任意一节点的value都大于(小于)它的孩子节点的value;
- 构建最大堆;
- 将堆顶元素和最后一个元素交换,那么最后一个元素为最大值;
- 交换堆顶后可能会违反堆的性质,需要从堆顶开始递归和孩子节点比较,重新构建最大堆
- 重复步骤1-3,则排序完成
时间复杂度:O(NlgN);
1 void adjust_max_heap(int arr[], int len, int cur) 2 { 3 int child = 0; 4 if(NULL == arr || len <= 1 || cur >= len) 5 { 6 return ; 7 } 8 9 while(2*cur + 1 < len) //exist left child 10 { 11 child = 2*cur + 1;//left node 12 if(child+1 < len && arr[child] < arr[child+1]) //exist right child and left child < right child 13 { 14 child++; 15 } 16 17 if(arr[cur] < arr[child]) 18 { 19 swap(&arr[cur], &arr[child]); 20 cur = child; 21 } 22 else 23 { 24 break; 25 } 26 } 27 } 28 29 void heap_sort(int arr[], int len) 30 { 31 if(NULL == arr || len <= 1) 32 { 33 return ; 34 } 35 36 //1.build max_heap 37 for(int i = len/2-1; i >= 0; i--) 38 { 39 adjust_max_heap(arr, len, i); 40 } 41 42 //2.exchange front and back, adjust the reset of heap to max_heap 43 for(int j = len-1; j >= 1; j--) 44 { 45 swap(&arr[j], &arr[0]); 46 adjust_max_heap(arr, j, 0); 47 } 48 }
6. 快速排序---升级版的冒泡
- partition即从初始序列中挑选一个基准pivot,使得位于pivot之前的元素都小于pivot,位于pivot之后的元素都大于pivot;
- 递归操作pivot的前部分;
- 递归操作pivot的后部分;
时间复杂度:O(NlgN)~O(N^2),平均O(NlgN);当元素有序后倒序时复杂度最差即O(N^2);
1 int partition(int arr[], int low, int high) 2 { 3 int pivot = low, val = 0; 4 if(NULL == arr || low >= high) 5 { 6 return low; 7 } 8 9 val = arr[low]; 10 while (low < high) //low<high, not include "=" 11 { 12 while(low < high && arr[high] >= val) //low<high, not include "=" 13 { 14 high--; 15 } 16 //swap(&arr[low], &arr[high]); 17 18 while(low < high && arr[low] <= val) //low<high, not include "=" 19 { 20 low++; 21 } 22 swap(&arr[low], &arr[high]); 23 } 24 //this moment, low equal high, maybe not equal index !!!!!! 25 swap(&arr[low], &arr[pivot]); 26 return low; 27 28 } 29 void _qsort(int arr[], int low, int high) 30 { 31 int pivot = 0; 32 if(low >= high) 33 { 34 return ; 35 } 36 37 pivot = partition(arr, low, high); 38 _qsort(arr, 0, pivot -1); 39 _qsort(arr, pivot + 1, high); 40 } 41 42 void quick_sort(int arr[], int len) 43 { 44 if(NULL == arr || len <= 1) 45 { 46 return ; 47 } 48 49 _qsort(arr, 0, len-1); 50 51 }
7. 归并排序
- 将长度为n的初始序列分为两个长度为n/2的子序列;
- 递归对两个子序列排序;
- 将两个排好序的子序列合并为一个最终的排序序列;
时间复杂度:O(NlgN);
1 void merge(int arr[], int low, int mid, int high) 2 { 3 int *p_tmp_array = NULL, len = 0; 4 if(NULL == arr) 5 { 6 return ; 7 } 8 len = high - low + 1; 9 p_tmp_array = (int*)calloc(1, len * sizeof(int)); 10 for(int i = 0; i < len; i++) 11 { 12 p_tmp_array[i] = arr[low + i]; 13 } 14 15 for(int i = low, j = mid + 1, k = 0; k < len; ) 16 { 17 if(i > mid ) 18 { 19 arr[low + k] = p_tmp_array[j - low]; 20 j++; 21 k++; 22 } 23 else if (j > high) 24 { 25 arr[low + k] = p_tmp_array[i - low]; 26 i++; 27 k++; 28 } 29 else if(p_tmp_array[i - low] > p_tmp_array[j - low]) 30 { 31 arr[low + k] = p_tmp_array[j - low]; 32 j++; 33 k++; 34 } 35 else 36 { 37 arr[low + k] = p_tmp_array[i - low]; 38 i++; 39 k++; 40 } 41 } 42 43 } 44 void _msort(int arr[], int low, int high) 45 { 46 int mid = (low+high)/2; 47 if(low >= high) 48 { 49 return ; 50 } 51 52 _msort(arr, low, mid); 53 _msort(arr, mid+1, high); 54 merge(arr, low, mid, high); 55 } 56 57 void merge_sort(int arr[], int len) 58 { 59 int mid = 0; 60 if(NULL == arr || len <= 1) 61 { 62 return ; 63 } 64 65 _msort(arr, 0, len-1); 66 }
8. 计数排序
- 找到初始序列中的最大元素max和最小元素min;
- 分配一个大小为max-min=k的数组,数组的下标表示待排序的值,数组的值表示该下标出现的次数;
- 遍历初始序列,对数据进行填充,比如a[i]=count,表示元素i出现了count次;
- 遍历数组,进行反向填充则排序完成;
时间复杂度:O(N+k)
适用情况:k << N,比如对一个公司所有员工的年龄排序;
9. 基数排序
- 找到初始序列中的最大数,并得到位数d;
- 从最低位开始对序列进行计数排序;
- 一直到最高位排好序后,则排序完成;
时间复杂度:O(N×d);
适用情况:电话号码、年月日等;
10. 桶排序
- 设置桶大小为BucketSize,即每个桶可以放多少个不同的数值;
- 遍历初始序列,将每个数据放入对应的桶里面,并同时对每个桶排序,比如插入排序;
- 把所有桶拼接起来就是最终的排序序列,即排序完成;
时间复杂度:???
适用情况:序列基本有序;