基础排序算法总结及实现
2017-03-31 19:13
225 查看
最近复习了一下基础排序算法,这里简单总结一下算法实现原理及实现代码。由于代码是根据原理写的基础算法,并没有过多的考虑性能优化。我们这里讲的所有算法都是升序排序。
算法:
1、比较相邻的元素。如果第一个比第二个大,就交换他们两个,这样最后的元素就是最大的数。
2、重复步骤一,每次选出一个最大的元素,无序数组少一个元素,有序数组多一个元素。
3、每次选择一个相对最大的数放在右边,进行length-1次比较即可。
代码实现
改进代码实现
算法:
1、 记录无序区最左边位置为i,初始化无序区中最小值索引为index
2、将位置i与其右边无序区每一个元素依次对比,比它小则更新索引index。
3、一轮比较完成后,如果i不为index,则交换两个元素位置。
4、循环进行下一轮比较,每一轮选择一个最小值,进行length-1轮即可。
代码实现
算法:
1、取出无序区最左边的数,将其与有序区的数值比较,如果小于则交换,如果大于则此次插入结束。
2、无序区从下标为1开始取值。
3、无序区下标超出数组长度,则排序完成。
代码实现
算法:
1、取出无序区最左边位置为i的数,暂存在temp变量中。初始化low和high分别为0和i-1
2、计算mid=(low+high)/2,如果mid位置的变量比temp小,则更新low=mid+1,反之更新high=mid-1
3、一直循环比较逐渐缩小low和high的距离(也就是缩小待插入位置的范围),直到low不小于high,那么这个时候,low的值即为待插入位置。
4、将有序区中位于low右边的值往右移,更新low位置的变量为temp。此次插入完成。
5、循环插入所有数,进行length-1次插入即可。
代码实现
算法:
1、初始化待快排位置low为0 ,high为length-1;
2、如果low比high小,则进行快排分割,快排分割将待快排数组范围的第一个数据放在正确位置,并返回其放置的位置key
3、得到别分割的两个数组,范围分别为low到key-1,key+1到high。递归再次对其进行快排。
代码实现
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1}
第二次归并后:{6,100,202,301},{1,8,38}
第三次归并后:{1,6,8,38,100,202,301}
算法:
1、初始化归并增量为1
2、计算数组长度size,计算如果该次增量为len归并成功,会形成的多少个有序集合并赋给mid。计算此次归并中,是否存在单数集合并赋给c。
3、如果执行归并后,有序集合数等于0,则说明已经是有序序列,退出。
4、以增量len将数组划分开来,调用merge函数(具体在步骤7)。执行mid次merge即可。
5、如果c变量为1,即该次归并经过mid次之后,还存在没有排序的数组。那么将为排序的数组与它左边len长度的数组进行归并。
6、递归调用下一次归并函数。
7、merge函数第一个参数为数组,第二个参数为需要归并的第一个序列的开始位置,第三个参数为第二个序列的开始位置,第四个参数为第二个序列的结束位置,两个序列物理位置是相邻的。首先申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列,因为待归并的两个序列都是有序的,因此只需要分别记录两个序列的左边位置,数据小的复制,然后右移索引,直到某个序列复制完成。然后将复制另一序列的剩余数据。最终将空间的数据回写到参数数组中。
代码实现
如:a = 3 1 3 5 9 7 4 4
d = length/2 = 4
那么a[0]和a[4],a[1]和a[5],a[2]和a[6],a[3]和a[7]一组,分别对四组进行快排
第一次排序为:3 1 3 4 9 7 4 5
类似的,第一次d=2,排序为3 1 3 4 4 5 9 7
第三次d=1,排序为1 3 3 4 4 5 7 9
算法:
1、初始化增量为d=length/2
2、进行shell排序,d此时的隐含含义就是该增量下,数组被分割的小序列数目。因此进行需要进行d次快排。
3、剩下的就是在每一次快排中调用之前的快排算法了,这部分可以参考前面的算法。
代码实现
这里需要了解的基础是,堆的特性,如果堆顶以标号0开始的话,其父节点为i,那么左右子节点分别为2i+1,2i+2。
更新堆顶元素的调整过程:一个大顶堆,更新堆顶元素之后,需要与其左右子节点比较,如果小于子节点,则需要交换,然后继续与子节点比较,直到大于等于子节点或者子节点序号大于数组长度为止。
参考文章,堆排序的更详细的原理介绍:http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html
算法:
1、首先编写堆适应函数heapAjust,第一个参数是数组,第二个参数是parent(即更新过后的不稳定节点),第三个参数是length,length为堆大小。
数组左边为大顶堆,右边为有序序列。每次更新堆中节点parent,我们首先将a[parent]赋值给temp,计算左子节点索引为child=2*parent+1,判断左子节点是否存在,如果存在,则继续判断其右子节点是否存在且大于左子节点,如果是,则更新child++,即获取右子节点索引。然后比较temp值于子节点中较大值,如果temp大,则堆不变,如果temp小,则交换,此时更新的值为child节点,继续循环比较child的子节点操作。
2、得到待排序数组后,首先我们将其初始化为大顶堆。从我们刚才编写的堆适应函数来看,它是从父节点来更新堆,因此为了包括所有的元素,我们可以从len/2这个索引开始初始化堆。然后依次递减,直到初始化所有元素。
3、此时数组中是以大顶堆顺序排列的,有序序列在数组右边,左边为堆,我们取出第一个元素,与数组的最后一个元素交换,此时堆中元素少一个,有序序列中元素多一个,我们执行len-1次操作,即可将堆中元素全部取出得到有序序列。
代码实现
基础排序算法的学习就到此为止了。
冒泡排序
原理简介:数组左边无序,右边有序,每次比较两个相邻的元素,然后选出最大的放在有序队列队首。算法:
1、比较相邻的元素。如果第一个比第二个大,就交换他们两个,这样最后的元素就是最大的数。
2、重复步骤一,每次选出一个最大的元素,无序数组少一个元素,有序数组多一个元素。
3、每次选择一个相对最大的数放在右边,进行length-1次比较即可。
代码实现
//冒泡排序 public static void bubbleSort(int[] a){ for(int i = 0; i<a.length-1;i++) for(int j=0;j<a.length-i-1;j++){ if(a[j]>a[j+1]){ int temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } }改进:我们知道,每一轮两两比较,选出最大数则交换,最后最右边的数必定为本轮选出的最大数,那么如果在某轮比较中,如果没有发生交换,即左边的数都比右边的数小,则说明当前数组已经是有序的。
改进代码实现
//冒泡排序改进算法 public static void bubbleSortOpt(int[] a){ boolean exchange; for(int i = 0; i<a.length-1;i++) { exchange = false; for(int j=0;j<a.length-i-1;j++){ if(a[j]>a[j+1]){ int temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; exchange = true; } } if(!exchange){ return; } } }
选择排序
原理简介:选择排序,左边是有序区,右边是无序区,每一轮选择出最小数放在左边有序区,直到无序区长度为1.算法:
1、 记录无序区最左边位置为i,初始化无序区中最小值索引为index
2、将位置i与其右边无序区每一个元素依次对比,比它小则更新索引index。
3、一轮比较完成后,如果i不为index,则交换两个元素位置。
4、循环进行下一轮比较,每一轮选择一个最小值,进行length-1轮即可。
代码实现
//选择排序 public static void selectSort(int[] a){ int index = 0; for(int i = 0; i<a.length-1; i++){ index = i; for(int j =i+1; j<a.length;j++ ){ if(a[index]>a[j]){ index = j; } } if(index !=i){ int temp = a[index]; a[index] = a[i]; a[i] = temp; } } }
插入排序
原理简介:左边是有序区,右边是无序区,将无序区最左边的值依次与有序区数值比较,插入到适当位置。直到无序区长度为0。算法:
1、取出无序区最左边的数,将其与有序区的数值比较,如果小于则交换,如果大于则此次插入结束。
2、无序区从下标为1开始取值。
3、无序区下标超出数组长度,则排序完成。
代码实现
//插入排序 public static void insertSort(int[] a){ for(int i=1;i<a.length;i++){ for(int j =i;j>0;j--){ if(a[j]<a[j-1]){ int temp = a[j-1]; a[j-1] =a[j]; a[j] = temp; }else{ break; } } } }
二分排序
原理简介:二分排序是对插入排序改进的一种排序算法。区别在于,插入排序在有序区中插入一个值的时候,是从后往前一个个比较。而二分排序,会记录有序区的待插入范围,初始化范围为有序区的最左low和最右high,然后计算位置mid,如果比mid小,则说明插入范围在左边,更新high值为mid-1,反之更新low值为mid+1,然后进行下一次mid更新查找,直到找到合适的插入位置。算法:
1、取出无序区最左边位置为i的数,暂存在temp变量中。初始化low和high分别为0和i-1
2、计算mid=(low+high)/2,如果mid位置的变量比temp小,则更新low=mid+1,反之更新high=mid-1
3、一直循环比较逐渐缩小low和high的距离(也就是缩小待插入位置的范围),直到low不小于high,那么这个时候,low的值即为待插入位置。
4、将有序区中位于low右边的值往右移,更新low位置的变量为temp。此次插入完成。
5、循环插入所有数,进行length-1次插入即可。
代码实现
//二分排序 public static void binarySort(int[] a){ int i,j; int low,high,mid; int temp; for(i=1;i<a.length;i++){ temp = a[i]; low = 0; high = i-1; while(low<=high){ mid = (low+high)/2; if(a[mid]>temp){ high = mid-1; }else{ low = mid+1; } } for(j = i;j>low;j--){ a[j]=a[j-1]; } a[low] = temp; } }
快排
原理简介:快排思想是,任意取一个数(这里我们选取待快排的第一个数),比它小的放它左边,比它大的放它右边,此时原来的数组被分割为两个数组,且被选取的数在其对应的正确位置上,然后再对两个数组分别使用快排,又分割成4个数组,此时有3个数在其对应的正确位置上。就这样一直使用递归调用,直到所有的数都在其正确位置上位置。算法:
1、初始化待快排位置low为0 ,high为length-1;
2、如果low比high小,则进行快排分割,快排分割将待快排数组范围的第一个数据放在正确位置,并返回其放置的位置key
3、得到别分割的两个数组,范围分别为low到key-1,key+1到high。递归再次对其进行快排。
代码实现
//快排 本函数为调用函数 public static void quickSort(int[] a){ int low = 0; int high = a.length-1; quickSortHelp(a, low, high); } public static void quickSortHelp(int[] a, int low, int high){ if(low>=high) return; int key = partition(a, low, high); quickSortHelp(a, low, key-1); quickSortHelp(a, key+1, high); } public static int partition(int[] a, int low, int high){ int i = low; int j = high; int temp; while(i<j){ for(;j>i;j--){ if(a[j]<a[i]){ temp = a[j]; a[j] = a[i]; a[i] = temp; break; } } for(;i<j;i++){ if(a[j]<a[i]){ temp = a[j]; a[j] = a[i]; a[i] = temp; break; } } } return j; }说明:这里我的分割方法partition利用了嵌套循环,网上看到另外一个更优雅的实现,测试了下可行,不过我暂时没明白原理,代码这里给大家参考一下。
public static int partition(int[] a, int low, int high){ int last = a[high]; int i = low -1; for (int j = low; j <= high-1; j++) { if(a[j] <= last){ i++; if(i != j){ a[i] = a[i]^a[j]; a[j] = a[i]^a[j]; a[i] = a[i]^a[j]; } } } if((i+1) != high){ a[i+1] = a[i+1]^a[high]; a[high] = a[i+1]^a[high]; a[i+1] = a[i+1]^a[high]; } return i+1; }
归并排序
原理简介:归并算法是在两个有序子序列的基础上,将其合并为一个子序列。如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1}
第二次归并后:{6,100,202,301},{1,8,38}
第三次归并后:{1,6,8,38,100,202,301}
算法:
1、初始化归并增量为1
2、计算数组长度size,计算如果该次增量为len归并成功,会形成的多少个有序集合并赋给mid。计算此次归并中,是否存在单数集合并赋给c。
3、如果执行归并后,有序集合数等于0,则说明已经是有序序列,退出。
4、以增量len将数组划分开来,调用merge函数(具体在步骤7)。执行mid次merge即可。
5、如果c变量为1,即该次归并经过mid次之后,还存在没有排序的数组。那么将为排序的数组与它左边len长度的数组进行归并。
6、递归调用下一次归并函数。
7、merge函数第一个参数为数组,第二个参数为需要归并的第一个序列的开始位置,第三个参数为第二个序列的开始位置,第四个参数为第二个序列的结束位置,两个序列物理位置是相邻的。首先申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列,因为待归并的两个序列都是有序的,因此只需要分别记录两个序列的左边位置,数据小的复制,然后右移索引,直到某个序列复制完成。然后将复制另一序列的剩余数据。最终将空间的数据回写到参数数组中。
代码实现
//归并排序 本函数为调用函数 public static void mergeSort(int[] a){ mergeSort(a ,1); } public static void mergeSort(int[] a, int len){ int size = a.length; int mid = size/(len<<1); int c = size %(len << 1); if(mid ==0){ return; } int s = 0; for(int i = 0; i<mid; ++i){ merge(a, s, s+len,s+(len<<1)-1); s = s + (len<<1); } if (c != 0){ merge(a, size - c - 2 * len, size - c, size - 1); } mergeSort(a, 2 * len); } public static void merge(int[] a, int s, int m, int t){ int p = s; int q = m; int i=0; int[] c = new int[t-s+1]; while(p<m && q<=t){ if(a[p]<=a[q]){ c[i] = a[p]; p++; i++; }else{ c[i] = a[q]; q++; i++; } } if(p<m){ for(int j =i;j<=t-s;j++){ c[j] = a[p]; p++; } } if(q<=t){ for(int j =i;j<=t-s;j++){ c[j] = a[q]; q++; } } System.arraycopy(c, 0, a, s, c.length); }
希尔排序
原理简介:希尔排序也是对插入排序的一种 改进算法。它是按增量来分割数组,对数组进行排序,增量初始化为length/2,然后递减增量知道增量为1,则排序完成。如:a = 3 1 3 5 9 7 4 4
d = length/2 = 4
那么a[0]和a[4],a[1]和a[5],a[2]和a[6],a[3]和a[7]一组,分别对四组进行快排
第一次排序为:3 1 3 4 9 7 4 5
类似的,第一次d=2,排序为3 1 3 4 4 5 9 7
第三次d=1,排序为1 3 3 4 4 5 7 9
算法:
1、初始化增量为d=length/2
2、进行shell排序,d此时的隐含含义就是该增量下,数组被分割的小序列数目。因此进行需要进行d次快排。
3、剩下的就是在每一次快排中调用之前的快排算法了,这部分可以参考前面的算法。
代码实现
//希尔排序 本函数为调用函数 public static void shellSort(int[] a){ int d = a.length/2; while(d>=1){ shellSortHelp(a, d); d=d/2; } } public static void shellSortHelp(int[] a, int d){ for(int x=0;x<d;x++) for(int i=x+d; i<a.length; i=i+d){ for(int j =i;j-d>=0;j=j-d){ if(a[j]<a[j-d]){ int temp = a[j-d]; a[j-d] =a[j]; a[j] = temp; }else{ break; } } } }
堆排序
原理简介:大顶堆是指父节点必定大于子节点,因此堆顶元素必定是最大值。堆排序是指将我们的数组初始化为一个大顶堆,然后将堆顶元素取出来,调整剩下的数据,构造一个新的大顶堆,进行length-1次取堆顶元素即可。这里需要了解的基础是,堆的特性,如果堆顶以标号0开始的话,其父节点为i,那么左右子节点分别为2i+1,2i+2。
更新堆顶元素的调整过程:一个大顶堆,更新堆顶元素之后,需要与其左右子节点比较,如果小于子节点,则需要交换,然后继续与子节点比较,直到大于等于子节点或者子节点序号大于数组长度为止。
参考文章,堆排序的更详细的原理介绍:http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html
算法:
1、首先编写堆适应函数heapAjust,第一个参数是数组,第二个参数是parent(即更新过后的不稳定节点),第三个参数是length,length为堆大小。
数组左边为大顶堆,右边为有序序列。每次更新堆中节点parent,我们首先将a[parent]赋值给temp,计算左子节点索引为child=2*parent+1,判断左子节点是否存在,如果存在,则继续判断其右子节点是否存在且大于左子节点,如果是,则更新child++,即获取右子节点索引。然后比较temp值于子节点中较大值,如果temp大,则堆不变,如果temp小,则交换,此时更新的值为child节点,继续循环比较child的子节点操作。
2、得到待排序数组后,首先我们将其初始化为大顶堆。从我们刚才编写的堆适应函数来看,它是从父节点来更新堆,因此为了包括所有的元素,我们可以从len/2这个索引开始初始化堆。然后依次递减,直到初始化所有元素。
3、此时数组中是以大顶堆顺序排列的,有序序列在数组右边,左边为堆,我们取出第一个元素,与数组的最后一个元素交换,此时堆中元素少一个,有序序列中元素多一个,我们执行len-1次操作,即可将堆中元素全部取出得到有序序列。
代码实现
//堆排序 本函数为调用函数 public static void heapSort(int[] a){ for(int i=a.length/2-1; i>=0;i--){ heapAjust(a, i, a.length); } for(int j=a.length-1;j>0;j--){ int temp = a[j]; a[j] = a[0]; a[0] = temp; heapAjust(a,0,j); } } public static void heapAjust(int[] a, int parent, int length){ int temp = a[parent]; int child = parent*2+1; while(child<length){ if(child+1<length&&a[child]<a[child+1]){ child++; } if(temp>a[child]){ break; } a[parent] = a[child]; parent = child; child = 2*child+1; } a[parent] = temp; }
基础排序算法的学习就到此为止了。
相关文章推荐
- Java基础学习总结(28)——Java对各种排序算法的实现
- Java基础学习总结(28)——Java对各种排序算法的实现
- ZH奶酪:【数据结构与算法】基础排序算法总结与Python实现
- Java基础学习总结(28)——Java对各种排序算法的实现
- python面试之算法基础(排序算法总结与实现)
- 算法基础:基本排序算法原理、实现与总结
- 排序算法总结-python实现
- 黑马程序员——Java基础语法:几种简单的排序算法的实现
- 各种排序算法的java实现及时间、空间复杂度、稳定程度总结
- 总结java实现八大排序算法及之间的关系
- java实现8张基础排序算法
- 基础排序算法总结
- 基础排序算法总结
- 各种排序算法及其实现总结
- 黑马程序员:Java基础总结----子接口 set<E>及其实现类
- android中webView使用总结---飞速浏览器实现基础.
- 剑指offer:排序算法实现经验教训总结
- 各种排序算法及其实现总结
- 第2章 Java编程基础——FAQ2.27 数组的排序算法有哪些?如何实现?
- linux c 实现八大排序算法总结