排序算法:快排、堆排序、归并排序


几种排序的总结(含重复用元素): https://blog.csdn.net/weixin_46544385/article/details/107131501
快排一:
遇到相同的元素,元素可能在前半部分,也可能在后半部分。
8,8,0,9,4,10,5
8,0,9,4,10,8

最小的K个数(剑指LeetCode)

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k==0 || arr.length == 0){
            return new int[0];
        }
        return quickSort(arr,0,arr.length-1,k-1);
    }
    int[] quickSort(int[] arr,int low,int high,int k){
        int j = partition(arr,low,high);
        if(j==k){
            return Arrays.copyOfRange(arr,0,j+1);
        }
        return j>k?quickSort(arr,low,j-1,k):quickSort(arr,j+1,high,k);
    }
    int partition(int[] arr,int low,int high){//pivot是value,不是索引index
       int pivot = arr[low];
       int i = low,j = high+1;
       while(true){
           while(++i<=high && arr[i]<pivot);
           while(--j>=low && arr[j]>pivot);
           if(i>=j){
               break;
           }
           int tmp = arr[i];arr[i] = arr[j];arr[j]=tmp;
       }
       //最后用j交换第一个元素,为什么用j呢?因为j是走到arr[j]<=pivot停止的,j指向的元素<=pivot,可将j指向的元素调到第一个位置;
        arr[low] = arr[j];
        arr[j] = pivot;
        return j;
    }
}

剑指 Offer 45. 把数组排成最小的数

总结一:含重复元素的快排模板

在这里插入图片描述

题目:912. 排序数组

例子,8 15 4 9 0 8 4 10 8
重复元素可能在左边也可能在右边。

912 快速排序

class Solution {
    //快排
    public int[] sortArray(int[] nums) {
        int i = 0,j = nums.length-1;
        quickSort(nums,i,j);
        return nums;
    }
    public void quickSort(int[] nums,int begin,int end){
        if(begin < end){
            int mid = partition(nums,begin,end);
            quickSort(nums,begin,mid-1);
            quickSort(nums,mid+1,end);
        }
    }
    public int partition(int[] nums,int low,int high){
        int pivot = nums[low];
        int i = low, j= high;
        while(i<j){
            while(i<j && pivot <= nums[j]) j--;
            nums[i] = nums[j];
            while(i<j && pivot >= nums[i]) i++;
            nums[j] = nums[i];
        }
        nums[i] = pivot;
        return i;
    }
}

912 堆排序

class Solution {
    //堆排序
    //初建堆:完美二叉树的叶子节点是堆,从下往上建堆,当前节点的左子树和右子树都是堆
    //调整堆,每输出堆顶元素,就将堆从上往下调整
    //升序排列:小顶堆还是大顶堆取决于获取元素的方式
    public int[] sortArray(int[] nums) {
        createHeap(nums);
        return nums;
    }
    //初建堆
    public void createHeap(int[] nums){
        //初建堆
        for(int i=nums.length/2-1;i>=0;i--){
            adjustHeap(nums,i,nums.length-1);
        }
        //堆排序,每次交换根节点与最后一个节点,最后一个节点固定,不参与调整,并从根节点向下调整堆
        //从这可以看出:小顶堆得到降序,大顶堆得到升序
        for(int i=nums.length-1;i>=0;i--){
            int tmp = nums[i];
            nums[i] = nums[0];
            nums[0] = tmp;
            //堆的最后一个元素与堆顶元素交换
            adjustHeap(nums,0,i-1);
        }

    }
    //调整堆:调整以begin为根节点的堆,大顶堆
    //下标从0开始的完美二叉树,父节点为f,子节点分别为f*2+1,f*2+2
    public void adjustHeap(int[] nums,int begin,int end){
        int pivot = nums[begin];
        int s = 0,f = begin;//s代表son,f代表father
        for(s = f *2+1,f = begin;s <= end;){
            if(s<end && nums[s]<nums[s+1]) s++;//找最大的孩子下标
            if(pivot > nums[s]) break;//最大的孩子(两个孩子)小于要往下调整的pivot,则不再往下调整,接下来可令父节点的值为为pivot
            nums[f] = nums[s];//将较大的孩子换到父节点
            f = s;//父节点等于较大孩子的索引
            s = f * 2 + 1;//孩子的索引为父节点索引*2
        }
        nums[f] = pivot;//结束,父节点的值为pivot
    }
}

912 归并排序

class Solution {
    //归并排序,时间O(NlogN),空间O(N)
    //拆分数组,归并两个有序数组
    public int[] sortArray(int[] nums) {
        int[] tmp = new int[nums.length];
        mergeSort(nums,0,nums.length-1,tmp);
        return nums;
    }
    //归并排序
    void mergeSort(int[] nums,int begin, int end,int[] tmp){
        if(begin < end){
            int mid = begin + ((end - begin) >> 1);
            mergeSort(nums,begin,mid,tmp);
            mergeSort(nums,mid+1,end,tmp);
            merge(nums,begin,mid,end,tmp);
        }

    }
    //合并两个数组,并将合并的数组放到nums的对应位置
    void merge(int[] nums,int begin,int mid,int end,int[] tmp){
        //将nums中begin-end的元素复制到临时数组tmp中
        //System.arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
        System.arraycopy(nums,begin,tmp,begin,end-begin+1);
        //合并两个有序数组
        int i = begin,j = mid+1,k = begin;
        while(i<=mid && j <=end){
            if(tmp[i] <= tmp[j]) {//这里要写<=,不然会得到不稳定排序
                nums[k++] = tmp[i++];
            }else{
                nums[k++] = tmp[j++];
            }
        }
        while(i<=mid){
            nums[k++] = tmp[i++];
        }
        while(j<=end){
            nums[k++] = tmp[j++];
        }
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值