常见经典排序算法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常见经典排序算法相关的知识,希望对你有一定的参考价值。
插入排序: 算法简介:接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。时间复杂度为O(n^2)。 最稳定的排序算法但是效率很低 代码实现: void InsertSort(int *arr,int n) { for (int index = 0; index < n-1; ++index) { int end = index+1; int tmp = arr [end]; while (end>0&&tmp<arr [end - 1]) { arr[end] = arr [end - 1]; end--; } arr[end] = tmp; } } 显然当最小的数字在数组的最右端时,此时需要将整个数组进行移位,效率很低,而希尔排序可以有效的改善这个问题 希尔排序:希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法 先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。 代码实现: void ShellSort(int *arr, int n)//希尔排序 { int gap = n ; while (gap>1)//由于gap=gap/3+1 最小值为1 则在gap=1时跳出循环 { gap = gap / 3 + 1; //{ 2, 8, 9, 6, 1, 3, 4, 5, 7, 0 ,-1,-2}//注意这里的+1 当gap=1时此时排序等同于插入排序 但是由于之前将最小的数据已经移到最左边所以效率 //高于插入排序 for (int index = 0; index <n-gap; ++index) { int end = index; int tmp = arr [end+gap]; while (end>= 0 && arr [end]>tmp) { arr[end+gap] = arr [end]; end -=gap;//此时插入间距为end } arr[end+gap] = tmp; } } } 附注:上面希尔排序的步长选择都是从n/3+1开始,每次再取上一次的三分之一加1,直到最后为1。关于希尔排序步长参见维基百科:http://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F 冒泡排序:20世纪经典算法之一, 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,再进行第二趟冒泡,由此我们可以写出以下代码: void BubbleSort(int *arr, int n) { for (int i = 0; i < n; ++i) { for (int j = 0; j < n - i - 1; ++j) { if (arr [j]>arr[j + 1]) swap( arr[j], arr [j + 1]); } } } 这里我们设立一个标记变量 flag用来标记数列中的数是否在循环结束前就已经排好序 代码改进如下: void BubbleSort(int *arr, int n) { bool flag = true ; int k = n ; while (flag) { flag = false; for (int i = 1; i < k; ++i) { if (arr [i - 1]<arr[i]) { swap( arr[i - 1], arr [i]); flag = true;//有发生交换 继续冒泡 否则说明已经有序 } } k--; } } 如果这一趟发生了交换,这flag为rrue,则还需要继续进行冒泡,否则说明数组已经有序,后面的不必进行下去。 那么这里给出这样一种情况:(2,1,3,4,5,6,7,8,9,10)第一次循环交换之后我们会发现,后面的数组已经有序,不再需要我们进行冒泡,后面的操作都是不必要的 这里我们只需要记录下最后一次进行交换的位置,那么下次遍历只要遍历到这个位置就可以结束。 代码进一步优化如下: void BubbleSort(int *arr, int n) { int flag = n ;//第一次遍历终点为数组尾 while (flag > 0) { int k = flag; flag = 0; for (int j = 1; j < k; ++j) { if (arr [j - 1] > arr[j]) { swap( arr[j - 1], arr [j]); flag = j;//后面的已经排序好 记录下下一次排序的终点 } } } } 虽然有了这么多优化,但是冒泡排序总体还是一种效率很低的排序,数据规模过大时不适合使用这种排序 堆排序: 堆的定义: 1.堆是一颗完全二叉树 2.父结点总是大于或等于(小于或等于)任何一个子节点 3每个结点的左子树和右子树都是一个二叉堆 当父结点总是大于或等于任何一个子节点值时为最大堆。反之则为最小堆 堆的结构示意图如下: 存储方式: 我们使用数组来存储一个堆,可以看出设父节点下标值为i 其左孩子下标值为2*i+1,右孩子为2*1+2; 代码实现如下: void AdJust_down(int *arr, int parent, int size ) { int child = 2 * parent +1; while (child<size ) { if (child+1<size &&arr[child+1]> arr[child]) { ++child; } if (arr [parent]> arr[child]) break; swap( arr[parent ], arr[child]); parent = child; child = 2 * parent; } } void HeapSort(int *arr,int n) { for (int i = n/2-1; i >= 0; --i) { AdJust_down( arr, i, n ); } for (int i = n - 1; i >= 1; --i) { swap( arr[0], arr [i]); AdJust_down( arr, 0, i); } } 思路分析: 1.如果要对数字进行升序,我们首先首先将数组初始化为原始大堆 for (int i = n/2-1; i >= 0; --i) { AdJust_down( arr, i, n );//从最后一个非叶子节点开始调整 } 2.进行排序(以升序为例) 大堆的性质为:最大的数据一定在堆顶将堆顶和堆最后一个元素进行交换,则最大的数字此时在数字尾部,再将堆顶元素下调,且堆的大小减1,知道堆大小为1循环结束,排序完成。 代码如下: for (int i = n - 1; i >= 1; --i) { swap( arr[0], arr [i]); AdJust_down( arr, 0, i); } 选择排序 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法 为了减少比较的次数,我们一次遍历同时选出最大值和最小值,代码实现如下: void SelectSort(int *arr, int n) { int i = 0, j = n - 1; int max = j; int min = i; int left = 0; int right = n - 1; while (left<=right) { min = left; max = right; ///!!!!!!!!!!!重点 for (i = left, j = right; i <= j; i++) { if (arr [min]>arr[i]) min = i; if (arr [max] < arr[i]) ////{ 2, 9, 6, 1, 3, 4, 5, 7, 0 ,-8,1,-2} max = i; } if (left != min) { swap( arr[left], arr [min]); if (max == left) max = min; } if (right != max) swap( arr[right], arr [max]); left++; right--; } } 这里我们必须注意到,以升序为例,如果一次遍历找到的最大值刚好在数组左边,此时肯定会先被移走,此时就最大值得下标就得更新为转移后的位置 快速排序: 该方法的基本思想是: 1.先从数列中取出一个数作为key。 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。 3.再对左右区间重复第二步,直到各区间只有一个数。 代码实现如下: int PartSort(int *arr, int left, int right) { int key = arr [right]; //{ 10,2, 8, 9, 6, 1, 3, 4, 5, 7, 0 ,-1,-2,-100}; int begin = left ; int end = right - 1; while (begin<end) { while (begin<end&&arr [begin]<=key) { begin++; } while (begin<end&&arr [end]>=key) { end--; } if (begin < end) swap( arr[begin], arr [end]); } if (arr [begin]>arr[right]) { swap( arr[begin], arr [right]); return begin; } return right ; } void QuickSort(int *arr, int left,int right) { if (left >= right) return; int div = PartSort(arr , left, right); QuickSort( arr, div + 1, right ); QuickSort( arr, left , div - 1); }
以上是关于常见经典排序算法的主要内容,如果未能解决你的问题,请参考以下文章