2.1 初级排序
2.1.2 选择排序
最简答的一种排序。整个排序分为两个循环。对于一个长度为n的数组而言,在内层循环中,每次循环的目的是为了找出从索引为i的元素开始,到最后一个元素为止,[i,n-1]这个区间内最小的元素的下标,然后交换i和min_index。
整个排序算法的代价和输入的数组的特性无关,无论是完全有序的数组还是逆序的数组,总是要经过同样的代价。对一个长度为n的数组,交换的次数一定是n次,当然如果在交换前能够比较一下是否有必要交换,那么交换的次数就小于n次;比较的次数是一定的,第一次循环n-1次,第二次n-2次,这是一个等差数列,复杂度是n方。
protected void sort(int[] a) { int N = a.length; for(int i=0; i<N; i++){ int min_index = i; for(int j=min_index; j<N; j++){ if(a[j] < a[min_index]){ min_index = j; } } exch(a,i,min_index); } }
2.1.3 插入排序
维护一个有序数组,然后有序数组不停的朝着一个方向生长。同样是双层循环,外部循环让数组增长,内部数组负责把一个元素放到有序区的合适位置。
插入排序的交换次数和数组的初始状态有关,一个完全有序的数组只要外层循环走一遍就可以了。最好的情况下比较n-1次,交换0次。最坏的情况下数组完全逆序,比较和交换的次数都是n方次的,也是一个等差数列求和。
protected void sort(int[] a) { int n = a.length; for(int i=1;i<n;i++){ //在有序数组里找到自己的位置以维护这种有序性 for(int j=i-1;j>=0;i--){ if(a[i] < a[j-1]){ exch(a,j,i); }else { break; } } } }
2.1.4 希尔排序
希尔排序是对插入排序的一种优化,插入排序在原始数组基本有序或者原始数组比较小的时候排序效率比较高,所以根据这个特性,每次排序的时候都选择一个较小的数组进行排序,在弄的差不多有序的情况下,再清楚插入排序进行最终的排序。
首先是gap的大小的选择,gap大小选择有很多种说法,书上给的是n/3递减到1。不管何种gap的选择,gap一定是逐渐递减到1对应着退化到插入排序的过程,区别在于最大的gap选择多少。
外层循环对应的是gap的缩小的过程,每次gap除以3,当然是取整。
内层循环是使当前gap下子数组有序的过程,这里一个特点是无论一个多大的数组,当选定了间隔为gap时,只会生成gap个子数组,对gap个子数组有序化的过程对应第一个for循环。对取出的每一个子数组,采用插入排序使之有序,这里的插入排序和一般的插入排序的区别在于每次的间隔不是1而是gap。
protected void sort(int[] a) { int n = a.length; //找到最大的gap,然后在循环的过程中不断减小gap直到1 int gap = 1; while (gap<n/3) gap=3*gap+1; while (gap>=1){ //对于每一个给定的gap,使原数组所有以gap为间隔的子序列全部有序 //对任意一个数组,以gap为间隔生成子序列,最多可以生成gap个 for(int i=0;i<gap;i++){ //找到以i为开始,i+gap i+2*gap的子序列中最小的那个并与i交换位置 int min_index = i; for(int j=gap+i ;j<n;j+=gap){ if(a[j] < a[min_index]){ min_index = j; } exch(a,i,min_index); gap/=3; } } } }