一、 冒泡排序
冒泡排序是非常容易理解和实现,以从小到大排序举例:
设数组长度为N。
冒泡排序从前往后遍历和从后往前遍历一样的原理。
目标数组:3,5,2,6,4,9,7,12,11
从前往后:
第一波:从第一个数开始,如果第一个数大于第二个数,就把这两个数调换位置,否则保留之前的排列,第二次把第二个和第三个数比较,比较方式同上,这样经过第一波比较后,留在最后面的数是整个数组中最大的数;
第二波:由于第一波比较出了数组中最大的数在最后面(N的位置),所以第二波比较的时候只要遍历到N-1的位置就可以了,可以将这一波中得出的最大得数放在N-1的位置。
第三波,第四波. . . ..
这样比较N-1波【一共(n-1)! 次】
二、 直接插入排序
直接插入排序(InsertionSort)的基本思想是:每次将一个待排序的记录,按其关
键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成
为止。
设数组为a[0…n-1]。
1. 初始时,a[0]自成 1 个有序区,无序区为a[1..n-1]。令 i=1
2. 将 a[i]并入当前的有序区 a[0…i-1]中形成 a[0…i]的有序区间。
3. i++并重复第二步直到 i==n-1。排序完成。
三、 直接选择排序
直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是
直接插入排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有
序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
设数组为a[0…n-1]。
4. 初始时,数组全为无序区为 a[0..n-1]。令 i=0
5. 在无序区 a[i…n-1]中选取一个最小的元素, 将其与 a[i]交换。 交换之后 a[0…i]
就形成了一个有序区。
6. i++并重复第二步直到 i==n-1。排序完成。
四、 快速排序
快速排序由于排序效率在同为 O(N*logN)的几种排序方法中效率较高, 因此经常
被采用,再加上快速排序思想——分治法也确实非常实用,因此很多软件公司的
笔试面试,包括像腾讯,微软等知名 IT 公司都喜欢考这个,还有大大小的程序
方面的考试如软考,考研中也常常出现快速排序的身影。
总的说来,要直接默写出快速排序还是有一定难度的,因此本人就自己的理解对
快速排序作了下白话解释,希望对大家理解有帮助,达到快速排序,快速搞定。
快速排序是C.R.A.Hoare 于 1962 年提出的一种划分交换排序。它采用了一种分
治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它
的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
虽然快速排序称为分治法, 但分治法这三个字显然无法很好的概括快速排序的全
部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法:
先来看实例吧,定义下面再给出(最好能用自己的话来总结定义,这样对实现代
码会有帮助) 。
以一个数组作为示例,取区间第一个数为基准数。
初始时,i =0; j = 9; X = a[i] = 72
由于已经将a[0]中的数保存到 X 中,可以理解成在数组 a[0]上挖了个坑,可以将
其它数据填充到这来。
从 j 开始向前找一个比 X 小或等于 X 的数。当 j=8,符合条件,将 a[8]挖出再填
到上一个坑a[0]中。a[0]=a[8]; i++; 这样一个坑 a[0]就被搞定了,但又形成了一
个新坑a[8],这怎么办了?简单,再找数字来填 a[8]这个坑。这次从 i 开始向后
找一个大于 X的数,当 i=3,符合条件,将 a[3]挖出再填到上一个坑中 a[8]=a[3];
j--;
数组变为:
i = 3; j = 7; X=72
再重复上面的步骤,先从后向前找,再从前向后找。
从 j 开始向前找,当 j=5, 符合条件, 将 a[5]挖出填到上一个坑中, a[3] = a[5]; i++;
从 i 开始向后找,当 i=5 时,由于 i==j 退出。
此时,i =j = 5,而 a[5]刚好又是上次挖的坑,因此将 X 填入 a[5]。
数组变为:
可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对 a[0…4]
和a[6…9]这二个子区间重复上述步骤就可以了。
对挖坑填数进行总结
1.i =L; j = R; 将基准数挖出形成第一个坑 a[i]。
2.j--由后向前找比它小的数,找到后挖出此数填前一个坑 a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑 a[j]中。
4.再重复执行 2,3 二步,直到 i==j,将基准数填入 a[i]中。
package test2; /** * @author lee * */ public class PaiXu { /** * @description TODO * @time 2017年3月5日 下午6:28:24 * @author lee * @return void */ public static void main(String[] args) { // TODO Auto-generated method stub /*int[] a = {3,5,2,6,4,9,7,12,11};*/ int[] a ={3,2,4,1,7,0,5}; //maoPaoSort1(a); //maoPaoSort2(a); //zjxz(a); quickSort(a, 0, a.length-1); for(int k=0;k<a.length;k++) { System.out.print(a[k]+","); } } /** * * @description 冒泡排序1 - 直接根据实现逻辑进行编写代码 * 主要是要留意for循环总j的范围:for(j=0;j<a.length-i-1;j++) * @time 2017年3月5日 下午6:41:50 * @author lee * @return void */ static void maoPaoSort1(int[] a) { int i,j,temp; for(i=0;i<a.length;i++) { for(j=0;j<a.length-i-1;j++) { if(a[j]>a[j+1]) { temp = a[j+1]; a[j+1] = a[j]; a[j] = temp; } } } } /** * @description 冒泡排序2 - 优化 * 设置一个标志,如果这一趟发生了交换,则为true,否则为 * false。明显如果有一趟没有发生交换,说明排序已经完成 * @time 2017年3月5日 下午6:45:39 * @author lee * @return void */ static void maoPaoSort2(int[] a) { int i,j = a.length,temp; boolean flag = true; while(flag) { flag = false; //以下for循环只要发生了一次交换,flag就置为true了, //如果某一波循环没有交换,那么flag就为false了,排序完成。 for(i=0;i<j-1;i++) { if(a[i]>a[i+1]) { temp = a[i+1]; a[i+1] = a[i]; a[i] = temp; flag = true; } } //每次遍历的长度-1 j--; } } /** * * @description 直接插入排序1 * @time 2017年3月7日 下午8:44:04 * @author lee * @return void */ static void zjcr1(int[] a) { int i,j = 0,temp = 0; for(i=1;i<a.length;i++) for(j=i-1;j>=0&&a[j]>a[j+1];j--) { temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } /** * 直接选择排序 * @author lee 2017年3月12日 下午2:21:15 */ static void zjxz(int[] a) { int i,j,minIndex,temp; for(i=0;i<a.length;i++) { minIndex = i; //这个for循环是求无序区中最小值的索引 for(j=i+1;j<a.length;j++) { if(a[j]<a[minIndex]) minIndex = j; } //将最小值与a[i]交换 temp = a[minIndex]; a[minIndex] = a[i]; a[i] = temp; } } /** * 快速排序 * @author lee 2017年3月12日 下午4:06:57 */ static void quickSort(int[] a,int l,int r) { if(l<r) { int i=l,j=r,x=a[l]; while(i<j) { //从右到左,找第一个小于x的数 while(i<j&&a[j]>=x) { j--; } if(i<j) { a[i]=a[j]; } while(i<j&&a[i]<=x) { i++; } if(i<j) { a[j] = a[i]; } } a[i] = x; //递归调用 quickSort(a, l, i-1); quickSort(a, i+1, r); } } }