各种排序算法的实现以及思考.docx
- 文档编号:4454634
- 上传时间:2022-12-01
- 格式:DOCX
- 页数:11
- 大小:20.15KB
各种排序算法的实现以及思考.docx
《各种排序算法的实现以及思考.docx》由会员分享,可在线阅读,更多相关《各种排序算法的实现以及思考.docx(11页珍藏版)》请在冰豆网上搜索。
各种排序算法的实现以及思考
冒泡排序(Bubblesort)
原理
冒泡排序是一种简单的排序算法。
它重复访问要排序的数列,每一次比较两个元素,如果前一个大于后一个元素,则交换数据。
那么在一次全部访问过程中,最大的元素就’浮’动到数列的最后。
然后重复进行方法,知道再没有数据交换,也就是数列已经排序完成。
步骤
1.比较相邻的元素。
如果第一个比第二个大,就交换他们两个。
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。
这步做完后,最后的元素会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
实现
voidBubbleSort(intarr[],intn){
inti,j;
i=n;
boolflag=true;
while(flag){
flag=false;
for(j=1;j
if(arr[j-1]>arr[j]){
swap(arr[j-1],arr[j]);
flag=true;
}
}
i--;
}
}
在上面的代码中加入了一个flag来标记是否有数据交换,如果在排序过程中没发生数据交换,则表示已经排列好了,后面就不需要在遍历了。
冒泡排序算是最简单的排序算法了,但毕竟是一种效率低下的排序算法,再数据量不大的情况下可以使用。
插入排序(Insertionsort)
原理
插入排序是一种直观的排序算法。
它通过构建有序数列,对未排序的数据,在已排序的数列中从后往前扫描,找到相应的位置插入。
在排序的实现上,从后向前的扫描过程中,需要反复把已排序的元素逐步向后移动,为要插入的元素留空间。
步骤
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2~5
实现
voidInsertSort(intarr[],intn){
inti,j;
for(i=1;i if(arr[i] inttemp=arr[i]; for(j=i-1;j>=0&&arr[j]>temp;j--){ arr[j+1]=arr[j]; } arr[j+1]=temp; } } } 插入排序不适合对于数据了比较大的排序应用。 但是,如果排序数据了很小,比如一千左右,那插入排序是一个不错的选择。 选择排序(Selectionsort) 原理 选择排序与插入排序很像,插入排序是将一个数插入已经排好的序列,而选择排序是在未排序的序列中找到最小(大)元素,放在排序序列的起始位置。 然后,再从剩下的未排序的序列中再次寻找最小(大)元素,放在已排序序列的末尾,反复重复,知道所有元素排序完成。 步骤 1.初始时,数组全为无序区为a[0..n-1]。 令i=0 2.在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交换。 交换之后a[0…i]就形成了一个有序区。 3.i++并重复第二步直到i==n-1。 排序完成。 实现 voidSelectSort(intarr[],intn){ inti,j,minIndex; for(i=0;i minIndex=i; for(j=i+1;j if(arr[minIndex]>arr[j]){ minIndex=j; } } if(minIndex! =i){ Swap(arr[i],arr[minIndex]); } } } 选择排序中,如果某个元素位于正确的最终位置,则不会被移动。 选择排序每次交换一对元素,它们当中至少有一个将被移动到最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。 在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。 希尔排序(Shellsort) 原理 希尔排序,也称作递减增量排序算法,是插入排序的一种更高效版本。 它以一定的增量将序列分为若干个组,然后每一组进行插入排序,然后减少增量反复分组进行插入排序。 直到增量为1时,就为普通的插入排序,但是此时序列已经基本排列完成,只需要进行少量的移动即可完成。 步骤 将数组列在一个表中并对列排序(用插入排序)。 重复这过程,不过每次用更长的列来进行。 最后整个表就只有一列了。 将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i+=step_size而不是i++) 例如,假设有这样一组数[13149433822559946523452773253910],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样: 1314943382 2559946523 4527732539 10 然后我们对每列进行排序: 1014732523 1327943339 2559946582 45 将上述四行数字,依序接在一起时我们得到: [10147325231327943339255994658245].这时10已经移至正确位置了,然后再以3为步长进行排序: 101473 252313 279433 392559 946582 45 排序之后变为: 101413 252333 272559 396573 459482 94 最后以1步长进行排序(此时就是简单的插入排序了)。 实现 voidShellSort(intarr[],intn){ inti,j,gep; for(gep=n/2;gep>0;gep/=2){ for(i=gep;i inttemp=arr[i]; for(j=i-gep;j>=0&&arr[j]>temp;j-=gep){ arr[j+gep]=arr[j]; } arr[j+gep]=temp; } } } 希尔排序步长的选择十分灵活,只要最终步场为1的任何步长序列都可以工作。 以上的代码从n/2开始,每一次减半,最终步长为1,算法变为插入排序,就保证了数据一定会被排序。 归并排序(Mergesort) 原理 归并排序是创建在归并操作上的一种有效的排序算法,基本思想是分治法。 它时将两个已经排序的序列合并成一个序列的操作。 步骤 归并操作 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2.设定两个指针,最初位置分别为两个已经排序序列的起始位置 3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 4.重复步骤3直到某一指针到达序列尾 5.将另一序列剩下的所有元素直接复制到合并序列尾 归并排序 1.将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素 2.将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素 3.重复步骤2,直到所有元素排序完毕 实现 //归并操作 void_merge_array(intarr[],intfirst,intmid,intlast,inttemp[]){ inti=first,j=mid+1; intm=mid,n=last; intk=0; while(i<=m&&j<=n){ if(arr[i] temp[k++]=arr[i++]; }else{ temp[k++]=arr[j++]; } } while(i<=m){ temp[k++]=arr[i++]; } while(j<=n){ temp[k++]=arr[j++]; } for(i=0;i arr[first+i]=temp[i]; } } //归并排序 void_merge_sorte(intarr[],intfirst,intlast,inttemp[]){ if(first intmid=(first+last)/2; _merge_sorte(arr,first,mid,temp); _merge_sorte(arr,mid+1,last,temp); _merge_array(arr,first,mid,last,temp); } } voidMergeSort(intarr[],intn){ int*p=(int*)malloc(sizeof(int)*n); if(p! =nullptr){ _merge_sorte(arr,0,n-1,p); } free(p); } 归并排序是分治法的典型应用,当一个数组的左右两边都有序然后归并整个数组就有序了,利用递归逐层分治,然后合并上来就排好序了。 快速排序(Quicksort) 快速排序算是我最喜欢的一个排序了,记得第一次接触的时候惊讶排序还可以这么排…题外话了 原理 快速排序也适用分治法,已一个数作为基准,左边全为笔它小的数,右边全为比它大的数。 然后左右递归重复即可。 步骤 1.从数列中挑出一个元素,称为”基准”(pivot) 2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。 在这个分区结束之后,该基准就处于数列的中间位置。 这个称为分区(partition)操作。 3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。 实现 下面列两种实现一种MoreWindows大神已挖坑填数总结的,一种是算法导论上的实现。 MoreWindows: voidQuickSort(intarr[],intl,intr){ if(l inti=l,j=r; intk=arr[i]; while(i while(i j--; } if(i arr[i++]=arr[j]; } while(i i++; } if(i arr[j--]=arr[i]; } } arr[i]=k; QuickSort(arr,l,i-1); QuickSort(arr,i+1,r); } } 算法导论: intquick_sort(intarr[],intleft,intright){ intindex=left; intk=arr[index]; Swap(arr[index],arr[right]); for(inti=left;i if(arr[i] Swap(arr[index++],arr[i]); } } Swap(arr[index],arr[right]); returnindex; } voidQuickSort(intarr[],intleft,intright){ if(left intindex=quick_sort(arr,left,right); QuickSort1(arr,left,index-1); QuickSort1(arr,index+1,right); } } 快速排序递归下去的最低情形,是数列的大小0或1,也就是永远排好了序。 在每一次递归中至少有一个数会摆到它最后的位置。 堆排序(Heapsort) 堆排序对于我而言算是比较难搞懂的排序了 原理 堆排序是利用堆这种数据结构所设计的一种排序算法。 最大(小)堆的根节点为整个序列的最大(小)数。 堆每取出一个根节点,堆被破坏,然后堆就会调整使之符合最大(小)堆。 那么依次取出堆的根节点,依次放入新的数列中,直到堆元素为0位置,那么新的数列已经排好序了。 步骤 堆节点的访问: 通常堆是通过一维数组来实现的。 在起始数组为0的情形中: ∙父节点i的左子节点在位置(2*i+1); ∙父节点i的右子节点在位置(2*i+2); ∙子节点i的父节点在位置floor((i-1)/2); 堆的操作: 在堆的数据结构中,堆中的最大值总是位于根节点。 堆中定义以下几种操作: ∙最大堆调整(Max_Heapify): 将堆的末端子节点作调整,使得子节点永远小于父节点 ∙创建最大堆(Build_Max_Heap): 将堆所有数据重新排序 ∙堆排序(HeapSort): 移除位在第一个数据的根节点,并做最大堆调整的递归运算 原地堆排序: 1.创建一个堆H[0..n-1] 2.把堆首(最大值)和堆尾互换 3.把堆的尺寸缩小1,并调用MaxPrcdown(),目的是把新的数组顶端数据调整到相应位置 4.重复步骤2,直到堆的尺寸为1 实现 voidMaxPrcdown(intarr[],inti,intn){ inttemp=arr[i]; intchlid=2*i+1; while(chlid if(chlid+1 chlid++; } if(arr[chlid]<=temp){ break; } arr[i]=arr[chlid]; i=chlid; chlid=2*i+1; } arr[i]=temp; } voidHeapSort(intarr[],intn){ for(inti=n/2-1;i>=0;i--){ MaxPrcdown(arr,i,n); } for(inti=n-1;i>=1;i--){ Swap(arr[0],arr[i]); MaxPrcdown(arr,0,i); } } 总结 排序算法 最差时间复杂度 平均时间复杂度 空间复杂度 稳定性 冒泡排序 O(n^2) O(n^2) O (1) 稳定 插入排序 O(n^2) O(n^2) O (1) 稳定 选择排序 O(n^2) O(n^2) O (1) 稳定 归并排序 O(nlogn) O(nlogn) O(n) 不一定 希尔排序 O O O (1) 不稳定 快速排序 O(n^2) O(nlogn) O(logn)~O(n) 不稳定 堆排序 O(n*log2n) O(n*log2n) O (1)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 各种 排序 算法 实现 以及 思考