常见排序算法总结
2016-04-24 18:52
351 查看
学习排序和查找算法这些入门级的算法,对自身的编程思想的提升是显著的。因为这些算法实现代码较短,应用很常见。一般在面试中最常考的是快速排序和归并排序,并且经常有面试官要求现场写出这两种排序的代码。还有冒泡排序、插入排序、堆排序、基数排序、桶排序等。面试过程中对于这些排序可能会要求比较各自的优劣、各种算法的思想、其使用场景和时间、空间复杂度等。因此掌握常见的排序和查找算法很有必要。
下面简要分析一下常见的排序算法及其使用场景。
1.冒泡排序:
冒泡排序算是最简单的排序之一了,其大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名。冒泡排序的平均和最差时间复杂度为O(n^2),存储空间为O(1)。
2.选择排序:
基本思想是:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换;接着对不包括第一个记录外的其他记录进行第二轮比较,得到最小记录并与第二个记录位置交换,重复该过程,直到进行比较的记录只有一个时为止。选择排序的平均和最差时间复杂度为O(n^2),存储空间为O(1)。
3.插入排序:
对于给定的一组记录,初始时假设第一个记录自成一个有序序列,其余记录为无序序列。接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中,直至最后一个记录插入到有序序列中为止。插入排序的平均和最差时间复杂度为O(n^2),存储空间为O(1)。
4.快速排序:
快速排序是一种非常高效的排序算法,它采用“分而治之”的思想,把大的拆分成小的,小的再拆分成更小的。原理是:对于一组给定的记录,通过一趟排序后,把原序列分为两部分,其中前一部分的所有记录均比后一部分的所有记录小,然后再依次对前后两部分的记录进行快速排序,递归该过程,直到序列中的所有记录均有序为止。快速排序的平均时间复杂度为O(nlogn),最坏情况下的时间复杂度为O(n^2),在所有平均时间复杂度为O(nlogn)的算法中,快速排序的平均性能是最好的。平均空间复杂度为O(logn)。
5.归并排序:
对于给定的一组记录,首先将每两个相邻的长度为1的子序列进行归并,得到n/2(向上取整)个长度为2或1的有序子序列,再将其两两归并,反复执行此过程,直到得到一个有序序列。所以,归并排序的关键就是两步:第一步,划分半子表;第二步,合并半子表。归并排序的平均情况和最坏情况的时间复杂度均为O(nlogn),空间复杂度为O(n)。
6.希尔排序:
希尔排序也称为“缩小增量排序”,基本思想是:先将待排序的数组元素分成多个子序列,使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序,待整个待排序序列“基本有序后”,最后再对所有元素进行一次直接插入排序。希尔排序的分析是复杂的,时间复杂度是所取增量的函数,这涉及一些数学上的难题。但是在大量实验的基础上推出当n在某个范围内时,时间复杂度可以达到O(n^1.3)。
其他的排序还有堆排序、桶排序、位图排序、基数排序等,每种排序算法都有其最佳的适用场合,结合实际情况进行分析选择合适的排序算法。
下面简要分析一下常见的排序算法及其使用场景。
1.冒泡排序:
冒泡排序算是最简单的排序之一了,其大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名。冒泡排序的平均和最差时间复杂度为O(n^2),存储空间为O(1)。
public class BubbleSort { static int[] data = {9,2,7,19,100,98,63,208,55,78}; public static void bubbleSort(){ //从后往前比较,小的数字依次往前冒泡,直到排序完成 int tmp = 0; for(int i=0;i<data.length;i++){ //需要遍历n趟,每一趟冒泡一个最小的数字 for(int j=data.length-1;j>i;j--){ if(data[j] < data[j-1]){ //在每一趟中,从后往前相邻进行比较,小的数字往前冒泡,直到该趟最前 tmp = data[j]; data[j] = data[j-1]; data[j-1] = tmp; } } } } public static void print(){ for(int i =0;i<data.length;i++){ System.out.print(data[i]+" "); } } public static void main(String[] args){ BubbleSort.print(); System.out.println(); System.out.println("=============="); BubbleSort.bubbleSort(); BubbleSort.print(); } }
2.选择排序:
基本思想是:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换;接着对不包括第一个记录外的其他记录进行第二轮比较,得到最小记录并与第二个记录位置交换,重复该过程,直到进行比较的记录只有一个时为止。选择排序的平均和最差时间复杂度为O(n^2),存储空间为O(1)。
public class SelectSort { static int[] data = {9,2,7,19,100,98,63,208,55,78}; public static void selectSort(){ //按位置排序,第1个位置该排谁,第2个位置该排谁。。。。。 int k =0 ,tmp =0; for(int i=0;i<data.length;i++){ //按位置排序,首先找到最小值,排在第1位,然后在待排序的数中选择最小的值,排在第2位。。。。。。 k=i; for(int j=i+1; j<data.length;j++){ if(data[j] < data[k]){ k = j; //k:用于保存每次的最小值的下标,然后和i位置的数字进行交换 } } tmp = data[k]; data[k] = data[i]; data[i] = tmp; } } public static void print(){ for(int i=0;i<data.length;i++){ System.out.print(data[i]+" "); } } public static void main(String[] args){ SelectSort.print(); System.out.println(); System.out.println("======================="); SelectSort.selectSort(); SelectSort.print(); } }
3.插入排序:
对于给定的一组记录,初始时假设第一个记录自成一个有序序列,其余记录为无序序列。接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中,直至最后一个记录插入到有序序列中为止。插入排序的平均和最差时间复杂度为O(n^2),存储空间为O(1)。
public class InsertSort { static int[] data = {9,2,7,19,100,98,63,208,55,78}; public static void insertSort(){ int tmp=0,j=0; for(int i=1;i<data.length;i++){ tmp = data[i]; //把data[i]保存起来,tmp用来保存即将进行排序的数字 j = i-1; //j是i的前一个数字的下标 while(j>=0 &&tmp<data[j]){ data[j+1] = data[j]; //每次跟前面的数字进行比较,如果前面的数字大就把该数字向后移动1位 j--; } data[j+1] = tmp; //j+1就是tmp应该插入的位置,直接插入即可 } } public static void print(){ for(int i=0;i<data.length;i++){ System.out.print(data[i]+" "); } } public static void main(String[] args){ InsertSort.print(); System.out.println(); System.out.println("===================="); InsertSort.insertSort(); InsertSort.print(); } }
4.快速排序:
快速排序是一种非常高效的排序算法,它采用“分而治之”的思想,把大的拆分成小的,小的再拆分成更小的。原理是:对于一组给定的记录,通过一趟排序后,把原序列分为两部分,其中前一部分的所有记录均比后一部分的所有记录小,然后再依次对前后两部分的记录进行快速排序,递归该过程,直到序列中的所有记录均有序为止。快速排序的平均时间复杂度为O(nlogn),最坏情况下的时间复杂度为O(n^2),在所有平均时间复杂度为O(nlogn)的算法中,快速排序的平均性能是最好的。平均空间复杂度为O(logn)。
public class QuickSort { static int[] data = {9,2,7,19,100,98,63,208,55,78}; public int partition(int[] data,int low,int high){ //选取第一个数为key,把数组分为两部分 int key = data[low]; //先把第一个数保存在变量key中,最后放到合适的位置上 while(low < high){ while(low < high && data[high]>=key) //如果data[high]比key大,就跳过high,再往前找一个数 high--; //直到data[high]比key小,然后进行交换 data[low] = data[high]; while(low < high && data[low]<=key) //同理...... low++; data[high] = data[low]; } data[low] = key; //把事前保存的key放到合适的位置low上 return low; //返回这个合适的位置,就是可以使数字分居左右两侧的key位置 } public int[] sort(int low, int high){ //递归调用sort,直到low == high,递归结束,排序完成 if(low < high){ int result = partition(data, low, high); sort(low,result-1); sort(result+1,high); } return data; } public void print(int[] data){ for(int i = 0; i<data.length; i++){ System.out.print(data[i]+" "); } } public static void main(String[] args){ QuickSort qs = new QuickSort(); qs.data = data; qs.print(data); System.out.println(); System.out.println("==================="); qs.sort(0, data.length-1); qs.print(data); }
5.归并排序:
对于给定的一组记录,首先将每两个相邻的长度为1的子序列进行归并,得到n/2(向上取整)个长度为2或1的有序子序列,再将其两两归并,反复执行此过程,直到得到一个有序序列。所以,归并排序的关键就是两步:第一步,划分半子表;第二步,合并半子表。归并排序的平均情况和最坏情况的时间复杂度均为O(nlogn),空间复杂度为O(n)。
public class MergeSort { public static void mergeSort(int[] a){ Sort(a,0,a.length-1); //下标的范围: [0 ~ a.length-1] } private static void Sort(int[] a, int left, int right) { if(left < right){ //分解条件:left < right ,当left >= right时,说明已经分解到单个数字了 int mid = (left + right)/2; //选取中间位置 Sort(a,left,mid); //递归对左半部分进行分解,直到不满足(left < right)条件为止 Sort(a,mid+1,right); //递归对右半部分进行分解,直到不满足(left < right)条件为止 Merge(a,left,mid,right); //分解过程完成,进行合并 } } public static void Merge(int[] a, int left, int mid, int right) { //合并算法,就当做是两个数组进行合并 int[] tmp = new int[a.length]; //附件空间tmp数组,长度和原数组长度一致 int i = left; int j = mid+1; int k = 0; while(i <= mid && j<=right){ if(a[i] <= a[j]){ tmp[k++] = a[i++]; }else{ tmp[k++] = a[j++]; } } while(i<=mid){ tmp[k++] = a[i++]; } while(j<=right){ tmp[k++] = a[j++]; } for(i=0;i<k;i++){ //k在上面多加了一次,所以此时,i<k 而不是 i<= k [注意!] a[left+i] = tmp[i]; //合并完成之后,把此时已经有序的附加数组tmp拷贝回原数组a[] } } public static void print(int[] a){ for(int i=0;i<a.length;i++){ System.out.print(a[i]+" "); } } public static void main(String[] args){ int[] a = {8,16,99,732,10,1,29,66}; MergeSort.print(a); System.out.println(); System.out.println("==========================="); MergeSort.mergeSort(a); MergeSort.print(a); } }
6.希尔排序:
希尔排序也称为“缩小增量排序”,基本思想是:先将待排序的数组元素分成多个子序列,使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序,待整个待排序序列“基本有序后”,最后再对所有元素进行一次直接插入排序。希尔排序的分析是复杂的,时间复杂度是所取增量的函数,这涉及一些数学上的难题。但是在大量实验的基础上推出当n在某个范围内时,时间复杂度可以达到O(n^1.3)。
public class ShellSort { public static void shellInsert(int[] arr, int d) { for(int i=d; i<arr.length; i++) { int j = i - d; int temp = arr[i]; //记录要插入的数据 while (j>=0 && arr[j]>temp) { //从后向前,找到比其小的数的位置 arr[j+d] = arr[j]; //向后挪动 j -= d; } if (j != i - d) //存在比其小的数 arr[j+d] = temp; } } public static void shellSort(int[] arr) { if(arr == null || arr.length == 0) return ; int d = arr.length / 2; while(d >= 1) { shellInsert(arr, d); d /= 2; } }
其他的排序还有堆排序、桶排序、位图排序、基数排序等,每种排序算法都有其最佳的适用场合,结合实际情况进行分析选择合适的排序算法。
相关文章推荐
- CodeForces 660C Hard Process
- Dialog 对话框
- C++ primer第二节笔记
- Toast
- 北京林业大学“计蒜客”杯程序设计竞赛 网络赛 A题 换瓶盖
- numpy 中array参数'f'与order='f'区别
- Struts多个文件上传
- 构建之法阅读笔记04
- 【转载】Gradle学习 第十章:网络应用快速入门
- Android控件SeekBar
- Gallery&自定义适配器
- Laxcus大数据管理系统2.0(9)- 第七章 分布任务组件
- HDU 5640 King's Cake
- 滚动控件ScrollView
- JSP中如何 循环显示 数据库中的数据到list.jsp页面 (转载)
- Android的页面管理控件ViewFlipper
- jQuery EasyUI 中EasyLoader加载器
- angular 指令@、=、&的用法和区别
- 深入理解okio的优化思想
- K 均值聚类算法-C#