七大排序之堆排、快排及优化、归并排序

堆排的思路及实现代码

  1. 建立大根堆,使得堆顶的元素,也就是数组首元素为当前数组最大值
  2. 交换最大值到队尾,并且endIndex--
  3. 将剩余的n-1个元素重新建立大根堆,主要是对刚刚交换到首元素位置的元素进行下沉操作
  4. 重复执行2,3操作,直到endIndex下标指向首元素
java 复制代码
 public int[] heapSort(int[] arr) {//堆排序
     createBigHeap(arr);
     int endIndex=arr.length-1;
     while(endIndex>=0){
         swap(arr,0,endIndex);
         siftDown(arr,0,endIndex);
         endIndex--;
     }
     return arr;
 }
 public static void createBigHeap(int[] arr){
     for(int parent=(arr.length-2)/2;parent>=0;parent--){
         siftDown(arr,parent,arr.length);
     }
 }
 public static void siftDown(int[]arr,int parent,int end){
     int child=parent*2+1;
     while(child<end){
         if(child+1<end&&arr[child]<arr[child+1]){
            //这里的end条件需要用上,不能用arr.length,因为在堆排方法里,需要用到
             child++;
         }
         if(arr[parent]<arr[child]){
             swap(arr,parent,child);
             parent=child;
             child=parent*2+1;
         }else{
             break;
         }
     }
 }

注:这里shiftDown方法是向下操作,将给定 的parent向下比较,所以一轮交换之后还需要,将被交换的子节点设置为parsent,再次向下比较

快排的思路及代码实现

  1. 使用hoare/挖坑法选择基准值,将数组分为两部分,左序列都小于基准值,右序列都大于基准值
  2. 然后左右序列都分别重复1操作,直到序列长度为1时递归结束
  3. hoare思路:将传过来的数组首元素保存为基准值,right指针后面向前开始比较找到小于基准值停止,再left指针从前向后找大于基准值的数,两个数都找到就交换,完成后继续开始查找,直到两个找元素下表相遇,就将基准值和right left相遇的值交换
  4. 挖坑法思路: 和hoare的思路差不多少,不同的是right找到小于基准值下标后,直接将值给left,同样left找到之后同样将值给right,最后两个下标相遇之后就将基准值给相遇的下标
java 复制代码
 public int[] quickSort(int[]arr){//hoare/挖坑
        quick(arr,0,arr.length);
        return arr;
    }
    public void quick(int[]arr,int start,int end){
        if (start>end){
            return;
        }
        int index=partition(arr,start,end);
        quick(arr,start,index-1);
        quick(arr,index+1,end);

    }
    public int partition(int[]arr,int left,int right){//hoare法
        int i=left;
        int pivot=arr[left];
        while (left<right){
            while (left<right&&arr[right]>=pivot){
                right--;
            }
            while (left<right&&arr[left]<=pivot){
                left++;
            }
            swap(arr,left,right);
        }
        swap(arr,i,left);
        return left;
    }


    public int partition2(int[]arr,int left,int right) {//挖坑法
        int i=left;
        int pivot=arr[left];
        while (left<right){
            while (left<right&&arr[right]>=pivot){
                right--;
            }
            arr[left]=arr[right];//找到直接覆盖到另一个下标
            while (left<right&&arr[left]<=pivot){
                left++;
            }
            arr[right]=arr[left];//找到直接覆盖到另一个下标
        }
        arr[left]=pivot;
        return left;
    }

优化思路:

  1. 在最理想的情况下,每次都可以将通过partition方法找到最中间的数返回给index,在通过递归分为两个子数组,可以写一个三数取中法,将排序序列的首、尾和中间三个位置的元素,然后取这三个元素中的中位数作为基准值。这样做的好处是,即使输入序列已经部分有序,也能够得到一个相对较好的划分,从而减少递归的深度和不平衡的可能性,进而提升算法的性能。
  2. 在递归到数组总长小于10(经验推断值),采用插入排序法方法因为快速排序在处理小数据集时,由于递归调用和划分操作带来的开销,不如插入排序这样的简单排序算法效率高。

优化后的快排

java 复制代码
 public static void quickSort(int[] arr,int start,int end){
        if(start>=end){
            return;
        }
        if(start-end-1==10){
            insertSort(arr,start,end);
            return;
        }
        int Index=midTreeNum(arr,start,end);
        swap(arr,start,Index);
        int par=partition(arr,start,end);
        quickSort(arr,start,par-1);
        quickSort(arr,par+1,end);
    }

    public static int partition(int[] arr,int left,int right){
        int i=left;
        int pivot=arr[left];
        while(left<right){
            while(left<right&&arr[right]>=pivot){
                right--;
            }
            while(left<right&&arr[left]<=pivot){
                left++;
            }
            swap(arr,left,right);
        }
        swap(arr,i,left);
        return left;
    }
    public static void insertSort(int[] arr,int left,int right) {//插排
        for(int i=left;i<right;i++){
            int tmp=arr[i];
            int j=i-1;
            for(;j>=right;j--){
                if(arr[j]>tmp){
                    arr[j+1]=arr[j];
                }else{
                    break;
                }
            }
            arr[j+1]=tmp;
        }
    }
    
    public static int midTreeNum(int[]arr,int left,int right){
        int mid=(left+right)/2;
        if(arr[left]<arr[right]){
            if(arr[mid]<arr[left]){
                return left;
            }else if(arr[mid]>arr[right]){
                return right;
            }else{
                return mid;
            }
        }else{
            if(arr[mid]<arr[right]){
                return right;
            }else if(arr[mid]>arr[left]){
                return left;
            }else{
                return mid;
            }
        }
    }

归并排序思路及代码实现

  1. 将一个待排序的数组不断地二分,直到每个子数组只剩下一个元素为止。
  2. 然后将这些子数组视为已排序的数组,并依次将相邻的两个子数组用merge方法合并成一个有序的数组,直到最终合并成一个完整的有序数组。
    merage实现思路:需要创建一个临时数组来存放合并后的序列。合并时,通过比较两个子数组中的元素,将较小的元素依次放入临时数组中,直到其中一个子数组的所有元素都放入临时数组。然后将另一个子数组中剩余的元素直接放入临时数组的末尾。最后,将临时数组中的元素复制回原始数组,完成一次合并操作。
java 复制代码
public int[] Sort(int[]arr){
        mergeSort(arr,0,arr.length-1);
        return arr;
    }

    public void mergeSort(int[]arr,int start,int end){
        if(start>=end){
            return;
        }
        int midIndex=(end+start-1)/2;//注意是加
        mergeSort(arr,start,midIndex);
        mergeSort(arr,midIndex+1,end);
        merge(arr,start,midIndex,end);

    }
    public void merge(int[]arr,int start,int midIndex,int end){
        int s1=start;
        int e1=midIndex;
        int s2=midIndex+1;
        int e2=end;
        int k=0;
        int[]tmp=new int[end-start+1];
        while (s1<=e1&&s2<=e2){
            if(arr[s1]<=arr[s2]){
                tmp[k++]=arr[s1++];
            }
            if(arr[s1]>=arr[s2]){
                tmp[k++]=arr[s2++];
            }
        }
        while (s1<=e1){
            tmp[k++]=arr[s1++];
        }
        while (s2<=e2){
            tmp[k++]=arr[s2++];
        }

        for(int i=0;i<k;i++){
            //arr[start++]=tmp[i];
            arr[i+start]=tmp[i];
        }
    }
相关推荐
Amor风信子3 分钟前
【力扣】2376. 统计特殊整数
算法·leetcode·职场和发展
极客小张3 分钟前
基于正点原子Linux开发板的智能监控与家电控制系统设计:深度解析Video4Linux和TCP/IP技术栈
linux·运维·c++·物联网·网络协议·tcp/ip·算法
JustCouvrir1 小时前
代码随想录算法训练营Day5
算法
一只特立独行的猪6111 小时前
Java面试——集合篇
java·开发语言·面试
讓丄帝愛伱2 小时前
spring boot启动报错:so that it conforms to the canonical names requirements
java·spring boot·后端
weixin_586062022 小时前
Spring Boot 入门指南
java·spring boot·后端
周哈里窗的编程2 小时前
CSP-CCF★201912-2回收站选址★
c++·算法·图论
SpongeG3 小时前
数据结构第三周做题总结_链表
数据结构·算法·链表
everyStudy3 小时前
前端五种排序
前端·算法·排序算法
小珑也要变强3 小时前
队列基础概念
c语言·开发语言·数据结构·物联网