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

堆排的思路及实现代码

  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];
        }
    }
相关推荐
步、步、为营6 分钟前
.NET10:asp.net core mini api中的验证
java·asp.net·.net
孟大本事要学习10 分钟前
算法第15天:继续二叉树|前序递归+回溯与前序递归的场景总结、最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树
算法
GalaxyPokemon23 分钟前
LeetCode - 76. 最小覆盖子串
运维·服务器·数据结构·算法·leetcode
麦兜*26 分钟前
【为什么RabbitMQ能够控制事务?控制事务的原理】
java·rabbitmq·java-rabbitmq
嵌入式@秋刀鱼26 分钟前
《 第三章-招式初成》 C++修炼生涯笔记(基础篇)程序流程结构
linux·开发语言·数据结构·c++·笔记·visual studio code
温温top26 分钟前
java中合并音频
java·音视频
九转苍翎36 分钟前
Java SE(13)——工具类
java·工具类
手握风云-39 分钟前
动态规划算法的欢乐密码(二):路径问题
算法·动态规划
小马爱打代码40 分钟前
数据结构 - Java 队列
java·数据结构
盖世英雄酱581361 小时前
🚀不改SQL,也能让SQL的执行效率提升100倍
java·数据库·后端