算法系列——排序算法
此表格转自此链接博客(http://www.cnblogs.com/nannanITeye/archive/2013/04/11/3013737.html)
1.插入排序
插入排序的方法:从第二个数字num[i]开始,依次与它前边的num[j]比较,如果num[i]>num[j],比较下一个num[j],如果num[i]<num[j],则将num[j]和之后的数字依次向后 移动一个位置,为num[i]腾出空间,将num[i]放在num[j]的位置上。
public static void main(String[] args) throws IOException { int[] num = {4,8,3,9,5,7,2}; sort(num); for(int i =0;i<num.length;i++){ System.out.println(num[i]); } } private static void sort(int[] num) { int temp = 0; for(int i = 1;i<num.length;i++){ for(int j =0;j<i;j++){ if(num[i]>num[j]){ continue; } if(num[i]<num[j]){ temp = num[i]; for(int x=i;x>j;x--){ //找到数字应该放置的位置后,将应在位置和实际位置之间的数字向后移一位,以便插入数字。 num[x] = num[x-1]; } num[j] =temp; } } } }
2.希尔排序
希尔排序的方法:第一步:选取增量序列的值,一般是按除2的方式选取,直至1 gap=num.length/2;gap=gap/2...
第二步:如果gap=num.leng/2=3,则将数列中的第0项,第3项,第6项...放在一起;第1项,第4项,第7项...放在一起,分别按插入排序的方法比较。
最后一步:gap=3/2=1;就将数列按照插入排序的方法排序一次,得出结果。
public static void main(String[] args){ int[] num = {3,3,5,2,7,0,1,4,9,6,8}; sort(num); for(int i = 0;i<num.length;i++){ System.out.println(num[i]); } } public static void sort(int[] num){ int gap = num.length/2; for(int k = gap;k>0;k=k/2){ for(int i=0;i<num.length;i++){ for(int j = i+k;j<num.length;j+=k){ if(num[i]>num[j]){ int temp = num[i]; num[i] = num[j]; num[j] = temp; } } } } }
3.冒泡排序
冒泡排序的方法:第一步:将num[0]与之后的num[i]依次比较,如果num[0]<num[i],继续比较下一个,如果num[0]>num[i],将num[0]和num[i]互换。第一步结束之 后,数组中的最小值就是num[0];
第二步:将num[1]与之后的num[i]依次比较,重复第一步。第二小的数字便是num[1].
public static void main(String[] args){ int[] num = {9,4,2,7,2,8,4,1}; sort(num); for(int i =0;i<num.length;i++){ System.out.println(num[i]); } } public static void sort(int[] num){ int temp = 0; for(int i=0;i<num.length-1;i++){ for(int j=i+1;j<num.length;j++){ if(num[i]<num[j]){ continue; } else{ temp = num[i]; num[i] = num[j]; num[j] = temp; } } } }
4.快速排序
快速排序的方法:分治法+填坑法。
第一步:首先取最右端(或最左端或随机都可以,但尽量在最左或最右,此次按最右端)的值num[num.length-1]赋给中间值middle,此时num.length-1处可以看作没有了,是一个坑,所以我们 就可以找一个值来填充它。
那么把哪个位置上的值填充到这里呢?由于我们的目的是按照从小到大的顺序排列这个数列,所以当我们在数列的最后一位有空位置的时候,我们自然就要把比middle大的值放在空位置上。
那么怎么来找这一个比middle大的值?我们就需要两个指针,一个low,一个high,low初始指向num[0],high初始指向num[num.length-2],由它们带领来查找合适的填坑值。
当高位有空位时,就从低位找一个比middle大的值放过来,当低位有空位时,就从高位找一个比middle小的值来填充,同时low和high也在不断逼近,当low=high时,第一次排序完成。把middle的值放在low和high处。
第二步:low和high在的位置就是中间值的位置,然后递归将middle左边的数列排序,middle右边的数列排序。
注释掉的是自己写的时候犯的错误,包括对于方法参数的不明确。
最坏情况:排一个已经排好序的数列,时间复杂度是O(n2);
public class Test { public static void main(String[] args){ int[] num = {1,7,2,4,6,8,5}; /*int middle = getMiddle(num,0,num.length-1); getMiddle(num,0,middle-1); getMiddle(num,middle+1,num.length-1);*/ sort(num,0,num.length-1); for(int i = 0;i<num.length;i++){ System.out.print(num[i]); } } public static int getMiddle(int[] num,int low,int high){ int middleValue = num[high];//int middleValue = num[num.length-1]; while(low<high){ while(low<high&&num[low]<middleValue){ low++; } num[high] = num[low]; while(low<high&&num[high]>middleValue){ high--; } num[low] = num[high]; } num[low] = middleValue; return low; } public static void sort(int[] num,int low,int high){ int middle; if(low<high){ middle = getMiddle(num,low,high); getMiddle(num,low,middle-1); getMiddle(num,middle+1,high); } } }
以下是一开始按照自己的理解写的代码,很复杂,而且最后出现了错误,如果不加注释掉的那三行,能获取正确的middle,加上之后返回的middle就不对了。
public static int getMiddle(int[] num){ int temp = 0; int middle = 0; int j =0; int value = num[num.length-1]; for(int i =0;i<num.length-1;i++){ if(num[i]<value){ if(i==0){ continue; }else{if(j==0){ continue; }else{ temp = num[i]; num[i]= num[j]; num[j] = temp; j++; } } }else{ if(j==0){ j=i; } else{ continue; } } } middle =j; //int emp = value; //num[num.length-1] = num[j]; //num[j] = emp; return middle; }
5.简单选择排序
选择排序的方法:和冒泡法类似,冒泡法是便利num[i]之后的数字,遇见小的就换;选择排序是将较小数字的角标记录,然后继续遍历,遇见更小的就更新存储角标的变量,最后将变量中角标对应的数字变换位置。
public static void sort(int[] num){ for(int i=0;i<=num.length-2;i++){ int temp = i; for(int j=i+1;j<=num.length-1;j++){ if(num[j]<num[temp]){ temp = j; } else{ continue; } } if(temp!=i){ int cha = num[i]; num[i] = num[temp]; num[temp] = cha; } } }
6.堆排序(选择排序的改进)
完全二叉树:出最后一层之外,二叉树的每一层都是满的,在最后一层上只缺少右侧的结点。
最大最小堆:最大就是从上至下结点值是按降序排列的,越往上越大,越往左越大。
构造堆的时候,结点都是有规律的,如上图,左子结点的序号等于父结点的2倍(从0开始就是2倍加1),右子结点的序号等于父结点序号的2倍+1(从0开始就是2倍加2),在构造堆的时候可以利用这一点。
堆主要有两种操作:1.结点插入:将插入结点放到最后的位置,然后维护堆(依次与父结点比较大小)。2.结点删除:删除操作只能删除根节点,然后让最后一个结点成为根结点,之后维护堆。
排序方法是在构造堆后,输出num[0],然后将最后一个结点与根节点互换,然后重新构造长度减去1的堆,再输出num[0],重复以上操作。
构造堆的过程就是将数列中最大值放到根节点。
public static void main(String[] args){ int[] num = {3,5,2,7,0,1,4,9}; makeHeap(num,num.length-1,(num.length-2)/2); adjustHeap(num); } private static void makeHeap(int[] num,int heapSize,int index) { int left,right,large; for(int i = index;i>=0;i--){ left = i*2+1; right = i*2+2; large = i; if(left<=heapSize&&num[left]>num[i]){ large = left; } if(right<=heapSize&&num[right]>num[i]){ if(num[right]>num[left]){ large = right; } } if(large!=i){ int temp =num[i]; num[i] = num[large]; num[large] = temp; } } } public static void adjustHeap(int[] num){ int a; for(int i=0;i<=num.length-1;i++){ a=num[0]; System.out.println(a); int temp = num[num.length-1-i]; num[0] = temp; num[num.length-1-i]=temp; makeHeap(num,num.length-2-i,(num.length-3-i)/2); } }
7.归并排序
归并排序的方法:数列不断的被分割,当分到只有两个或一个不能再分的时候,就开始合并,合并的结果一开始临时存入到result数组中,每一次合并完成,就把result中的数放到num中对应位置(i+start)上。
public static void main(String[] args){ int[] num = {3,5,2,7,0,1,4}; sort(num,0,num.length-1); for(int i = 0;i<num.length;i++){ System.out.println(num[i]); } } public static void sort(int[] num,int start,int end){ if(start<end){ sort(num,start,(start+end)/2); sort(num,(start+end)/2+1,end); merge(num,start,(start+end)/2,end); } } private static void merge(int[] num, int start, int mid, int end) { int[] result= new int[end -start+1]; int left = start; int right = mid+1; int index = 0; while(left<=mid&&right<=end){ if(num[left]<num[right]){ result[index++] = num[left++]; } else { result[index++] = num[right++]; } } while(left<=mid){ result[index++]= num[left++]; } while(right<=end){ result[index++] = num[right++]; } for(int i = 0;i<result.length;i++){ num[i+start] = result[i]; } }
8.计数排序 O(n)
计数排序的方法:将数列遍历,在一个新的数列A里记录下每个数重复出现的次数,A的长度等于最大元素的值加一,然后将A中数列值累加,反向填充新数列。
public static void main(String[] args){ int[] num = {3,3,5,6,3,5,6}; sort(num); } public static void sort(int[] num){ int a =num[0]; int sum = 0; for(int i = 0;i<num.length-1;i++){ if(num[i]>a){ a=num[i]; } } int[] num1 = new int[a+1]; for(int j = 0;j<num.length;j++){ num1[num[j]]+=1; }
int sum =0; for(int k=0;k<num1.length;k++){ num1[k]+=sum;
sum = num1[k];
}
int[] result = new int[num.length]; 反向填充for(int q = 0;q<result.length;q++){ System.out.println(result[q]); } }
9.基数排序 O(n)
类似于重新排好扑克牌,先把四组相同花色的放到一起,然后再按从小到大顺序排列。第一次的基数是花色,第二次是大小。或者是先把面值相同的放到一起,然后再按花色组合。
如果数列中数字是两位数,那么先取第二位为基数,将基数相同的放到一起,从小到大组成新的数列;然后将新数列以第一位为基数,重新把基数相同的放到一起,然后按照基数大小顺序组成新的数列就是排好的数列。
如果数的长度不一样,前面补0。
10.桶排序 O(n)
桶排序方法:把一定范围内的数放到一个桶中,然后将各个桶内的数据进行排序,然后将各个桶内的数据合并