认识O(NlogN)的排序

155 阅读1分钟

求中点:mid=L+(R-L)>>1

image.png

递归

在范围内求最大值: image.png

递归行为时间复杂度估算:master公式

image.png

归并排序

归并排序:将数组分成两部分,分别对两部分进行排序,将排好序的两部分利用双指针合并成一个数组(需要一个外部空间),最后将外部空间的数组拷贝到原数组中。对每部分的排序采用递归的方式进行。

代码实现:


public class MergeTest {
    public static void main(String[] args) {
        int[] arr={2,3,5,1,7,4};
        //测试
        process(arr,0,arr.length-1);
        for (int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }
    }
    public static void process(int[] arr,int L,int R){
        if(L==R){
            return;
        }
        int mid=L+((R-L)>>1); //异或运算求中点
        //递归调用
        process(arr, L, mid);
        process(arr, mid+1, R);
        merge(arr,L,mid,R);
    }
    //利用辅助空间进行排序操作
    public static void merge(int[] arr,int L,int M,int R){
        //定义一个辅助空间用来存放merge后的数组
        int[] help=new int[R-L+1];
        int i=0,p1=L,p2=M+1;
        //都不越界
        while (p1<=M && p2<=R){
            //双指针,如果左侧小于等于右侧,就把左侧数据放入辅助空间中,反之亦然
            help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
        }
        //左侧未越界,说明右侧越界了,则把左侧剩余的数据直接放到辅助空间
        while (p1<=M){
            help[i++]=arr[p1++];
        }
        //同理,两个while只会执行一个
        while (p2<=R){
            help[i++]=arr[p2++];
        }
        //从L开始
        for (int j=0;j< help.length;j++){
            arr[L+j]=help[j];
        }

    }
}

合并有序数组

力扣T88

题目描述:

image.png

题解:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p1=0,p2=0;
        int res[m+n];
        int cur;

        while(p1<m || p2<n){
            if(p1==m){
                cur=nums2[p2++];
            }else if(p2==n){
                cur=nums1[p1++];
            }else if(nums1[p1]<nums2[p2]){
                cur=nums1[p1++];
            }else{
                cur=nums2[p2++];
            }

            res[p1+p2-1]=cur;
        }
        for(int i =0;i!=m+n;i++){
            nums1[i]=res[i];
        }
    }
};

小和问题

小和问题:在一个数组中,每个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。

例子:数组[1,3,4,2,5]中,1左边比1小的数,没有;3左边比3小的数,1;4左边比4小的数,1、3;2左边比2晓得数,1;5左边比5小的数,1、3、4、2;所以小和为1+1+3+1+1+3+4+2=16。

解法思路:采用递归的方式分为左右两个数组,则左边的数组产生的小和+右边的数组产生的小和+左右数组合并产生的小和就为最终的结果。

public class SmallSum {
    public static void main(String[] args) {
        int[] arr={3,6,8,4,5};
        int ss=smallSum(arr);
        System.out.println(ss);
    }

    public static int smallSum(int[] arr){
        if(arr==null || arr.length<2){
            return 0;
        }
        return mergeSort(arr,0,arr.length-1);
    }

    public static int mergeSort(int[] arr,int l,int r){
        if(l==r){
            return 0;
        }
        int mid=l+((r-l)>>1);
        return mergeSort(arr,l,mid)+
                mergeSort(arr,mid+1,r)+
                merge(arr,l,mid,r);
    }
    public static int merge(int[] arr,int l,int m,int r){
        //辅助空间
        int[] help=new int[r-l+1];
        int i=0,p1=l,p2=m+1;
        int res=0;

        //都不越界
        while (p1<=m && p2<=r){
            //边界值判断,如果左右两侧值相等的话,应该是右侧指针右移
            res+=arr[p1]<arr[p2]?(r-p2+1)*arr[p1]:0;
            help[i++]=arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
        }
        //有一侧越界
        while (p1<=m){
            help[i++]=arr[p1++];
        }
        while (p2<=r){
            help[i++]=arr[p2++];
        }

        for (int j=0;j<help.length;j++){
            arr[l+j]=help[j];
        }
        return res;
    }
}

逆序对问题

剑指Offer 51

题目描述:

image.png

题解:


public class nxd {
    public static void main(String[] args) {
        int[] arr={7,5,6,4};
        int ss=smallSum(arr);
        System.out.println(ss);
    }

    public static int smallSum(int[] arr){
        if(arr==null || arr.length<2){
            return 0;
        }
        return mergeSort(arr,0,arr.length-1);
    }

    public static int mergeSort(int[] arr,int l,int r){
        if(l==r){
            return 0;
        }
        int mid=l+((r-l)>>1);
        return mergeSort(arr,l,mid)+
                mergeSort(arr,mid+1,r)+
                merge(arr,l,mid,r);
    }
    public static int merge(int[] arr,int l,int m,int r){
        //辅助空间
        int[] help=new int[r-l+1];
        int i=0,p1=l,p2=m+1;
        int res=0;

        //都不越界
        while (p1<=m && p2<=r){
            //边界值判断,如果左右两侧值相等的话,应该是右侧指针右移
            res+=arr[p1]>arr[p2]?(m-p1+1):0;
            help[i++]=arr[p1]>arr[p2]?arr[p2++]:arr[p1++];
        }
        //有一侧越界
        while (p1<=m){
            help[i++]=arr[p1++];
        }
        while (p2<=r){
            help[i++]=arr[p2++];
        }

        for (int j=0;j<help.length;j++){
            arr[l+j]=help[j];
        }
        return res;
    }
}