排序算法总结
来源:互联网 发布:单片机系统时钟频率 编辑:程序博客网 时间:2024/06/04 19:00
一、插入排序
- 直接插入排序
- 每次选择一个元素
K 插入到之前已排好序的部分A[1…i] 中,插入过程中K依次由后向前与A[1…i] 中的元素进行比较。若发现发现A[x]≥K ,则将K插入到A[x]的后面,插入前需要移动元素。 - 时间复杂度:
- 最好的情况下:正序有序(从小到大),这样只需要比较n次,不需要移动。因此时间复杂度为
O(n) - 最坏的情况下:逆序有序,这样每一个元素就需要比较n次,共有n个元素,因此实际复杂度为
O(n2)
- 最好的情况下:正序有序(从小到大),这样只需要比较n次,不需要移动。因此时间复杂度为
- 稳定性:插入排序是稳定的。(K1是已排序部分中的元素,当K2和K1比较时,直接插到K1的后面(没有必要插到K1的前面,这样做还需要移动)
- 每次选择一个元素
void Dir_Insert(int A[],int N){ int j,t; for(int i=1;i<N;i++){ t=A[i]; j=i-1; while(A[j]>t){ A[j+1]=A[j]; j--; } A[j+1]=t; }}
- 希尔排序
- 分组插入方法。先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量
dt=1 。 - 时间复杂度:
- 最好情况:未知。
- 最坏情况:O(N*logN),最坏的情况下和平均情况下差不多。
- 稳定性:不稳定。(不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱)
- 分组插入方法。先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量
void Shell(int A[],int n) { int i,j,k,t; (n/2)%2 == 0 ? k = n/2+1 : k = n/2; //保证增量为奇数 while(k > 0){ for(j=k;j<n; j++){ t = A[j]; i = j - k; while(i>=0 && A[i]>t){ A[i+k]=A[i]; i=i-k; } A[i+k]=t; } if(k == 1) break; (k/2)%2 ==0 ? k=k/2+1 : k=k/2; }}
二、交换排序
- 冒泡排序:临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束
void BubbleSort_1(int a[], int size){ for (int i = 0; i < size -1; i++) { for (int j = size - 1; j > i ; j--) { if (a[j-1] > a[j]) { int temp = a[j-1]; a[j-1] = a[j]; a[j] = temp; } } }}
- 优化1: 如果上面代码中,里面一层循环在某次扫描中没有执行交换,则说明此时数组已经全部有序列,无需再扫描了。因此,增加一个标记,每次发生交换,就标记,如果某次循环完没有标记,则说明已经完成排序。
void BubbleSort_2(int a[], int size){ bool bSwaped = true; for (int i = 0; i < size -1; i++) { // 每次先重置为false bSwaped = false; for (int j = size - 1; j > i ; j--) { if (a[j-1] > a[j]) { int temp = a[j-1]; a[j-1] = a[j]; a[j] = temp; bSwaped = true; } } // 如果上一次扫描没有发生交换,则说明数组已经全部有序,退出循环 if (!bSwaped) break; }}
- 优化2:如果R[0..i]已是有序区间,上次的扫描区间是R[i..n],记上次扫描时最后 一次执行交换的位置为lastSwapPos,则lastSwapPos在i与n之间,不难发现R[i..lastSwapPos]区间也是有序的,否则这个区间也会发生交换;所以下次扫描区间就可以由R[i..n] 缩减到[lastSwapPos..n]
void BubbleSort_3(int a[], int size){ int lastSwapPos = 0,lastSwapPosTemp = 0; for (int i = 0; i < size - 1; i++) { lastSwapPos = lastSwapPosTemp; for (int j = size - 1; j >lastSwapPos; j--) { if (a[j - 1] > a[j]) { int temp = a[j - 1]; a[j - 1] = a[j]; a[j] = temp; lastSwapPosTemp = j; } } if (lastSwapPos == lastSwapPosTemp) break; }}
- 快速排序:采用的思想是分治思想。
void quicksort(int left,int right) { int i,j,t,temp; if(left>right) return; temp=a[left]; //temp中存的就是基准数 i=left; j=right; while(i!=j) { //顺序很重要,要先从右边开始找 while(a[j]>=temp && i<j) j--; //再找右边的 while(a[i]<=temp && i<j) i++; //交换两个数在数组中的位置 if(i<j) { t=a[i]; a[i]=a[j]; a[j]=t; } } //最终将基准数归位 a[left]=a[i]; a[i]=temp; quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程 quicksort(i+1,right);//继续处理右边的 ,这里是一个递归的过程 }
三、选择排序
- 直接选择排序:从所有序列中先找到最小的,然后放到第一个位置。之后再看剩余元素中最小的,放到第二个位置……以此类推,就可以完成整个的排序工作了。
for(int i=0; i<v.size(); i++){ int min = v[i]; int temp; int index = i; for(int j=i+1;j<v.size();j++){ if(v[j] < min){ min = v[j]; index = j; } } temp = v[i]; v[i] = min; v[index]= temp; }
- 堆排序
- 堆存储:数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。
- 建堆
- 初始状态:
- 调整:
- 排序
- 整体步骤
- 首先从第一个非叶子节点开始,比较当前节点和其孩子节点,将最大的元素放在当前节点,交换当前节点和最大节点元素。
- 将当前元素前面所有的元素都进行1的过程,这样就生成了最大堆
- 将堆顶元素和最后一个元素交换,列表长度减1。由此无序区减1,有序区加1
- 剩余元素重新调整建堆
- 继续3和4,直到所有元素都完成排序
int adjust_heap(vector<int> &v, int length, int i){ int left = 2 * i; int right = 2 * i + 1; int largest = i; int temp; while(left < length || right < length){ if (left < length && v[largest] < v[left]){ largest = left; } if (right < length && v[largest] < v[right]){ largest = right; } if (i != largest){ temp = v[largest]; v[largest] = v[i]; v[i] = temp; i = largest; left = 2 * i; right = 2 * i + 1; } else{ break; } }}int build_heap(vector<int> &v, int length){ int i; int begin = length/2 - 1; //get the last parent node for (i = begin; i>=0; i--){ adjust_heap(v,length,i); }}int heap_sort(vector<int> &v){ int length = v.size(); int temp; printline("before sort:",v); build_heap(v,length); while(length > 1){ temp = v[length-1]; v[length-1] = v[0]; v[0] = temp; length--; adjust_heap(v,length,0); } printline("after sort:",v);}
- 时间复杂度:平均时间复杂度为O(nlogn),接近于最坏的时间复杂度。在最好情况下,时间复杂度为O(1)。
四、归并排序
- 分治思想
//将有二个有序数列a[first...mid]和a[mid...last]合并。void mergearray(int a[], int first, int mid, int last, int temp[]){ int i = first, j = mid + 1; int m = mid, n = last; int k = 0; while (i <= m && j <= n) { if (a[i] <= a[j]) temp[k++] = a[i++]; else temp[k++] = a[j++]; } while (i <= m) temp[k++] = a[i++]; while (j <= n) temp[k++] = a[j++]; for (i = 0; i < k; i++) a[first + i] = temp[i];}void mergesort(int a[], int first, int last, int temp[]){ if (first < last) { int mid = (first + last) / 2; mergesort(a, first, mid, temp); //左边有序 mergesort(a, mid + 1, last, temp); //右边有序 mergearray(a, first, mid, last, temp); //再将二个有序数列合并 }}bool MergeSort(int a[], int n){ int *p = new int[n]; if (p == NULL) return false; mergesort(a, 0, n - 1, p); delete[] p; return true;}
- 时间复杂度分析和改进方法:参考 浅谈算法和数据结构: 三 合并排序
五、计数排序
- 基本思想:计数排序假设n个输入元素中的每一个都是介于0-k的整数,此处k为某个整数。计数排序顾名思义离不开计数,我们要计的是输入元素中相同元素出现的次数。对每一个输入元素x,确定小于x的元素的个数,那样排序之后,x在最终输出数组中的位置就可以确定了。
- 步骤:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
#include <stdio.h>#include <stdlib.h>#include <time.h>/* run this program using the console pauser or add your own getch, system("pause") or input loop */void print_arry(int *arr,int n){ int i; for(i = 0; i<n; i++) { printf("%d ", arr[i]); } printf("\n");}void count_sort(int *arr, int *sorted_arr, int n){ int *count_arr = (int *)malloc(sizeof(int) * 100); int i; //初始化计数数组 for(i = 0; i<100; i++) count_arr[i] = 0; //统计i的次数 for(i = 0;i<n;i++) count_arr[arr[i]]++; //对所有的计数累加 for(i = 1; i<100; i++) count_arr[i] += count_arr[i-1]; //逆向遍历源数组(保证稳定性),根据计数数组中对应的值填充到先的数组中 for(i = n; i>0; i--) { sorted_arr[count_arr[arr[i-1]]-1] = arr[i-1]; count_arr[arr[i-1]]--; } free(count_arr);}int main() { int n,i; printf ("待排序数组的大小 n="); scanf ("%d", &n); int *arr = (int *)malloc(sizeof(int) * n); int *sorted_arr = (int *)malloc(sizeof(int) * n); srand (time (0)); for (i = 0; i<n; i++) { arr[i] = rand() % 100; } printf ("随机生成数值为0~99的数组...\n"); printf ("初始化数组: "); print_arry(arr, n); count_sort(arr, sorted_arr, n); printf ("排序后的数组:"); print_arry(sorted_arr, n); return 0; system ("pause");}
- 特点:
- 提前必须是已知待排序的关键字为整型且范围已知。
- 时间复杂度为O(n+k),不是基于比较的排序算法,因此效率非常之高。
- 稳定性好,这个是计数排序非常重要的特性,可以用在后面介绍的基数排序中。
- 但需要一些辅助数组,如C[0..k],因此待排序的关键字范围0~k不宜过大。而B[1..n]用来存放排序结果,我们可以对上述算法进行改进,使排序在原地进行。改进之后如下:
memset(C,sizeof(C),0); //C数组置零 for i=1 to n do C[A[i]]++; //统计输入数组中相同元素的个数 idx = 0; for i=0 to k do while(C[i]>0) do //C[i]中保存的是值为i元素的个数 A[idx++] = i; //因此很容易找到i在A中适合的位置 C[i]--;
六、桶排序
- 参考: 经典排序算法 - 桶排序Bucket sort
0 0
- 算法--排序算法总结
- 算法:排序算法总结
- 算法:排序算法总结
- 算法-排序算法总结
- 算法-排序算法总结
- 【排序算法】排序算法总结
- 排序算法总结---希尔排序
- 排序算法总结---冒泡排序
- 排序算法总结----快速排序
- 排序算法总结---希尔排序
- 排序算法总结【内排序】
- 排序算法之内排序总结
- 排序算法总结:冒泡排序
- 【排序算法总结】冒泡排序
- 【排序算法总结】选择排序
- 排序算法总结
- 排序算法大总结
- 排序算法总结
- linux常用基本命令
- localStorage兼容方案实现
- iOS9 HTTP 不能正常使用的解决办法
- [MTK]WiFi框架
- Codeforces Round #320 (Div. 2) [Bayan Thanks-Round] D. "Or" Game 贪心
- 排序算法总结
- localstorage兼容ie8以下浏览器的问题
- Android 4.0 Camera架构分析之Camera初始化
- cuda编程 总结
- 为什么要在html和body加上“height:100%;”
- BZOJ 1135 [POI2009]Lyz 线段树
- 含有重复元素的二分查找算法
- 完美解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
- 经典电动力学基础