排序算法(简)

排序技术有很多种,下面简单介绍一下几种。

一  插入排序

1.1  直接插入排序

基本思想:每次将一个待排序额记录按其关键码的大小插入到一个已经排好序的有序序列中,直到全部记录排好序。

图解:

代码实现:

[cpp] view plaincopy

  1. //直接顺序排序
  2. void InsertSort(int r[], int n)
  3. {
  4. for (int i=2; i<n; i++)
  5. {
  6. r[0]=r[i];                        //设置哨兵
  7. for (int j=i-1; r[0]<r[j]; j--)   //寻找插入位置
  8. r[j+1]=r[j];                //记录后移
  9. r[j+1]=r[0];
  10. }
  11. for(int k=1;k<n;k++)
  12. cout<<r[k]<<" ";
  13. cout<<"\n";
  14. }

1.2 希尔排序

基本思想是: 先将整个待排序记录序列分割成若干个子序列,在在序列内分别进行直接插入排序,待整个序列基本有序时,再对全体记录进行一次直接插入排序。

图解:


代码实现:

[cpp] view plaincopy

  1. <span style="font-size:14px;">//希尔排序
  2. void ShellSort(int r[], int n)
  3. {
  4. int i;
  5. int d;
  6. int j;
  7. for (d=n/2; d>=1; d=d/2)            //以增量为d进行直接插入排序
  8. {
  9. for (i=d+1; i<n; i++)
  10. {
  11. r[0]=r[i];                 //暂存被插入记录
  12. for (j=i-d; j>0 && r[0]<r[j]; j=j-d)
  13. r[j+d]=r[j];       //记录后移d个位置
  14. r[j+d]=r[0];
  15. }
  16. }
  17. for(i=1;i<n;i++)
  18. cout<<r[i]<<" ";
  19. cout<<"\n";
  20. }</span>

二 交换排序

2.1 起泡排序

起泡排序是交换排序中最简单的排序方法,其基本思想是: 两两比较相邻记录的关键码,如果反序则交换,直到没有反序的记录为止。

图解:

代码实现:

[cpp] view plaincopy

  1. <span style="font-size:14px;">//起泡排序
  2. void BubbleSort(int r[], int n)
  3. {
  4. int temp;
  5. int exchange;
  6. int bound;
  7. exchange=n-1;                       //第一趟起泡排序的范围是r[0]到r[n-1]
  8. while (exchange)                    //仅当上一趟排序有记录交换才进行本趟排序
  9. {
  10. bound=exchange;
  11. exchange=0;
  12. for (int j=0; j<bound; j++)     //一趟起泡排序
  13. if (r[j]>r[j+1])
  14. {
  15. temp=r[j];
  16. r[j]=r[j+1];
  17. r[j+1]=temp;
  18. exchange=j;                   //记录每一次发生记录交换的位置
  19. }
  20. }
  21. for(int i=0;i<n;i++)
  22. cout<<r[i]<<" ";
  23. cout<<"\n";
  24. }</span>

2.2快速排序

基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

图解:

代码实现:

[cpp] view plaincopy

  1. //快速排序一次划分
  2. int Partition(int r[], int first, int end)
  3. {
  4. int i=first;                        //初始化
  5. int j=end;
  6. int temp;
  7. while (i<j)
  8. {
  9. while (i<j && r[i]<= r[j])
  10. j--;                        //右侧扫描
  11. if (i<j)
  12. {
  13. temp=r[i];                 //将较小记录交换到前面
  14. r[i]=r[j];
  15. r[j]=temp;
  16. i++;
  17. }
  18. while (i<j && r[i]<= r[j])
  19. i++;                         //左侧扫描
  20. if (i<j)
  21. {
  22. temp=r[j];
  23. r[j]=r[i];
  24. r[i]=temp;                //将较大记录交换到后面
  25. j--;
  26. }
  27. }
  28. return i;                           //i为轴值记录的最终位置
  29. }
  30. //快速排序
  31. void QuickSort(int r[], int first, int end)
  32. {
  33. if (first<end)
  34. {                                   //递归结束
  35. int pivot=Partition(r, first, end);  //一次划分
  36. QuickSort(r, first, pivot-1);//递归地对左侧子序列进行快速排序
  37. QuickSort(r, pivot+1, end);  //递归地对右侧子序列进行快速排序
  38. }
  39. }

三 选择排序

3.1 简单选择排序

基本思想:设所排序序列的记录个数为n。i取1,2,…,n-1,从所有n-i+1个记录(Ri,Ri+1,…,Rn)中找出排序码最小的记录,与第i个记录交换。执行n-1趟
后就完成了记录序列的排序。

图解:

代码实现:

[cpp] view plaincopy

  1. //简单选择排序
  2. void SelectSort(int r[ ], int n)
  3. {
  4. int i;
  5. int j;
  6. int index;
  7. int temp;
  8. for (i=0; i<n-1; i++)                //对n个记录进行n-1趟简单选择排序
  9. {
  10. index=i;
  11. for (j=i+1; j<n; j++)            //在无序区中选取最小记录
  12. if (r[j]<r[index])
  13. index=j;
  14. if (index!=i)
  15. {
  16. temp=r[i];
  17. r[i]=r[index];
  18. r[index]=temp;
  19. }
  20. }
  21. for(i=0;i<n;i++)
  22. cout<<r[i]<<" ";
  23. cout<<"\n";
  24. }

3.2 堆排序

堆的定义

是具有下列性质的完全二叉树:每个结点的值都小于或等于其左右孩子结点的值(小根堆);或者每个结点的值都大于或等于其左右孩子结点的值(大根堆)。

大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary
Heap),类似地可定义k叉堆。

假设当前要筛选结点的编号为k,堆中最后一个结点的编号为m,并且结点k的左右子树均是堆(即r[k+1] ~ r[m]满足堆的条件),则筛选算法用伪代码可描述为:

具体的筛选代码如下:

[cpp] view plaincopy

  1. //筛选法调整堆
  2. void Sift(int r[], int k, int m)
  3. {
  4. int i;
  5. int j;
  6. int temp;
  7. i=k;
  8. j=2*i+1;                            //置i为要筛的结点,j为i的左孩子
  9. while (j<=m)                          //筛选还没有进行到叶子
  10. {
  11. if (j<m && r[j]<r[j+1])
  12. j++;                          //比较i的左右孩子,j为较大者
  13. if (r[i]>r[j]) break;             //根结点已经大于左右孩子中的较大者
  14. else
  15. {
  16. temp=r[i];
  17. r[i]=r[j];
  18. r[j]=temp;                   //将根结点与结点j交换
  19. i=j;
  20. j=2*i+1;                     //被筛结点位于原来结点j的位置
  21. }
  22. }
  23. }

堆排序

堆排序的基本思想是:首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最大者即堆顶记录,然后将它从堆中移走(通常将堆顶记录和堆中最后一个记录交换),并将剩余的记录再调整成堆,这样又找出了次大的记录,以此类推,直到堆中只有一个记录为止。

(1)用大根堆排序的基本思想

① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区

② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key

③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。

……

直到无序区只有一个元素为止。

(2)大根堆排序算法的基本操作:

① 初始化操作:将R[1..n]构造为初始堆;

② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。

注意:

①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。

②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止

代码实现:

[cpp] view plaincopy

  1. //堆排序
  2. void HeapSort(int r[ ], int n)
  3. {
  4. int i;
  5. int temp;
  6. for (i=n/2; i>=0; i--)                //初始建堆,从最后一个非终端结点至根结点
  7. Sift(r, i, n) ;
  8. for (i=n-1; i>0; i--)                //重复执行移走堆顶及重建堆的操作
  9. {
  10. temp=r[i];
  11. r[i]=r[0];
  12. r[0]=temp;
  13. Sift(r, 0, i-1);
  14. }
  15. for(i=0;i<n;i++)
  16. cout<<r[i]<<" ";
  17. cout<<"\n";
  18. }

四 归并排序

二路归并排序

基本思想:将若干个有序序列进行两两归并,直至所有待排序记录都在一个有序序列为止。

一路归并算法实现:

[cpp] view plaincopy

  1. //一次归并
  2. void Merge(int r[], int r1[], int s, int m, int t)
  3. {
  4. int i=s;
  5. int j=m+1;
  6. int k=s;
  7. while (i<=m && j<=t)
  8. {
  9. if (r[i]<=r[j])
  10. r1[k++]=r[i++];            //取r[i]和r[j]中较小者放入r1[k]
  11. else
  12. r1[k++]=r[j++];
  13. }
  14. if (i<=m)
  15. while (i<=m)                  //若第一个子序列没处理完,则进行收尾处理
  16. r1[k++]=r[i++];
  17. else
  18. while (j<=t)                  //若第二个子序列没处理完,则进行收尾处理
  19. r1[k++]=r[j++];
  20. }

[cpp] view plaincopy

  1. //一趟归并
  2. void MergePass(int r[ ], int r1[ ], int n, int h)
  3. {
  4. int i=0;
  5. int k;
  6. while (i<=n-2*h)                     //待归并记录至少有两个长度为h的子序列
  7. {
  8. Merge(r, r1, i, i+h-1, i+2*h-1);
  9. i+=2*h;
  10. }
  11. if (i<n-h)
  12. Merge(r, r1, i, i+h-1, n);       //待归并序列中有一个长度小于h
  13. else for (k=i; k<=n; k++)            //待归并序列中只剩一个子序列
  14. r1[k]=r[k];
  15. }
  16. //归并排序的非递归算法
  17. void MergeSort1(int r[ ], int r1[ ], int n )
  18. {
  19. int h=1;
  20. int i;
  21. while (h<n)
  22. {
  23. MergePass(r, r1, n-1, h);           //归并
  24. h=2*h;
  25. MergePass(r1, r, n-1, h);
  26. h=2*h;
  27. }
  28. for(i=0;i<n;i++)
  29. cout<<r[i]<<" ";
  30. cout<<"\n";
  31. }

下面看看二路归并排序的递归实现

[cpp] view plaincopy

  1. //归并排序的递归算法
  2. void MergeSort2(int r[], int r1[], int r2[],int s, int t)
  3. {
  4. int m;
  5. if (s==t)
  6. {
  7. r1[s]=r[s];
  8. }
  9. else
  10. {
  11. m=(s+t)/2;
  12. MergeSort2(r, r2, r1, s, m);        //归并排序前半个子序列
  13. MergeSort2(r, r2, r1, m+1, t);      //归并排序后半个子序列
  14. Merge(r2, r1, s, m, t);             //将两个已排序的子序列归并
  15. }
  16. }

总结

各种排序方法的比较(未完待续):

时间: 2024-10-05 13:36:07

排序算法(简)的相关文章

TimSort排序算法及一个问题分析

摘要 排序算法简析 代码入口 排序算法 获取两个有序数组A和B 找到待归并区间 准备操作 归并操作 TimSort的优化归并操作 问题解析 问题解析 问题原因 解决方案 参考 摘要 简单介绍了传统归并排序算法,以及Java API提供的TimSort优化后的归并排序算法. 并且分析了代码中出现的一个问题原因与解决方案. 敬请忽略文中的灵魂画风. 排序算法简析 代码入口 Collections.sort.List.sort.Arrays.sort方法是逐级调用的关系,最终的底层是Arrays.so

常用排序算法时间复杂度和空间复杂度简析

1. preface /**** *    This article will try to explain something about: *        --Bubble sort. *        --Quick sort. *        --Merge sort. *        --Heap sort. *    To read this, some prerequisites is necessary: *        --a survive skill in C pr

8大排序算法-我熟知二(希尔、选择)

3.希尔排序(递减增量排序算法)不稳定的-- - - 直接插入排序的改进  .复杂度介于O(nlog^2n)~ O(n),空间是O(n) 基于插入排序的两点性质: 1.对于几乎已排好序的数组效率高,可达到线性 2.但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位 方法:先将整个待排元素序列分割成若干个子序列(由相隔某个"增量"的元素组成的)分别进行直接插入排序,可以让一个元素可以一次性地朝最终位置前进一大步.然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够

转:排序算法总结

排序算法总结 本博文摘自伍迷老师的<大话数据结构>,想学数据结构的,大大的推荐此书... 事实上,目前还没有十全十美的排序算法,有优点就会有缺点,即使是快速排序法,也只是在整体性能上优越,它也存在排序不稳定.需要大量辅助空间.对少量数据排序无优势等不足.因此我们就来从多个角度来剖析一下提到的各种排序的长与短. 我们将7种算法的各种指标进行对比,如表9‐10‐1所示. 表9‐10‐1 排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性冒泡排序 O(n2)   O(n)           

必须知道的八大种排序算法【java实现】

一.冒泡排序 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端. 冒泡排序的示例: 冒泡排序的算法实现如下:[排序后,数组从小到大排列] /* * 冒泡排序 * 比较相邻的元素.如果第一个比第二个大,就交换他们两个. * 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后

十种常见的排序算法,面试算法必考

1.冒泡排序 已知一组无序数据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]以相同方

Java实现常见的排序算法

排序的基本概念与分类 排序是我们生活中经常会面对的问题.同学们做操时会按照从矮到高排列;老师查看上课出勤情况时,会按学生学号顺序点名;高考录取时,会按成绩总分降序依次录取等.那排序的严格定义是什么呢? 假设含有 n 个记录的序列为{r1,r2,......,rn}, 其相应的关键字分别为{k1,k2,......,kn} ,需确定 1, 2--, n 的一种排列 p1,p2,.......,使其相应的关键字满足 kp1<=kp2<=-- <=kpn (非递减或非递增) 关系,即使得序列成

数据结构复习之排序算法的总结回顾

根据排序过程中借助的主要操作,我们将内排序分为四类: 插入排序类 直接插入排序 希尔排序 选择排序类 简单选择排序 堆排序 交换排序类 冒泡排序 快速排序 归并排序类 归并排序 从算法的简单性来看,我们将7种算法分为两类: 简单算法:冒泡排序.简单选择排序.直接插入排序 改进算法:希尔排序.堆排序.归并排序.快速排序 对7中算法的各种指标进行对比: 平均情况看:显然最后三种算法要胜过希尔算法,并远远超过前3种简单算法. 最好情况看:反而冒泡和直接插入排序要更胜一筹,也就是说,如果待排序序列总是基

经典排序算法 - 选择排序Selection sort

经典排序算法 - 选择排序Selection sort 顾名思意,就是直接从待排序数组里选择一个最小(或最大)的数字,每次都拿一个最小数字出来, 顺序放入新数组,直到全部拿完 再简单点,对着一群数组说,你们谁最小出列,站到最后边 然后继续对剩余的无序数组说,你们谁最小出列,站到最后边 再继续刚才的操作,一直到最后一个,继续站到最后边,现在数组有序了,从小到大 举例 先说看每步的状态变化,后边介绍细节,现有无序数组[6 2 4 1 5 9] 第一趟找到最小数1,放到最前边(与首位数字交换) 交换前

Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等

本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排序.快速排序(重点).堆排序.归并排序等等.看下图: 给定数组:int data[] = {9,2,7,19,100,97,63,208,55,78} 一.直接插入排序(内部排序.O(n2).稳定) 原理:从待排序的数中选出一个来,插入到前面的合适位置. [java] view plain copy