1.希尔排序
1.1 排序思想
通俗来讲,希尔排序就是一种分组的插入排序,通过一个偏移量h将待排序序列分组进行插入排序,偏移量逐渐减小直至为1,当h为1时,排序已经完成。
插入排序就是偏移量从头到尾都为1的希尔排序。
1.2 代码实现
希尔排序的时间复杂度主要与h的取值有关,我自己这边主要是将
Step1:先确定h的值
Step2:进行while循环,循环结束的条件是h=1
Step2.1:找到排序元素将其插入前面排序好的序列中(主要是序列的index值注意体会)
Step2.2 h/2
public class Shell { public static void sort(Comparable[] a) { //1.确定h的值 int length = a.length; int h = 1; while (h < length / 2) { h = 2 * h + 1; } /*** * 其实这里的逻辑与插入排序一致 * 都是先找到排序的元素,然后将该元素插入到前面排序好的序列中 * 不一样的点: * 插入排序的h是固定为1; * 但是希尔排序的h会慢慢减少到1 */ //2.进行排序 while (h >= 1) { //2.1找到排序的元素 for (int i = h; i < length; i++) { //将当前的元素插入前面排序好的序列中 for(int j=i;j>=h;j-=h){ if(greater(a[j-h],a[j])){ exchange(a,j-h,j); }else{ break; } } } //3.将h的值减少 h=h/2; } } private static void exchange(Comparable[] a, int i, int j) { Comparable temp; temp=a[i]; a[i]=a[j]; a[j]=temp; } /*** * 比较大小 * @param comparable * @param comparable1 * @return */ private static boolean greater(Comparable comparable, Comparable comparable1) { //大于0表示comparable>comparable1 return comparable.compareTo(comparable1)>0; } }
1.3 时间复杂度分析
希尔排序的时间复杂度主要是与h的取值有关。
2.快速排序
2.1 排序思想
个人觉得快速排序是一个有哨兵的排序,这个哨兵就是待排序的第一个元素,通过一定的操作(从后往前找比哨兵小的进行交换,从前往后找比哨兵大的进行交换)将数列分为比哨兵小的部分和比哨兵大的部分,对两部分进行分而治之,这两部分的操作和之前的一模一样。
典型的一种分治算法。
2.2 代码实现
通过两个指针left、right完成分组的操作
public class Quick { /*** * 总方法 * 进行排序 * @param a */ public static void sort(Comparable[] a){ int low=0; int high=a.length-1; //调用分的方法 sort1(a,low,high); } /*** * 在low和high区间内进行排序 * @param a * @param low * @param high */ public static void sort1(Comparable[] a,int low,int high){ //先做安全性校验 if(low>high){ return ; } //分成两个数组进行排序 int partition=partition(a,low,high); //返回的那个哨兵的索引值 sort1(a,low,partition-1); sort1(a,partition+1,high); } private static int partition(Comparable[] a,int low,int high){ //确定分界值 Comparable key=a[low]; //定义两个指针 int left=low; int right=high+1; //切分 while(true){ //从右向左移动right找到比key小的值 while(less(key,a[--right])){ if(right==low){ break; } } //从左向右移动left找到比key大的值 while(less(a[++left],key)){ if(left==high){ break; } } //判断left>=right,如果是就结束扫描,如果不是就交换上面两个的值 if(left>=right){ break; }else{ exchange(a,left,right); } } //交换分界值将第一个和right的值进行交换 exchange(a,low,right); //返回right的值 return right; } /*** * 交换 * @param a * @param i * @param j */ private static void exchange(Comparable[] a, int i, int j) { Comparable temp; temp=a[i]; a[i]=a[j]; a[j]=temp; } /*** * 比较大小 * @param comparable * @param comparable1 * @return */ private static boolean less(Comparable comparable, Comparable comparable1) { //小于0表示comparable<comparable1 return comparable.compareTo(comparable1)<0; } }
2.3 时间复杂度分析
最好的情况: 每次分组都很平均,可以形成一棵树
最坏的情况: 逆序,每次分组都得扫描全部
3 归并排序
3.1 排序思想
典型的分治算法,进行分组排序然后合并,每次都平均分组进行排序,再将排序好的数列进行合并,需要一个辅助数组进行辅助典型的用空间换时间的算法
3.2 代码实现
主要的代码实现是合并的部分merge()方法,主要是通过三个指针和一个辅助数组,这部分一定要手动模拟合并的过程。
public class Merge { //定义一个辅助数组 private static Comparable[] assist; /*** * 总方法 * 进行排序 * @param a */ public static void sort(Comparable[] a){ //初始化辅助数组 assist=new Comparable[a.length]; int low=0; int high=a.length-1; //调用分的方法 sort1(a,low,high); } /*** * 在low和high区间内进行排序 * @param a * @param low * @param high */ public static void sort1(Comparable[] a,int low,int high){ //先做安全性校验 if(low>high){ return ; } //分成两个数组进行排序 int mid=low+(high-low)/2; sort1(a,low,mid); sort1(a,mid+1,high); //合并两个数组 merge(a,low,mid,high); } /*** * 将[low,mid]和[mid,high+1] * 两个数组进行排序合并 * 主要是利用指针进行合并 * @param a * @param low * @param mid * @param high */ public static void merge(Comparable[] a,int low,int mid,int high){ //定义三个指针 int i=low; int p1=low; int p2=mid+1; //进行遍历比较指针大小 while(p1<=mid && p2<=high){ /*** * 如果p1的值比p2小 * 那就把p1的值放在辅助数组中 * 反之 * 把p2的值放在辅助数组中 */ if(less(a[p1],a[p2])){ assist[i++]=a[p1++]; }else{ assist[i++]=a[p2++]; } } //检查数组的值是否遍历完全 while(p1<=mid){ assist[i++]=a[p1++]; } while(p2<=high){ assist[i++]=a[p2++]; } //将辅助数组的值拷贝到原数组中 for(int index=low;index<=high;index++){ a[index]=assist[index]; } } /*** * 比较大小 * @param comparable * @param comparable1 * @return */ private static boolean less(Comparable comparable, Comparable comparable1) { //小于0表示comparable<comparable1 return comparable.compareTo(comparable1)<0; } }
3.3 时间复杂度分析
这里建议用一个8个数列的排序手动排序一下;
首先看拆分了多少次:其实拆分过程就是一棵树,树的高度就是拆分了多少次:
第k层有个数组,每个数组的长度是,那每个数组需要比较多少次就是数组长度,那每一层需要比较的次数为:
那k层共比较:
时间复杂度为