常用排序(二)

来源:互联网 发布:mac os x10.8下载地址 编辑:程序博客网 时间:2024/06/13 12:48

选择排序

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

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

1.直接选择排序

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

void SelectSort(dataList<int>& L,int left,int right){for(int i =left;i<right;i++){int k = i;//代表最小码for(int j = i+1;j<=right;j++){if(L[k]>L[j]) k=j;}if(k!=i) Swap(L[i],L[k]);//交换}}
最坏情况下总的移动次数是3(N-1)次,总的比较次数是N(N-1)/2次

2.锦标赛排序

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

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

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

void TournmentSort(int a[],int left,int right){size = right-left+1;WinnerTree<int> wt(size);int  data[size+1];for(int i =1;i<=size;i++){a[i+left-1] = data[i];}wt.Initial(data,size,Winner);//初始化胜者树for(i=1;i<=size;i++){a[i+left-1] = wt.Winner();//wt.Update();//修改胜者wt.rePlay(t[1],Winner);//重构,选出新的胜者if(wt.Winner()==maxValue) break;}}
3.堆排序

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

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

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

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

void siftDown(int start,int m){//从start开始到m为止int i= start;int j = 2*i+1;//父节点是I,左子女是2*i+1int temp  = heap[i];while(j<=m){if(j<=m && heap[j]>heap[j+1]) j++;//找到左右子女最小的if(temp<=heap[j]) break;else{heap[i] = heap[j];// i 节点的值现在是小的,然后 j 的值现在还是原来的i=j;j=2*j+1;}//原来的J结点变成父节点,继续找最小值,下移了一层}heap[i] = temp;//把最开始的I值放在最后的父节点上 }void siftUp(int start){//从start向上到节点0int j=start,i = (j-1)/2,temp = heap[j];while(j>0){if(heap[i]<=temp) break;else{heap[j] = heap[i];j = i;i = (i-1)/2;}}heap[j] = temp;}

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

void HeapSort(maxHeap<int>& H){//对H.heap[0]到H.heap[currentSize-1]排序for(int i = (currentSize-2)/2;i>=0;i--){//表转化为堆siftDown(i,currentSize-1);}for(i = currentSize-1;i>=0;i--){//表排序Swap(0,i);siftDown(0,i-1);//交换重建最大堆}}

主要解释一下第一个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的位置上,这样一次循环下去直到所有的都排好序


归并排序

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

void merge(dataList<int>& L1,dataList<int>& L2,int left,int mid,int right){int s1 = left,s2 = right,t = left,k;//s1和s2是检测指针,t是存放指针for(k = left;k<=mid;k++){L2[k] = L1[k];//将L1的正向全部复制到L2}for(k = mid+1;k<=right;k++){L2[right-(k-mid-1)] = L1[k];//反向复制}while(t<=right){//归并if(L2[s1]<=L2[s2]) L1[t++] = L2[s1++];<span style="white-space:pre"></span>//S1的数值小取出来放在L[t]中else L1[t++] = L2[s2--];<span style="white-space:pre"></span>//s2小取出来放入T中}}


基数排序

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

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

void RadixSort(dataList<int>& L,int left,int right,int d){//d表达的是第几位数,d=1是最低位int radix = 10;int i,j,count[radix],p1,p2;//count[k]处理第 i 位时,第 i 位值等于k的个数Element<int> array[right-left+1];//存放按桶分配结果if(d<=0) return;if(right-left+1<=M) InsertSort(L,left,right);//小的序列插排for(j=0;j<radix;j++) count[j]=0;//初始化for(i=left;i<=right;i++) //统计各桶元素count[getDigit(L[i],d)]++;for(j=1;j<radix;j++) //各桶元素存放位置count[j]=count[j]+count[j-1];for(i=left;i<=right;i++){//分配桶中j = getDigit(L[i],d);//取L[i]的第 d 位值array[count[j]-1] = L[i];count[j]--;}for(i=left,j=0;i<=right;i++,j++) L[i]=array[j];//写入原数组for(j=0;j<radix;j++){递归对d-1位处理p1 = count[j];//开始端p2 = count[j+1]-1;//尾端RadixSort(L,p1,p2,d-1);}}


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

1 0
原创粉丝点击