常用排序(二)

选择排序

选择排序说的是:每一趟在后面没有排序n-i个元素中,选择一个最小的放在第 i 个位置上,接下来选择i+1位置上的元素,一共需要执行n-2趟操作,他的时间复杂度是O(N*N)

选择排序有三种实现方式;直接选择排序、锦标赛排序、堆排序

1.直接选择排序

选择最小的元素,如果不是第一个就和第一个对调位置,这样他就是第一个了,重复直到排序结束

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void SelectSort(dataList<int>& L,int left,int right){  
  2.     for(int i =left;i<right;i++){  
  3.         int k = i;  //代表最小码  
  4.         for(int j = i+1;j<=right;j++){  
  5.             if(L[k]>L[j]) k=j;  
  6.         }  
  7.         if(k!=i) Swap(L[i],L[k]);   //交换  
  8.     }  
  9. }  
最坏情况下总的移动次数是3(N-1)次,总的比较次数是N(N-1)/2次

2.锦标赛排序

锦标赛排序的思想是:取N个元素的,两两进行比较,较小的保留下来,会得到N/2个元素,接下俩对着N/2个元素继续两两比较,直到选出最小的为止

胜者树是最底层的叶子节点是元素原本的样子,然后每两个叶节点进行比较,比较的结果变成他们的父节点,父节点之间在进行比较,最终根节点就是选择出来最小的结点,最底层的叶节点叫做胜者树的外部节点,非叶节点叫做内部节点

用胜者树对N个元素进行排序时,时间代价是O(nlog2 n)

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void TournmentSort(int a[],int left,int right){  
  2.     size = right-left+1;  
  3.     WinnerTree<int> wt(size);  
  4.     int  data[size+1];  
  5.     for(int i =1;i<=size;i++){  
  6.         a[i+left-1] = data[i];  
  7.     }  
  8.     wt.Initial(data,size,Winner);   //初始化胜者树  
  9.     for(i=1;i<=size;i++){  
  10.         a[i+left-1] = wt.Winner();  //  
  11.         wt.Update();    //修改胜者  
  12.         wt.rePlay(t[1],Winner); //重构,选出新的胜者  
  13.         if(wt.Winner()==maxValue) break;  
  14.     }  
  15. }  
3.堆排序

先是根据元素数据利用堆的调整算法siftDown实现初始堆,接下来元素交换调整进行排序

先来说说最小堆的调整,一般而言有两种调整策略,从上到下的下滑调整和从下到上的上滑调整

下滑调整是:从父节点出发,看他的左右两个儿子中有没有比他大的,如果有的话就小的上移

上滑调整:和下滑正好相反,他是从儿子出发,看看父节点是否比他小,如果父节点小的话就不调整,若父节点大的话就调整、

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void siftDown(int start,int m){ //从start开始到m为止  
  2.     int i= start;int j = 2*i+1; //父节点是I,左子女是2*i+1  
  3.     int temp  = heap[i];  
  4.     while(j<=m){  
  5.         if(j<=m && heap[j]>heap[j+1]) j++;    //找到左右子女最小的  
  6.         if(temp<=heap[j]) break;  
  7.         else{  
  8.             heap[i] = heap[j];// i 节点的值现在是小的,然后 j 的值现在还是原来的  
  9.             i=j;j=2*j+1;  
  10.             }   //原来的J结点变成父节点,继续找最小值,下移了一层  
  11.     }  
  12.     heap[i] = temp; //把最开始的I值放在最后的父节点上   
  13. }  
  14. void siftUp(int start){ //从start向上到节点0  
  15.     int j=start,i = (j-1)/2,temp = heap[j];  
  16.     while(j>0){  
  17.         if(heap[i]<=temp) break;  
  18.         else{  
  19.             heap[j] = heap[i];  
  20.             j = i;  
  21.             i = (i-1)/2;  
  22.         }  
  23.     }  
  24.     heap[j] = temp;  
  25.       
  26. }  

在到堆排序中用的是最大堆,上面的代码是最小堆,当把最大的也就是堆顶的元素得到之后放在最后一个n-1的位置上,再找下一个元素

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void HeapSort(maxHeap<int>& H){   //对H.heap[0]到H.heap[currentSize-1]排序  
  2.     for(int i = (currentSize-2)/2;i>=0;i--){ //表转化为堆  
  3.         siftDown(i,currentSize-1);  
  4.     }  
  5.     for(i = currentSize-1;i>=0;i--){ //表排序  
  6.         Swap(0,i);siftDown(0,i-1);  //交换重建最大堆  
  7.     }  
  8. }  

主要解释一下第一个for循环,i=curr-2/2,这指的是最后两个元素所对应的父节点的位置,因为 j = 2*i+1是左节点,j = 2*i+2是右节点,这里先从最右边父节点就开始建立堆,从(curr-2)/2开始到curr-1,建立堆,只有一个父节点,然后开始i--,建立下一个父节点,直到所有的节点都被建立完全,然后第二个for循环是找到最大的数然后放在后面,当前最大的数是堆顶元素,是heap(0)的元素,把他和curr-1交换,这样他就在最后一个,接下来重新建立前面n-2和元素的最大堆,取出堆顶元素在放在curr-2的位置上,这样一次循环下去直到所有的都排好序


归并排序

归并排序也采用的是分治法,将大的序列分解为两个小的序列,序列长度相等,然后再将两个合并起来,其中的难点是合并

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void merge(dataList<int>& L1,dataList<int>& L2,int left,int mid,int right){  
  2.     int s1 = left,s2 = right,t = left,k;    //s1和s2是检测指针,t是存放指针  
  3.     for(k = left;k<=mid;k++){  
  4.         L2[k] = L1[k];  //将L1的正向全部复制到L2  
  5.     }  
  6.     for(k = mid+1;k<=right;k++){  
  7.         L2[right-(k-mid-1)] = L1[k];    //反向复制  
  8.     }  
  9.     while(t<=right){ //归并  
  10.         if(L2[s1]<=L2[s2]) L1[t++] = L2[s1++];<span style="white-space:pre">   </span>//S1的数值小取出来放在L[t]中  
  11.         else L1[t++] = L2[s2--];<span style="white-space:pre">    </span>//s2小取出来放入T中  
  12.     }  
  13. }  


基数排序

每个元素的排序码有多个,排序时要用多排序码排序,利用多排序码排序实现对单个排序码的排序叫做基数排序,多排序码排序包含有最高位优先MSD和最低位优先LSD两种。

 在MSD中,把单排序码看作是一个子排序码组,由好几个组成,例如三位数可以看成是有百位、十位、个位三个排序码组在一起形成的,这样排序时可以按照一个一个的排序码进行排序,比如先排百位再排十位个位

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void RadixSort(dataList<int>& L,int left,int right,int d){  
  2.         //d表达的是第几位数,d=1是最低位  
  3.         int radix = 10;  
  4.         int i,j,count[radix],p1,p2;//count[k]处理第 i 位时,第 i 位值等于k的个数  
  5.         Element<int> array[right-left+1];//存放按桶分配结果  
  6.         if(d<=0) return;  
  7.         if(right-left+1<=M) InsertSort(L,left,right);//小的序列插排  
  8.         for(j=0;j<radix;j++) count[j]=0;//初始化  
  9.         for(i=left;i<=right;i++) //统计各桶元素  
  10.             count[getDigit(L[i],d)]++;  
  11.         for(j=1;j<radix;j++)     //各桶元素存放位置  
  12.             count[j]=count[j]+count[j-1];  
  13.         for(i=left;i<=right;i++){    //分配桶中  
  14.             j = getDigit(L[i],d);   //取L[i]的第 d 位值  
  15.             array[count[j]-1] = L[i];  
  16.             count[j]--;  
  17.         }  
  18.         for(i=left,j=0;i<=right;i++,j++)   
  19.             L[i]=array[j];  //写入原数组  
  20.         for(j=0;j<radix;j++){递归对d-1位处理  
  21.             p1 = count[j];  //开始端  
  22.             p2 = count[j+1]-1;  //尾端  
  23.             RadixSort(L,p1,p2,d-1);  
  24.         }  
  25. }  


另外附上一个学习网站,当你不了解他是如何工作的时候,可以看看他的动画演示https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值