数据排序汇总二(希尔排序、快速排序、归并排序)

简介: 数据排序汇总二(希尔排序、快速排序、归并排序)

希尔排序

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2...1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。

1. #include <iostream>
2. using namespace std;
3. int arr[]={9,1,2,5,7,4,8,6,3,5};
4. int length=10;
5. void swap(int &a,int &b){a+=b;b=a-b;a-=b;}
6. void prin_()
7. {
8.  for(int i=0;i<length;i++) cout<<arr[i]<<" ";
9.  cout<<endl;
10. }
11. int main()
12. {
13.   prin_();
14.   for(int gap=length/2;gap>0;gap/=2){
15.     for(int i=gap;i<length;i++){
16.       int j=i;
17.       while(j-gap>=0 && arr[j]<arr[j-gap]){
18.         swap(arr[j],arr[j-gap]);
19.         j-=gap;
20.       }
21.     }
22.     prin_();
23.   }
24.   return 0;
25. }

五.快速排序        

      快速排序是对冒泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。    

假设待排序的序列为{a[L],a[L+1],a[L+2],……,a[R]},首先任意选取一个记录(通常可选中间一个记作为枢轴或支点),然后重新排列其余记录,将所有关键字小于它的记录都放在左子序列中,所有关键字大于它的记录都放在右子序列中。由此可以将该“支点”记录所在的位置mid作分界线,将序列分割成两个子序列和。这个过程称作一趟快速排序(或一次划分)。        

      一趟快速排序的具体做法是:附设两个指针i和j,它们的初值分别为L和R,设枢轴记录取mid,则首先从j所指位置起向前搜索找到第一个关键字小于的mid的记录,然后从i所指位置起向后搜索,找到第一个关键字大于mid的记录,将它们互相交换,重复这两步直至i>j为止。

      快速排序的时间的复杂性是O(nlog2n),速度快,但它是不稳定的排序方法。就平均时间而言,快速排序是目前被认为是最好的一种内部排序方法

      由以上讨论可知,从时间上看,快速排序的平均性能优于前面讨论过的各种排序方法,但快速排序需一个栈空间来实现递归。若每一趟排序都将记录序列均匀地分割成长度相接近的两个子序列,则栈的最大深度为log(n+1)。

1. //快速排序<1> :10 20 40 32 67 40 20 89 300 400 15 
2. #include<iostream>
3. using namespace std;
4. void qsort(int,int);
5. int a[101];
6. int main()
7. {
8.  int n,i;
9.  cin>>n;
10.   for(i=1;i<=n;i++) cin>>a[i];
11.   qsort(1,n);
12.   for(i=1;i<=n;i++) cout<<a[i]<<" ";
13.   cout<<endl;
14.   return 0;
15.  }
16. void qsort(int l,int r){
17.   int i,j,mid,p;
18.   i=l;j=r;
19.   mid=a[(l+r)/2];
20.   do{
21.     while(a[i]<mid) i++;
22.     while(a[j]>mid) j--;
23.     if(i<=j){
24.       p=a[i];a[i]=a[j];a[j]=p;
25.       i++;j--;
26.     }
27.   }while(i<=j);
28.   if(l<j) qsort(l,j);
29.   if(i<r) qsort(i,r); 
30. }
1. //快速排序<1> :10 20 40 32 67 40 20 89 300 400 15 
2. #include <stdio.h>
3. #include<iostream>
4. using namespace std;
5. int a[101];
6. void sort(int *a,int left,int right){
7.  if(left>=right) return;
8.  int i,j,key;
9.  i=left;j=right;key=a[left];
10.   while(i<j){
11.     while(i<j && key<=a[j]) j--;
12.     a[i]=a[j];
13.     while(i<j && key>=a[i]) i++;
14.     a[j]=a[i];
15.   }
16.   a[i]=key;
17.   sort(a,left,i-1);
18.   sort(a,i+1,right);
19. }
20. int main()
21. {
22. int a[]={57,68,59,52,72,28,96,33,24};
23. sort(a,0,sizeof(a)/sizeof(a[0])-1);/*这里第三个参数要减1否则内存越界*/
24. for(int i=0;i<sizeof(a)/sizeof(a[0]);i++){
25.         cout<<a[i]<<"";
26.     } 
27. return 0;
28. }

六.归并排序

     归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

     例如有8个数据需要排序:10 4 6 3 8 2 5 7

     归并排序主要分两大步:分解、合并。

     合并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

1. #include <stdio.h>
2. #include<iostream>
3. using namespace std;
4. int a[]={57,68,59,52,72,28,96,33,24};
5. int r[101];
6. void msort(int s,int t){
7.  if(s==t) return;//只有一个数字无需排序 
8.  int mid=(s+t)/2;
9.  msort(s,mid);//分解左序列 
10.   msort(mid+1,t);//分解右序列 
11.   int i=s,j=mid+1,k=s;
12.   while(i<=mid && j<=t){
13.     if(a[i]<=a[j]){
14.       r[k]=a[i];k++;i++;
15.     }
16.     else{
17.       r[k]=a[j];k++;j++;
18.     }
19.   }
20.   while(i<=mid){//复制左序列剩余 
21.     r[k]=a[i];k++;i++;
22.   }
23.   while(j<=t){//复制右序列剩余 
24.     r[k]=a[j];k++;j++;
25.   }
26.   for(int i=s;i<=t;i++) a[i]=r[i];//更新数组 a 
27. } 
28. int main()
29. {
30. msort(0,sizeof(a)/sizeof(a[0]-1));/*这里第三个参数要减1否则内存越界*/
31. for(int i=0;i<sizeof(a)/sizeof(a[0]);i++)   cout<<a[i]<<" ";
32. return 0;
33. }

1311:【例2.5】求逆序对

【题目描述】

给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目。

【输入】

第一个数为n,表示序列长度,接下来的n个数,第n+1个数表示序列中的第n个数。

【输出】

所有逆序对总数。

【输入样例】

4 3 2 3 2

【输出样例】

3

【提示】

N≤10^5,Ai≤10^5。

【来源】

1. #include <iostream>
2. using namespace std;
3. long long a[100001];
4. long long r[100001];
5. long long ans=0;
6. void msort(long long s,long long t){
7.  if(s==t) return;
8.  long long mid=(s+t)/2;
9.  msort(s,mid);
10.   msort(mid+1,t);
11.   long long i=s,j=mid+1,k=s;
12.   while(i<=mid && j<=t){
13.     if(a[i]<=a[j]){
14.       r[k]=a[i];k++;i++; 
15.     }
16.     else{
17.       r[k]=a[j];k++;j++;
18.       ans+=mid-i+1;
19.     }
20.   }
21.   while(i<=mid) {
22.     r[k]=a[i];k++;i++;
23.   }
24.   while(j<=t){
25.     r[k]=a[j];k++;j++;
26.   }
27.   for(i=s;i<=t;i++) a[i]=r[i];
28. }
29. int main(int argc,char* argv[])
30. {
31.   long long n,i;
32.   cin>>n;
33.   for(i=0;i<n;i++) cin>>a[i];
34.   msort(0,n-1);
35.   cout<<ans<<endl;
36.   return 0;
37. }

其中ans+=mid-i+1这句代码统计新增逆序对的数量,ans作为全局变量,用于统计逆序对的数量,此时ans要增加左边区间剩余元素的个数。当归并排序结束后,逆序对问题也得到解决,ans即为逆序对的数量。

各种排序算法的比较

1.稳定性比较

     插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的。

     选择排序、希尔排序、快速排序、堆排序是不稳定的。

2.时间复杂性比较

     插入排序、冒泡排序、选择排序的时间复杂性为O(n^2);

      快速排序、堆排序、归并排序的时间复杂性为O(nlogn);

      桶排序的时间复杂性为O(n);

      若从最好情况考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n),其它算法的最好情况同平均情况相同;若从最坏情况考虑,则快速排序的时间复杂度为O(n^2),直接插入排序和冒泡排序虽然平均情况相同,但系数大约增加一倍,所以运行速度将降低一半,最坏情况对直接选择排序、堆排序和归并排序影响不大。

      由此可知,在最好情况下,直接插入排序和冒泡排序最快;在平均情况下,快速排序最快;在最坏情况下,堆排序和归并排序最快。

3.辅助空间的比较

     桶排序、二路归并排序的辅助空间为O(n),快速排序的辅助空间为O(logn),最坏情况为O(n),其它排序的辅助空间为O(1);

4.其它比较

      插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。反而在这种情况下,快速排序反而慢了。

      当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。

      若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。

      当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。

      当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序    快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短; 堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。

相关文章
|
11月前
|
索引
写一个希尔排序,归并排序,快速排序
写一个希尔排序,归并排序,快速排序
|
3月前
|
存储 搜索推荐 算法
|
5月前
|
存储 搜索推荐 算法
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)
|
5月前
|
机器学习/深度学习 搜索推荐 算法
【排序算法】插入排序与选择排序详解
【排序算法】插入排序与选择排序详解
|
10月前
|
算法
【算法】排序——选择排序和交换排序(快速排序)
上篇文章讲述了插入排序及插入排序的优化希尔排序,今天我们继续给大家带来排序中的选择排序和交换排序,选择排序包括直接选择排序、 其中还包括堆排序,因为之前讲过堆排序,这篇文章就不多讲解,点击直达堆排序。交换排序包括冒泡排序、快速排序。让我们开始今天的选择排序之旅吧!!!
|
11月前
|
搜索推荐 算法
排序算法 - 直接插入排序
排序算法 - 直接插入排序
42 0
|
搜索推荐 算法
排序算法:冒泡排序,插入排序,选择排序,归并排序,快速排序
排序算法:冒泡排序,插入排序,选择排序,归并排序,快速排序
|
搜索推荐 算法 Shell
八大排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序、基数排序(上)1
八大排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序、基数排序(上)1
163 0
|
搜索推荐 算法
八大排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序、基数排序(上)2
八大排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序、基数排序(上)2
|
搜索推荐
排序算法——直接插入排序
排序算法——直接插入排序