手撕 快速排序 / 归并排序

链接
题目:
在这里插入图片描述

1. 快速排序

基本思想:
通过一趟排序,以基准数字为界,将数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快速排序的详细原理

1.1 时间复杂度:

平均时间复杂度是 O(n logn)

最好情况:每一次切分选择的基准数字刚好将当前序列等分成两部分;如果把数组的切分看做是一个树,那么下图就是它的最优情况的图示,共切分了O(logn)次,所以,最优情况下快速排序的时间复杂度为O(n logn);
在这里插入图片描述

最坏情况: 每一次切分选择的 基准数字是当前序列中最大数或者最小数,这使得每次切分都只比上次少一个元素,那么总共就得切分n次,所以,最坏情况下,快速排序的时间复杂度为O(n^2);

注意:

  1. 先走指针,再走左指针 !最后返回的partition也是右指针;
  2. sort 是递归分界;partition是返回新的基准值,递归拆分时不算上partiton分界值!
    sort(nums,lo,partition-1);
    sort(nums,partition+1,hi);
    而归并排序中:
    sort(nums,lo,mid); // 是mid而不是mid-1
    sort(nums,mid+1,hi);
class Solution {
    public int[] sortArray(int[] nums) {
        int lo=0;
        int hi=nums.length-1;
        sort(nums,lo,hi);
        return nums;
    }
	// 以分界值为界分割数组
    void sort(int[] nums,int lo,int hi){
        if(lo>=hi){ 
            return;
        }
        //前序遍历 得到分界值索引
        int partition=partition(nums,lo,hi);
        sort(nums,lo,partition-1); // 递归
        sort(nums,partition+1,hi);
    }

	// 返回新的分界值、同时排序
	// 指针从两边往中间走!
    int partition(int[] nums,int lo,int hi){
        // 分界值
        int key=nums[lo];
        //指针
        int left=lo; // 第一个是分界值要跳过
        int right=hi+1;
        while(true){
	        // 先走右 !直到小于key则等待交换
            while(nums[--right]>key){ 
                if(left>=right){
                    break; // 越界跳出while
                }
            }
            // 再走左 ! 直到大于Key则等待交换
            while(nums[++left]<key){ // 再走左 !
                if(left>=right){
                    break; // 越界跳出while
                }
            }
            // 指针重合则跳出while(true)
            if(left>=right){ 
                break;  
            }
            swap(nums,left,right); // ※
        }
        // 跳出while(true),首元素和指针重合元素(分界节点)
        swap(nums,lo,right);
        return right; // 返回必须右边
    }

    void swap(int[] nums,int i,int j){
        int temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
}

2. 归并排序:

基本思想:
先拆分数组为两个子序列,然后将已有序的子序列合并,得到完全有序的序列;
即先使每个子序列有序,再使子序列段间有序;

注意: lo、mid、hi 都是索引 !在 merge中判断时有等号 !!!

2.1 时间复杂度:

每次固定以mid值来切分,所以切分的复杂度是O(logn),所以总共是O(n logn)
注意辅助数组的索引从 lo 开始,而不是从0 开始!

class Solution {
    // 辅助数组
    int[] s;
    public int[] sortArray(int[] nums) {
        s=new int[nums.length];
        int lo=0;
        int hi=nums.length-1;
        sort(nums,lo,hi);
        return nums;
    }

    void sort(int[] nums,int lo,int hi){
        // base case
        if(lo>=hi){
            return;
        }
        int mid=lo+(hi-lo)/2;
        sort(nums,lo,mid); // mid而不是mid-1
        sort(nums,mid+1,hi);
        //后序遍历
        merge(nums,lo,mid,hi);
    }
    //合并两个数组
    void merge(int[] nums,int lo,int mid,int hi){
        // 三个指针
        int i=lo;
        int p1=lo;
        int p2=mid+1;
        while( p1<=mid && p2<=hi ){
            if(nums[p1]<=nums[p2]){
                s[i++]=nums[p1++];
            }else{
                s[i++]=nums[p2++];
            }
        }
        // 有一边到头,到头的指针最后会++,超过范围
        while(p1<=mid){
            s[i++]=nums[p1++];
        }
        while(p2<=hi){
            s[i++]=nums[p2++];
        }

        //拷贝回原数组  注意j<=hi而不是< !
        for(int j=lo;j<=hi;j++){
            nums[j]=s[j];
        }
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值