八大排序算法
【IT专家】精通八大排序算法系列:一、快速排序算法
精通八大排序算法系列:一、快速排序算法2011/01/09 583 写此八大排序算法系列之前,先说点题外话。
每写一篇文章,我都会遵循以下几点原则:一、保持版面的尽量清晰,力保排版良好。
二、力争所写的东西,清晰易懂,图文并茂三、尽最大可能确保所写的东西精准,有实用价值。
因为,我觉得,你既然要把你的文章,公布出来,那么你就一定要为你的读者负责。
不然,就不要发表出来。
一切,为读者服务。
ok,闲不多说。
接下来,咱们立刻进入本文章的主题,排序算法。
众所周知,快速排序算法是排序算法中的重头戏。
因此,本系列,本文就从快速排序开始。
------------------------------------------------------ 一、快速排序算法的基本特性时间复杂度:O(n*lgn)最坏:O(n )空间复杂度:O(n*lgn)不稳定。
快速排序是一种排序算法,对包含n个数的输入数组,平均时间为O(nlgn),最坏情况是O(n )。
通常是用于排序的最佳选择。
因为,排序最快,也只能达到O (nlgn)。
二、快速排序算法的描述算法导论,第7章快速排序时基于分治模式处理的,对一个典型子数组A[p...r]排序的分治过程为三个步骤:1.分解:A[p..r]被划分为俩个(可能空)的子数组A[p ..q-1]和A[q+1 ..r],使得A[p ..q-1] = A[q] = A[q+1 ..r]2.解决:通过递归调用快速排序,对子数组A[p ..q-1]和A[q+1 ..r]排序。
3.合并。
三、快速排序算法 版本一:QUICKSORT(A, p, r)1 if p r2 then q ←PARTITION(A, p, r) //关键3 QUICKSORT(A, p, q - 1)4 QUICKSORT(A, q + 1, r) 数组划分快速排序算法的关键是PARTITION过程,它对A[p..r]进行就地重排:PARTITION(A, p, r)1 x ← A[r]2 i ← p - 13 for j ← p to r - 14 do if A[j] ≤ x5 then i ← i + 16 exchange A[i] - A[j]7 exchange A[i + 1] - A[r]8 return i + 1。
十大经典排序算法总结
⼗⼤经典排序算法总结最近⼏天在研究算法,将⼏种排序算法整理了⼀下,便于对这些排序算法进⾏⽐较,若有错误的地⽅,还请⼤家指正0、排序算法说明0.1 排序术语稳定:如果a=b,且a原本排在b前⾯,排序之后a仍排在b的前⾯不稳定:如果a=b,且a原本排在b前⾯,排序之后排在b的后⾯时间复杂度:⼀个算法执⾏所耗费的时间空间复杂度:⼀个算法执⾏完所需内存的⼤⼩内排序:所有排序操作都在内存中完成外排序:由于数据太⼤,因此把数据放在磁盘中,⽽排序通过磁盘和内存的数据传输才能进⾏0.2算法时间复杂度、空间复杂度⽐较0.3名词解释n:数据规模k:桶的个数In-place:占⽤常数内存,不占⽤额外内存Out-place:占⽤额外内存0.4算法分类1.冒泡排序冒泡排序是⼀种简单的排序算法。
它重复地⾛访过要排序的数列,⼀次⽐较两个元素,如果它们的顺序错误就把它们交换过来。
⾛访数列的⼯作是重复地进⾏直到没有再需要交换,也就是说该数列已经排序完成。
这个算法的名字由来是因为越⼩的元素会经由交换慢慢“浮”到数列的顶端1.1算法描述⽐较相邻的元素,如果前⼀个⽐后⼀个打,就交换对每⼀对相邻元素做同样的⼯作,从开始第⼀对到结尾最后⼀对,这样在最后的元素应该会是最⼤的数针对所有的元素重复以上的步骤,除了最后⼀个重复步骤1-3,知道排序完成1.2动图演⽰1.3代码实现public static int[] bubbleSort(int[] array) {if (array.length == 0)return array;for (int i = 0; i < array.length; i++)for (int j = 0; j < array.length - 1 - i; j++)if (array[j + 1] < array[j]) {int temp = array[j + 1];array[j + 1] = array[j];array[j] = temp;}return array;}1.4算法分析最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)2.选择排序表现简单直观的最稳定的排序算法之⼀,因为⽆论什么数据都是O(n2)的时间复杂度,⾸先在未排序序列中找到最⼩(⼤)元素,与数组中第⼀个元素交换位置,作为排序序列的起始位置,然后再从剩余未排序元素中继续寻找最⼩(⼤)的元素,与数组中的下⼀个元素交换位置,也就是放在已排序序列的末尾2.1算法描述1.初始状态:⽆序区为R[1..n],有序区为空2.第i躺排序开始时,当前有序区和⽆序区R[1..i-1]、R[i..n]3.n-1趟结束,数组有序化2.2动图演⽰2.3代码实现public static int[] selectionSort(int[] array) {if (array.length == 0)return array;for (int i = 0; i < array.length; i++) {int minIndex = i;for (int j = i; j < array.length; j++) {if (array[j] < array[minIndex]) //找到最⼩的数minIndex = j; //将最⼩数的索引保存}int temp = array[minIndex];array[minIndex] = array[i];array[i] = temp;}return array;}2.4算法分析最佳情况:T(n) = O(n2) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)3、插⼊排序是⼀种简单直观的排序算法,通过构建有序序列,对于未排序序列,在已排序序列中从后向前扫描,找到相应位置并插⼊,需要反复把已排序元素逐步向后挪位,为最新元素腾出插⼊空间3.1算法描述1.从第⼀个元素开始,该元素可以认为已经被排序2.取出下⼀个元素(h),在已排序的元素序列中从后往前扫描3.如果当前元素⼤于h,将当前元素移到下⼀位置4.重复步骤3,直到找到已排序的元素⼩于等于h的位置5.将h插⼊到该位置6.重复步骤2-53.2动图演⽰3.3代码实现public static int[] insertionSort(int[] array) {if (array.length == 0)return array;int current;for (int i = 0; i < array.length - 1; i++) {current = array[i + 1];int preIndex = i;while (preIndex >= 0 && current < array[preIndex]) {array[preIndex + 1] = array[preIndex];preIndex--;}array[preIndex + 1] = current;}return array;}3.4算法分析最佳情况:T(n) = O(n) 最坏情况:T(n) = O(n2) 平均情况:T(n) = O(n2)4、希尔排序是简单插⼊排序经过改进之后的⼀个更⾼效的版本,也称为缩⼩增量排序,同时该算法是冲破O(n2)的第⼀批算法之⼀。
十大排序算法原理
十大排序算法原理排序算法是计算机科学中最基本的算法之一,它可以将一组无序的数据按照一定的规则进行排列,使得数据更加有序,方便后续的处理。
在计算机科学中,有许多种排序算法,其中比较经典的有十大排序算法,它们分别是冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序和基数排序。
1. 冒泡排序冒泡排序是一种简单的排序算法,它的基本思想是从头到尾依次比较相邻的两个元素,如果前一个元素比后一个元素大,则交换它们的位置。
这样一趟下来,最大的元素就被排到了最后面。
然后再对剩下的元素进行同样的操作,直到所有元素都被排好序为止。
2. 选择排序选择排序是一种简单的排序算法,它的基本思想是从待排序的数据中选择最小的元素,将其放到已排序的数据的末尾。
然后再从剩余的数据中选择最小的元素,放到已排序的数据的末尾。
依次类推,直到所有的数据都被排序完毕。
3. 插入排序插入排序是一种简单的排序算法,它的基本思想是将待排序的数据分为已排序和未排序两部分,每次从未排序的数据中取出一个元素,插入到已排序的数据中的合适位置。
插入的过程中,需要将已排序的数据中比插入元素大的元素向后移动一位,为插入元素腾出位置。
4. 希尔排序希尔排序是一种改进的插入排序算法,它的基本思想是将待排序的数据分成若干个子序列,对每个子序列进行插入排序,然后再将所有子序列合并成一个序列。
希尔排序的特点是可以先对距离较远的元素进行比较和交换,从而减少比较和交换的次数,提高排序的效率。
5. 归并排序归并排序是一种分治算法,它的基本思想是将待排序的数据分成两个子序列,对每个子序列进行排序,然后将两个子序列合并成一个有序序列。
归并排序的特点是稳定、效率高,但需要额外的存储空间。
6. 快速排序快速排序是一种分治算法,它的基本思想是选择一个基准元素,将待排序的数据分成两个子序列,一个子序列中的元素都比基准元素小,另一个子序列中的元素都比基准元素大。
深入浅出-C语言8种经典排序算法
for(i=0;i<n-j;i++) /* 值比较大的元素沉下去后,只把剩下的元素中的最大值再 沉下去就可以啦 */
{ if(a[i]>a[i+1]) /* 把值比较大的元素沉到底 */ { k=a[i]; a[i]=a[i+1]; a[i+1]=k; }
(8)堆排序 我们知道堆的结构是节点 i 的孩子为 2*i 和 2*i+1 节点,大顶堆要求父节点大于等于其 2 个 子节点,小顶堆要求父节点小于等于其 2 个子节点。在一个长为 n 的序列,堆排序的过程是 从第 n/2 开始和其子节点共 3 个值选择最大(大顶堆)或者最小(小顶堆),这 3 个元素之间的选 择当然不会破坏稳定性。但当为 n/2-1, n/2-2, ...1 这些个父节点选择元素时,就会破坏稳定性。 有可能第 n/2 个父节点交换把后面一个元素交换过去了,而第 n/2-1 个父节点把后面一个相 同的元素没有交换,那么这 2 个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳 定的排序算法.
input[j + 1] = input[j]; /* 一边找一边移动元素 */ input[j] = temp; } } }
5.折半插入排序
5
折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次
程思想,值得学习
void sort(int num[], int low, int high) {
int i;
while(low<high) {
for(i=low; i<high; i++) /* bubble to high */ if(num[i]>num[i+1]) swap(num[i], num[i+1]);
算法_八大排序算法总结
算法_⼋⼤排序算法总结最近笔试⾯试中经常考到排序算法,及其对应的时间复杂度和空间复杂度分析,现做如下总结。
⼀,冒泡排序思想:对于0~n-1,依次⽐较相邻两个数,前者⽐后者⼤就交换,⼀轮后A[n-1]是最⼤数,在对0~n-2执⾏以上步骤,则A[n-2]是第⼆⼤的数,循环执⾏上⾯的步骤即可,形象的可以理解为⼤的数⼀个个冒到后⾯去,所以叫冒泡排序。
⽰意图:⾸先6和3⽐较,6⽐3⼤,交换6和5⽐较,交换6和7⽐较,不⽤交换依次执⾏以上步骤,第⼀轮后,序列变为接着,在0到n-2上执⾏以上步骤⼀轮过后,则变为依次执⾏以上步骤,最后序列为时间复杂度: O(n^2)空间复杂度:O(1)代码:1class BubbleSort {2public:3int* bubbleSort(int* A, int n)4 {5int temp;6// write code here7for(int i = 0; i < n; i++)8 {9for(int j = 0; j < n - i - 1; j++)10 {11if(A[j] > A[j+1])12 {13 temp = A[j];14 A[j] = A[j + 1];15 A[j + 1] = temp;16 }17 }1819 }20return A;21 }22 };⼆,选择排序思想:在序列中依次选择最⼩值放到最前端,重复以上步骤,只到排序完成⽰意图:最⼩数为0,放到最前端1到n-1最⼩数为1,放到最前端依次执⾏以上步骤,最后为时间复杂度:O(n^2)空间复杂度:O(1)代码:1class SelectionSort {2public:3int* selectionSort(int* A, int n)4 {5// write code here6//从前往后依次放⼊为排序的数组的最⼩值7int min_b;8int temp;9for(int i = 0; i < n - 1; i++)10 {11 min_b = i;12for(int j = i; j < n; j++) //寻找最⼩值13 {14if(A[min_b] > A[j])15 min_b = j;1617 }18 temp = A[i];19 A[i] = A[min_b];20 A[min_b] = temp;21 }22return A;23 }24 };三,插⼊排序思想:对于数组A[n],保证前⾯的A[0]~A[m]是排序好的,再把A[m+1]插⼊到前⾯排好序的序列中,m递增,知道m=n-2⽰意图:原始序列为:6和5⽐较,6⽐5⼤,要交换接下来把3插⼊到前⾯排好序的序列中,⾸先3和6⽐,6⼤,后移⼀位接着3和5⽐较,5⼤,后移⼀位只到前⾯没有数了,或者前⾯的数⽐要插⼊的数⼩,就在对应的位置插⼊该数再对1执⾏以上步骤重复以上步骤,只到整个序列排序完成时间复杂度:O(n^2)空间复杂度:O(1)代码1class InsertionSort {2public:3int* insertionSort(int* A, int n)4 {5// write code here6int temp;7for(int i = 1; i < n; i ++)8 {9 temp = A[i];10for(int j = i - 1; j >= 0; j--)11 {12if(temp < A[j])13 {14 A[j + 1] = A[j];15if(j == 0)16 {17 A[j] = temp;18 }19 }20else21 {22 A[j + 1] = temp;23break;24 }25 }26 }27return A;28 }29 };四,归并排序思想:对数组中每个数看成是长度为1的有序区间,接着合并相邻两个长度为1的有序区间,变为长度为2的有序区间,接着合并相邻长度为2的有序区间变成长度为4的有序区间,依次进⾏,只到排序完成⽰意图:⾸先为长度为1的有序区间合并为长度为2的有序区间合并为长度为4的有序区间合并为长度为8的有序区间,排序完成时间复杂度:O(nlogn)空间复杂度:O(N)代码1class MergeSort {2public:3int* mergeSort(int* A, int n)4 {5 mergeSort(A,0,n-1);6return A;78 }9void mergeSort(int* A, int left, int right)10 {11if(left == right)12return;13int mid=(left+right)/2;14 mergeSort(A,left,mid);15 mergeSort(A,mid+1,right);16 merge_p(A,left,mid,right);17return;18 }1920void merge_p(int* A, int left, int mid, int right)21 {22int* temp = new int[right - left + 1];23int l = left;24int r = mid + 1;25int k = 0;26while(l <= mid && r <= right)27 {28if(A[l] < A[r])29 temp[k++] = A[l++];30else31 temp[k++] = A[r++];32 }33while(l <= mid)34 temp[k++] = A[l++];35while(r <= right)36 temp[k++] = A[r++];37for(int i = 0; i < k; i++)38 {39 A[left + i] = temp[i];40 }41 }4243 };五,快速排序思想:随机选择数组中的数,⼩于等于这个数的放在左边,⼤于这个数的放在右边,递归调⽤以上步骤,完成排序⽰意图:⾸先随机选择,划分区间递归调⽤,即可完成排序。
八种基本排序及其空间复杂度
八种基本排序及其空间复杂度排序,听起来就像是让东西乖乖排队一样,没错,这就是它的意思!在这个信息爆炸的时代,数据多得让人眼花缭乱,排序就是让我们把这些数据变得整齐划一的好帮手。
今天,我们来聊聊八种基本的排序方法,当然也顺便说说它们的空间复杂度,别担心,我会尽量让这段旅程轻松愉快,保证你听了不打瞌睡。
冒泡排序,名字听起来是不是很萌?就像泡泡一样,慢慢浮上来。
它的原理简单得令人发笑,两两比较,把较大的“泡泡”往后推,简直像是在给小朋友们上课,让他们学会乖乖排队。
可这家伙慢得要命,特别是在数据量大的时候,简直像是乌龟赛跑,空间复杂度只有O(1),真是个勤俭节约的小家伙。
再来就是选择排序,顾名思义,它喜欢选择最小的那个,然后把它放到前面。
就像逛超市时,你每次都挑最便宜的商品,心里想着“哎呀,这个买了肯定划算!”但是,这货也慢得让人抓狂,时间复杂度是O(n^2),但是空间复杂度还是O(1),让我们知道选择固然重要,但速度更重要啊!插入排序就像是老派的舞会,大家一个个走上舞台,找到自己的位置。
刚开始,可能有点笨拙,但一旦上手,嘿,效率可高了。
尤其是在小数据量的时候,简直就像刀切黄油,流畅得不得了。
它的空间复杂度同样是O(1),这小子真是能省就是能省,值得点赞!然后,我们得聊聊快速排序,这个家伙可不简单。
它聪明地选择一个“基准”,然后把比它小的放一边,大的放另一边,瞬间就像变魔术一样。
时间复杂度在平均情况下是O(n log n),空间复杂度是O(log n),哎呀,这小子真的是神乎其神,仿佛在说:“看,我多聪明!”再说说归并排序,这是一种比较优雅的排序方式,仿佛优雅的绅士走入舞会。
他先把数据分成小块,然后再慢慢合并,像拼图一样,最终把完整的画面呈现出来。
时间复杂度同样是O(n log n),但是它的空间复杂度可是O(n),得花费点空间来存放那些临时的数据。
然后有个小家伙叫希尔排序,这名字听上去有点高深,其实就是插入排序的升级版。
8大排序算法
排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
我们这里说说八大排序就是内部排序。
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;基本思想:将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。
即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
要点:设立哨兵,作为临时存储和判断数组边界之用。
直接插入排序示例:如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。
所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
算法的实现:1.void print(int a[], int n ,int i){2. cout<<i <<":";3.for(int j= 0; j<8; j++){4. cout<<a[j] <<" ";5. }6. cout<<endl;7.}8.9.10.void InsertSort(int a[], int n)11.{12.for(int i= 1; i<n; i++){13.if(a[i] < a[i-1]){ //若第i个元素大于i-1元素,直接插入。
小于的话,移动有序表后插入14.int j= i-1;15.int x = a[i]; //复制为哨兵,即存储待排序元素16. a[i] = a[i-1]; //先后移一个元素17.while(x < a[j]){ //查找在有序表的插入位置18. a[j+1] = a[j];19. j--; //元素后移20. }21. a[j+1] = x; //插入到正确位置22. }23. print(a,n,i); //打印每趟排序的结果24. }25.26.}27.28.int main(){29.int a[8] = {3,1,5,7,2,4,9,6};30. InsertSort(a,8);31. print(a,8,8);32.}效率:时间复杂度:O(n^2).其他的插入排序有二分插入排序,2-路插入排序。
十大经典排序算法(动图演示)
⼗⼤经典排序算法(动图演⽰)0、算法概述0.1 算法分类⼗种常见排序算法可以分为两⼤类:⽐较类排序:通过⽐较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为⾮线性时间⽐较类排序。
⾮⽐较类排序:不通过⽐较来决定元素间的相对次序,它可以突破基于⽐较排序的时间下界,以线性时间运⾏,因此也称为线性时间⾮⽐较类排序。
0.2 算法复杂度0.3 相关概念稳定:如果a原本在b前⾯,⽽a=b,排序之后a仍然在b的前⾯。
不稳定:如果a原本在b的前⾯,⽽a=b,排序之后 a 可能会出现在 b 的后⾯。
时间复杂度:对排序数据的总的操作次数。
反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执⾏时所需存储空间的度量,它也是数据规模n的函数。
1、冒泡排序(Bubble Sort)冒泡排序是⼀种简单的排序算法。
它重复地⾛访过要排序的数列,⼀次⽐较两个元素,如果它们的顺序错误就把它们交换过来。
⾛访数列的⼯作是重复地进⾏直到没有再需要交换,也就是说该数列已经排序完成。
这个算法的名字由来是因为越⼩的元素会经由交换慢慢“浮”到数列的顶端。
1.1 算法描述⽐较相邻的元素。
如果第⼀个⽐第⼆个⼤,就交换它们两个;对每⼀对相邻元素作同样的⼯作,从开始第⼀对到结尾的最后⼀对,这样在最后的元素应该会是最⼤的数;针对所有的元素重复以上的步骤,除了最后⼀个;重复步骤1~3,直到排序完成。
1.2 动图演⽰1.3 代码实现function bubbleSort(arr) {var len = arr.length;for (var i = 0; i < len - 1; i++) {for (var j = 0; j < len - 1 - i; j++) {if (arr[j] > arr[j+1]) { // 相邻元素两两对⽐var temp = arr[j+1]; // 元素交换arr[j+1] = arr[j];arr[j] = temp;}}}return arr;}2、选择排序(Selection Sort)选择排序(Selection-sort)是⼀种简单直观的排序算法。
十大经典排序法
十大经典排序法
1. 冒泡排序(Bubble Sort):通过不断比较相邻元素并交换位置来排序,每一轮将最大的元素冒泡到最后。
2. 选择排序(Selection Sort):通过找到当前未排序部分的最小元素,将其放置到已排序部分的末尾,逐步构建有序序列。
3. 插入排序(Insertion Sort):将未排序元素逐个插入到已排序部分的正确位置,从而逐步构建有序序列。
4. 希尔排序(Shell Sort):是插入排序的改进版本,通过比较相隔一定间隔的元素进行排序,逐渐缩小间隔直至为1。
5. 归并排序(Merge Sort):采用分治策略,将待排序序列不断拆分为子序列,然后将子序列排序并合并得到最终有序序列。
6. 快速排序(Quick Sort):也是采用分治策略,通过选择一个基准元素将序列划分为左右两部分,分别对两部分进行排序。
7. 堆排序(Heap Sort):利用二叉堆的性质来进行排序,将待排序元素构建成最大(最小)堆,然后依次取出堆顶元素并调整堆结构。
8. 计数排序(Counting Sort):适用于元素值范围较小的情况,通过统计元素出现的次数,然后根据统计结果得到有序序列。
9. 桶排序(Bucket Sort):将元素根据大小分配到不同的桶中,每个桶内部再分别进行排序,最后将各个桶中的元素合并得到有序序列。
10. 基数排序(Radix Sort):将待排序元素按照位数进行排序,先按个位排序,再按十位排序,依此类推,直到最高位排序完成。
8种排序算法
J=2(38) [38 49] 65 97 76 13 27 49
J=3(65) [38 49 65] 97 76 13 27 49
J=4(97) [38 49 65 97] 76 13 27 49
J=5(76) [38 49 65 76 97] 13 27 49
2. 堆的定义: N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:
Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])
堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均大于等于其孩子结点的关键字。例如序列10,15,56,25,30,70就是一个堆,它对应的完全二叉树如上图所示。这种堆中根结点(称为堆顶)的关键字最小,我们把它称为小根堆。反之,若完全二叉树中任一非叶子结点的关键字均大于等于其孩子的关键字,则称之为大根堆。
(6)基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。
2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,对基于比较的排序算法而言,元素交换的次数可能会少一些(个人感觉,没有证实)。
c语言、数据结构中的8种排序分析与代码
8种排序一、冒泡排序(小者上扬原则)已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。
首先比较a[1]与a[2]的值,若a[1]大于a[2]则交换两者的值,否则不变。
再比较a[2]与a[3]的值,若a[2]大于a[3]则交换两者的值,否则不变。
再比较a[3]与a[4],以此类推,最后比较a[n-1]与a[n]的值。
这样处理一轮后,a[n]的值一定是这组数据中最大的。
再对a[1]~a[n-1]以相同方法处理一轮,则a[n-1]的值一定是a[1]~a[n-1]中最大的。
再对a[1]~a[n-2]以相同方法处理一轮,以此类推。
共处理n-1轮后a[1]、a[2]、……a[n]就以升序排列了。
优点:稳定,比较次数已知;缺点:慢,每次只能移动相邻两个数据,移动数据的次数多。
初始关键字[49 38 65 97 76 13 27 49]第一趟排序后[38 49 65 76 13 27 49] 97第二趟排序后[38 49 65 13 27 49] 76 97第三趟排序后[38 49 13 27 49] 65 76 97第四趟排序后[38 13 27 49] 49 65 76 97第五趟排序后[38 13 27] 49 49 65 76 97第六趟排序后[13 27]38 49 49 65 76 97第七趟排序后[13] 27 38 49 49 65 76 97最后排序结果13 27 38 49 49 76 76 97#include <iostream>using namespace std;void main(){int i,j,k;int a[8]={49,38,65,97,76,13,27,49};for(i=7;i>=0;i--){for(j=0;j<i;j++){if(a[j]>a[j+1]){k=a[j];a[j]=a[j+1];a[j+1]=k;}}}for(i=0;i<8;i++)cout<<a[i]<<endl;}二、选择排序①初始状态:无序区为R[1..n],有序区为空。
十大经典排序算法-C语言
十大经典排序算法(动图演示,收藏好文)0.1 算法分类十种常见排序算法可以分为两大类:非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
0.2 算法复杂度0.3 相关概念稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。
反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
1冒泡排序是一种简单的排序算法。
它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。
走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
1.1 算法描述▪比较相邻的元素。
如果第一个比第二个大,就交换它们两个;▪对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;▪针对所有的元素重复以上的步骤,除了最后一个;▪重复步骤1~3,直到排序完成。
1.2 动图演示1.3 代码实现function bubbleSort(arr) {var len = arr.length;for (var i = 0; i < len - 1; i++) {for (var j = 0; j < len - 1 - i; j++) {if (arr[j] > arr[j+1]) { // 相邻元素两两对比var temp = arr[j+1]; // 元素交换arr[j+1] = arr[j];arr[j] = temp;}}}return arr;}2选择排序(Selection-sort)是一种简单直观的排序算法。
数据结构--排序算法介绍
数据结构--排序算法总结概述排序的分类:内部排序和外部排序内部排序:数据记录在内存中进行排序外部排序:因排序的数据量大,需要内存和外存结合使用进行排序这里总结的八大排序是属于内部排序:当n比较大的时候,应采用时间复杂度为(nlog2n)的排序算法:快速排序、堆排序或归并排序。
其中,快速排序是目前基于比较的内部排序中被认为最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短。
———————————————————————————————————————————————————————————————————————插入排序——直接插入排序(Straight Insertion Sort)基本思想:将一个记录插入到已排序好的有序表中,从而得到一个新的,记录数增1的有序表。
即:先将序列的第1个记录看成一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
要点:设立哨兵,用于临时存储和判断数组边界直接插入排序示例:插入排序是稳定的,因为如果一个带插入的元素和已插入元素相等,那么待插入元素将放在相等元素的后边,所以,相等元素的前后顺序没有改变。
算法实现:[cpp]view plain copy1.#include<iostream>ing namespace std;3.4.void print(int a[], int n ,int i)5.{6. cout<<i<<":";7.for(int j= 0; j<8; j++){8. cout<<a[j] <<" ";9. }10. cout<<endl;11.}12.13.void InsertSort(int a[],int n)14.{15.int i,j,tmp;16.for(i=1;i<n;++i)17. {18.// 如果第i个元素大于第i-1个元素,直接插入19.// 否则20.// 小于的话,移动有序表后插入21.if(a[i]<a[i-1])22. {23. j=i-1;24. tmp=a[i]; // 复制哨兵,即存储待排序元素25. a[i]=a[i-1]; // 先后移一个元素26.while(tmp<a[j])27. {28.// 哨兵元素比插入点元素小,后移一个元素29. a[j+1]=a[j];30. --j;31. }32. a[j+1]=tmp; // 插入到正确的位置33. }34. print(a,n,i); // 打印每一趟排序的结果35. }36.}37.38.int main()39.{40.int a[8]={3,1,5,7,3,4,8,2};41. print(a,8,0); // 打印原始序列42. InsertSort(a,8);43.return 0;44.}分析:时间复杂度:O(n^2)———————————————————————————————————————————————————————————————————————插入排序——希尔排序(Shell Sort)基本思想:先将整个待排序的记录序列分割成为若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录依次进行直接插入排序。
八大排序总结
1.冒泡排序(稳定排序)思想:从第一个开始,每两个数进行比较,将较大(较小)的数交换到前面直到最后一个数。
两层循环嵌套,内层用于遍历一次数组,每次两两比较,外层控制遍历的次数。
时间复杂度:O(n^2).空间复杂度:O(1)。
2.直接插入排序(稳定排序)思想:每次把一个待排的数记录下来,按其值的大小插入前面适当的位置,知道全部插完为止。
时间复杂度:O(n^2)。
空间复杂度:O(1)。
3.选择排序(不稳定排序)思想:两层for循环嵌套,内层用于每次从(j=i+1)的位置开始,将最大(最下)的数找出来,和i位置的值交换,i++,外层控制找最大值的次数。
时间复杂度:O(n^2)。
空间复杂度:O(1)。
4.希尔排序(不稳定排序)思想:他是一种分组插入排序的方法,对于分的组数应该互为素数。
每次将下标间距相等的数分为一组,对每组进行插入排序,直到组数变成一为止,最后对整个数组进行一次排序。
时间复杂度:O(n^1.3)。
空间复杂度:O(1)。
5.堆排序(不稳定排序)思想:堆排序利用堆积树进行选择排序。
堆是二叉树,当由小到大排序时,建立大根堆,首先对树进行调整(从最后一棵枝桠开始),调整后的最大值便存储在0节点,将0节点的值与最后一个节点的值交换,便找出了整个数组的最大值,用for循环对整个树在进行调整,依次得到较大的值,知道数组有序。
时间复杂度:O(nlog2n)。
空间复杂度:O(1)。
6.快速排序(不稳定排序)思想:快排是对冒泡排序的一种改进。
每次将第一个数作为基准,一趟排序后,数组要分成两部分,比基准小的都放在左边,大的放在右边,因此需要定义low,high分别指向每块的第一个数和最后一个数。
通过对基准左边和右边数的个数的判断,分别对左右两边进行找基准排序,对此,既可以使用递归进行后面的排序,也可用while循环进行。
优化一:每次用rand()函数随机产生high指向的值。
rand()%(end-start)+start.产生从0到(end-start)+start.的随机数优化二:三数取中法。
8种排序算法Ultimate
8种排序算法(有代码)个人对这8种排序算法的理解,希望对大家有点帮助.趁自修时间,自己将这8种排序的代码写了一下.......1.简单的选择排序bool selectionsort(int *array,int n) //array为存储数据的数组,n为数组元素个数{int k,temp; //k用来存储,临时最小数据的位置for(int i=0;i<n-1;i++){k=i;for(int j=i+1;j<n;j++) //从第i个数开始选择最小数位置,存于k中if(array[j]<array[k])k=j;if(k!=i) //若最小数,不为array[i],则array[i]与array[k]进行交换 {temp=array[i];array[i]=array[k];array[k]=temp;}}return true;}思想:逐个找出,第一小,第二小....第n小的数...算法平均时间复杂度: O(n^2)2.插入排序bool insertionsort(int *array,int n){int temp; //用来存储,插入的数据for(int i=1;i<n;i++){temp=array[i]; //用temp记录array[i]for(int j=i-1;j>=0;j--) //逐个向前寻找插入点if(temp>array[j]) //找到,跳出循环break;else //没找到,将前一个数据后移array[j+1]=array[j];}array[j+1]=temp;}return true;}思想: 逐个取数,插入一个有序数组(从后向前插)算法平均时间复杂度: O(n^2)3.自底向上排序bool bottomupsort(int *array,int n){int length=1,temp_length,i; //temp_length表示单个合并数组的长度while(length<n){temp_length=length; //length表示合并后数组的长度length=2*temp_length;i=0; //i用于记录合并数组的起始位置while(i+length-1<=n-1){merge(array,i,i+temp_length,i+length-1); //合并i~i+temp_length-1 和 i+temp_length~i+length-1 段i=i+length; //取下一个合并段的起始位置}if(i+temp_length<n-1)merge(array,i,i+temp_length,n-1); //对尾部剩余段合并}return true;}bool merge(int *array,int start1,int start2,int n) //合并两个有序数{int temp_n=n-start1+1, //两合并数组的长度和*temp_array,n1=start2-1, //第一个有序数组的末端位置temp_start1=start1; //记录start1的初始位置temp_array=(int *)malloc(sizeof(int)*temp_n); //申请长度为temp_n 的整形空间,用于临时存储合并后的数组for(int i=0;start1<=n1&&start2<=n;i++) //对两个有序数组进行合并,存储于temp_array{if(array[start1]<=array[start2]){temp_array[i]=array[start1];start1++;}else{temp_array[i]=array[start2];start2++;}}if(start1<=n1){while(start1<=n1){temp_array[i++]=array[start1];start1++;}}else{while(start2<=n){temp_array[i++]=array[start2];start2++;}}for(i=0,start1=temp_start1;i<temp_n;start1++,i++) //将合并后的有序数组,复制到array数组中{array[start1]=temp_array[i];}free(temp_array);return true;}思想: 将数组的个部分,两两有序数组进行合并算法平均时间复杂度: O(nlogn)4.快速排序void QuickSort(int low,int high,int *array){int pos;if(low<high){pos=SPLIT(low,high,array); //以array[low]进行划分,pos最为划分点//前一部分<array[low],后一部分,反之QuickSort(low,pos-1,array); //对划分后的前一部分递归处理QuickSort(pos+1,high,array); //对划分后的后一部分递归处理}}int SPLIT(int low,int high,int *array){int temp=array[low]; //用temp来记录划分数while(low<high){while(array[high]>temp&&low<high) //寻找小于temp的数high--;if(low==high)break;else{array[low]=array[high];low++;}while(array[low]<temp&&low<high) //寻找大于temp的数low++;if(low==high)break;else{array[high]=array[low];high--;}}array[low]=temp; //最终low=high作为划分点,并将划分数存于array[low]return low;}思想:就是你从数组中任取一个元素 p (可随机取,现在以取第一个为例)以P作为主元,对数组进行划分 ,前一部分小于 P,后一部分大于p最后划分处存储 p然后分别对划分后的前一部分和后一部分递归调用算法平均时间复杂度: O(nlogn)5.归并排序bool MergeSort(int low,int high,int *array){int middle=(high+low)/2; //将数组划分为2分if(low<high){MergeSort(low,middle,array); //对前一部分进行递归处理MergeSort(middle+1,high,array); //对后一部分进行递归处理HeBing(low,middle,middle+1,high,array); //将排序后的,前后两部分,进行合并}return true;}bool HeBing(int low1,int high1,int low2,int high2,int *array){int *temp,i=low1,j=low2,k=0;temp=(int *)malloc((high2-low1+1)*sizeof(int)); //temp用于临时存储合并后的数组while(i<=high1&&j<=high2) //对两个有序数组进行合并{if(array[i]<array[j]){temp[k++]=array[i];i++;}else{temp[k++]=array[j];j++;}}if(i<=high1){while(i<=high1)temp[k++]=array[i++];}else{while(j<=high2)temp[k++]=array[j++];}for(i=low1,j=0;i<=high2;i++,j++) //将合并后的数组,复制到array中{array[i]=temp[j];}free (temp);return true;}思想: 将数组划分为小数组,通过局部的有序合并,解决问题算法平均时间复杂度: O(nlogn)6.冒泡排序bool bubblesort(int *array,int n){int flag=1, //用来标记是否发生交换temp;for(int i=0;i<n-1;i++){for(int j=i+1;j<n;j++){if(array[j]<array[j-1]){temp=array[i];array[i]=array[j];array[j]=temp;flag=0;}}if(flag) //如果flag为真,及没发生交换,直接跳出循环break;elseflag=1;}return true;}思想: 相邻两数比较,小数放前面算法平均时间复杂度: O(n^2)7.堆排序bool slipdown(int *array,int cur,int n){for(int next=2*cur;next<=n;next=2*cur) //next表示cur的左孩子{if(next<n&&array[next]<array[next+1]) //取cur的两个孩子的大者next++;if(array[next]<array[cur])break;int temp=array[cur]; //交换cur和他孩子中的大者array[cur]=array[next];array[next]=temp;cur=next; //令当前需要调整的关键字的位置cur=next}return true;}bool heapsort(int *array,int n){int temp;for(int i=n/2;i>0;i--) //将数组调整为大顶堆slipdown(array,i,n);for(int N=n;N>1;N--) //选出堆中最大元,存于N位置,循环进行{temp=array[N];array[N]=array[1];array[1]=temp;slipdown(array,1,N-1);}return true;}思想: 用二叉树的结构来表示数组,及用数组来表示二叉树的结构,比如i为父节点其孩子为,2i,和2i+1其中,大顶堆中父节点大于其两个孩子算法平均时间复杂度: O(nlogn)8.基数排序bool radixsort(int *array,int n){L TENL[10]; //其中TENL[m].number中存储,数据的第i位为m的数据int k;for(int i=0;i<10;i++)TENL[i].n=0;for(i=1;i<=5;i++) //这里假设数据都小于100000,对数据进行五次分配{for(int j=0;j<n;j++) //对数据进行分配{k=getnum(array[j],i);TENL[k].number[TENL[k].n]=array[j];TENL[k].n++;}j=0;for(k=0;k<10;k++) //将此次分配后的数据,按顺序重新置入array中{for(int m=0;m<TENL[k].n;m++)array[j++]=TENL[k].number[m];TENL[k].n=0;}}return true;}int getnum(int num,int i) //从个位起,获得num的第i为数据{int temp=1;for(int j=0;j<i;j++)temp=temp*10;return (num%temp-num%(temp/10))/(temp/10);}思想:先从数据的低位开始,进行分配,分成10个空间,分别存储位为,0,1,2,3 (9)重复的对次地位操作,知道预定的高位,排序完成8种排序算法个人对这8种排序算法的理解,希望对大家有点帮助.趁自修时间,自己将这8种排序的代码写了一下.......1.简单的选择排序思想:逐个找出,第一小,第二小....第n小的数...算法平均时间复杂度: O(n^2)2.插入排序思想: 逐个取数,插入一个有序数组(从后向前插)算法平均时间复杂度: O(n^2)3.自底向上排序思想: 将数组的个部分,两两有序数组进行合并算法平均时间复杂度: O(nlogn)4.快速排序思想:就是你从数组中任取一个元素 p (可随机取,现在以取第一个为例)以P作为主元,对数组进行划分 ,前一部分小于 P,后一部分大于p最后划分处存储 p然后分别对划分后的前一部分和后一部分递归调用算法平均时间复杂度: O(nlogn)5.归并排序思想: 将数组划分为小数组,通过局部的有序合并,解决问题算法平均时间复杂度: O(nlogn)6.冒泡排序思想: 相邻两数比较,小数放前面算法平均时间复杂度: O(n^2)7.堆排序思想: 用二叉树的结构来表示数组,及用数组来表示二叉树的结构,比如i为父节点其孩子为,2i,和2i+1其中,大顶堆中父节点大于其两个孩子算法平均时间复杂度: O(nlogn)8.基数排序思想:先从数据的低位开始,进行分配,分成10个空间,分别存储位为,0,1,2,3 (9)重复的对次地位操作,知道预定的高位,排序完成以关键字序列(265,301,751,129,937,863,742,694,076,438)为例,分别写出执行以下排序算法的各趟排序结束时,关键字序列的状态。
15种排序算法
15种排序算法
1. 冒泡排序 - 依次比较相邻元素的大小,将较大的数向后移动,直到没有交换
2. 选择排序 - 选择最小的元素,放到数组的起始位置,再从剩余元
素中选择最小的,以此类推
3. 插入排序 - 将一个元素插入已经排好序的序列中,从后向前比较
并移动元素
4. 希尔排序 - 将数组拆分成若干个子序列进行插入排序,缩小增量,直到增量为1
5. 归并排序 - 将数组分成两部分,分别排序,然后合并两个有序数
组
6. 快速排序 - 选取一个基准元素,将小于基准元素的放在左边,大
于基准元素的放在右边,然后分别对左右两边再递归快速排序
7. 堆排序 - 将数组建立一个最大/小堆,然后依次取出堆顶元素,再
将剩余元素重建堆
8. 计数排序 - 计算每个元素的出现次数,然后计算出每个元素应该
在排序后的序列中的位置
9. 桶排序 - 将元素分配到各个桶中,然后对每个桶进行排序,再依
次将各个桶中的元素输出到序列中
10. 基数排序 - 从低位到高位依次将元素排序,相同位上的元素按照
相同方式进行排序
11. 合并排序 - 将多个有序数组合并成一个有序数组,采用分治的思
想
12. 鸡尾酒排序 - 进行双向冒泡排序,先将最大的元素放到最后,再
将最小的元素放到前面,如此交替进行
13. 地精排序 - 选取一个随机数作为划分元素,将小于该随机数的元
素放在左边,大于该随机数的元素放在右边,然后对左右两边递归排
序
14. 跳跃表排序 - 利用跳跃表结构,快速查找元素并插入有序序列中
15. 非递归归并排序 - 利用非递归的方式实现归并排序,将序列分解成多个子序列,依次合并子序列。
数据结构之——八大排序算法
数据结构之——⼋⼤排序算法排序算法⼩汇总 冒泡排序⼀般将前⾯作为有序区(初始⽆元素),后⾯作为⽆序区(初始元素都在⽆序区⾥),在遍历过程中把当前⽆序区最⼩的数像泡泡⼀样,让其往上飘,然后在⽆序区继续执⾏此操作,直到⽆序区不再有元素。
这块是对⽼式冒泡排序的⼀种优化,因为当某次冒泡结束后,可能数组已经变得有序,继续进⾏冒泡排序会增加很多⽆⽤的⽐较次数,提⾼时间复杂度。
所以我们增加了⼀个标识变量flag,将其初始化为1,外层循环还是和⽼式的⼀样从0到末尾,内存循环我们改为从最后⾯向前⾯i(外层循环所处的位置)处遍历找最⼩的,如果在内存没有出现交换,说明⽆序区的元素已经变得有序,所以不需要交换,即整个数组已经变得有序。
(感谢@站在远处看童年在评论区的指正)#include<iostream>using namespace std;void sort(int k[] ,int n){int flag = 1;int temp;for(int i = 0; i < n-1 && flag; i++){flag = 0;for(int j = n-1; j > i; j--){/*下⾯这⾥和i没关系,注意看这块,从下往上travel,两两⽐较,如果不合适就调换,如果上来后⼀次都没调换,说明下⾯已经按顺序拍好了,上⾯也是按顺序排好的,所以完美!*/if(k[j-1] > k[j]){temp = k[j-1];k[j-1] = k[j];k[j] = temp;flag = 1;}}}}int main(){int k[3] = {0,9,6};sort(k,3);for(int i =0; i < 3; i++)printf("%d ",k[i]);}快速排序(Quicksort),基于分治算法思想,是对冒泡排序的⼀种改进。
快速排序由C. A. R. Hoare在1960年提出。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
八大排序算法排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
我们这里说说八大排序就是内部排序。
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;基本思想:将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。
即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
要点:设立哨兵,作为临时存储和判断数组边界之用。
直接插入排序示例:如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。
所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
算法的实现:[cpp]view plaincopyprint?1.void print(int a[], int n ,int i){2. cout<<i <<":";3.for(int j= 0; j<8; j++){4. cout<<a[j] <<" ";5. }6. cout<<endl;7.}8.9.10.void InsertSort(int a[], int n)11.{12.for(int i= 1; i<n; i++){13.if(a[i] < a[i-1]){ //若第i个元素大于i-1元素,直接插入。
小于的话,移动有序表后插入14.int j= i-1;15.int x = a[i]; //复制为哨兵,即存储待排序元素16. a[i] = a[i-1]; //先后移一个元素17.while(x < a[j]){ //查找在有序表的插入位置18. a[j+1] = a[j];19. j--; //元素后移20. }21. a[j+1] = x; //插入到正确位置22. }23. print(a,n,i); //打印每趟排序的结果24. }25.26.}27.28.int main(){29.int a[8] = {3,1,5,7,2,4,9,6};30. InsertSort(a,8);31. print(a,8,8);32.}效率:时间复杂度:O(n^2).其他的插入排序有二分插入排序,2-路插入排序。
希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。
希尔排序又叫缩小增量排序基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
操作方法:1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;2. 按增量序列个数k,对序列进行k 趟排序;3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。
仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
希尔排序的示例:算法实现:我们简单处理增量序列:增量序列d = {n/2 ,n/4, n/8 .....1} n为要排序数的个数即:先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。
继续不断缩小增量直至为1,最后使用直接插入排序完成排序。
[cpp]view plaincopyprint?1.void print(int a[], int n ,int i){2. cout<<i <<":";3.for(int j= 0; j<8; j++){4. cout<<a[j] <<" ";5. }6. cout<<endl;7.}8./**9. * 直接插入排序的一般形式10. *11. * @param int dk 缩小增量,如果是直接插入排序,dk=112. *13. */14.15.void ShellInsertSort(int a[], int n, int dk)16.{17.for(int i= dk; i<n; ++i){18.if(a[i] < a[i-dk]){ //若第i个元素大于i-1元素,直接插入。
小于的话,移动有序表后插入19.int j = i-dk;20.int x = a[i]; //复制为哨兵,即存储待排序元素21. a[i] = a[i-dk]; //首先后移一个元素22.while(x < a[j]){ //查找在有序表的插入位置23. a[j+dk] = a[j];24. j -= dk; //元素后移25. }26. a[j+dk] = x; //插入到正确位置27. }28. print(a, n,i );29. }30.31.}32.33./**34. * 先按增量d(n/2,n为要排序数的个数进行希尔排序35. *36. */37.void shellSort(int a[], int n){38.39.int dk = n/2;40.while( dk >= 1 ){41. ShellInsertSort(a, n, dk);42. dk = dk/2;43. }44.}45.int main(){46.int a[8] = {3,1,5,7,2,4,9,6};47.//ShellInsertSort(a,8,1); //直接插入排序48. shellSort(a,8); //希尔插入排序49. print(a,8,8);50.}希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。
目前还没有人给出选取最好的增量因子序列的方法。
增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。
希尔排序方法是一个不稳定的排序方法。
基本思想:在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
简单选择排序的示例:操作方法:第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;以此类推.....第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,直到整个序列按关键码有序。
算法实现:[cpp]view plaincopyprint?1.void print(int a[], int n ,int i){2. cout<<"第"<<i+1 <<"趟 : ";3.for(int j= 0; j<8; j++){4. cout<<a[j] <<" ";5. }6. cout<<endl;7.}8./**9. * 数组的最小值10. *11. * @return int 数组的键值12. */13.int SelectMinKey(int a[], int n, int i)14.{15.int k = i;16.for(int j=i+1 ;j< n; ++j) {17.if(a[k] > a[j]) k = j;18. }19.return k;20.}21.22./**23. * 选择排序24. *25. */26.void selectSort(int a[], int n){27.int key, tmp;28.for(int i = 0; i< n; ++i) {29. key = SelectMinKey(a, n,i); //选择最小的元素30.if(key != i){31. tmp = a[i]; a[i] = a[key]; a[key] = tmp; //最小元素与第i位置元素互换32. }33. print(a, n , i);34. }35.}36.int main(){37.int a[8] = {3,1,5,7,2,4,9,6};38. cout<<"初始值:";39.for(int j= 0; j<8; j++){40. cout<<a[j] <<" ";41. }42. cout<<endl<<endl;43. selectSort(a, 8);44. print(a,8,8);45.}简单选择排序的改进——二元选择排序简单选择排序,每趟循环只能确定一个元素排序后的定位。
我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。
改进后对n 个数据进行排序,最多只需进行[n/2]趟循环即可。
具体实现如下:[cpp]view plaincopyprint?1.void SelectSort(int r[],int n) {2.int i ,j , min ,max, tmp;3.for (i=1 ;i <= n/2;i++) {4.// 做不超过n/2趟选择排序5. min = i; max = i ; //分别记录最大和最小关键字记录位置6.for (j= i+1; j<= n-i; j++) {7.if (r[j] > r[max]) {8. max = j ; continue ;9. }10.if (r[j]< r[min]) {11. min = j ;12. }13. }14.//该交换操作还可分情况讨论以提高效率15. tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp;16. tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp;17.18. }19.}堆排序是一种树形选择排序,是对直接选择排序的有效改进。