数据结构算法-归并排序

从递归开始说起

在一个数组中查找最大值,先在left侧找max,在right侧找max,更大者就是整体的max;
使用递归就是使用系统栈,系统栈会自动帮我们把之前所有的信息压入栈中记录下来,从实际代码调试中看一看

public static void main(String[] args) {

        int[] nums = {3,6,1,21,7,4};
        System.out.println(getMax(nums, 0, nums.length-1));
    }

    public static int getMax(int[] nums,int left,int right){

        if(left==right)
            return nums[left];

        int middle = (right-left)/2+left;
        int maxL = getMax(nums, left, middle);
        int maxR = getMax(nums, middle+1, right);

        return maxL>maxR?maxL:maxR;

    }

这里写图片描述
这里写图片描述
这里写图片描述
当递归到left==right,即结束条件的时候,也就是left指针和right指针汇合了到一个点了,返回该值,就是一个数范围内的最大值;此时是getMax(nums,0,1)这个函数中的第18行执行完毕,返回maxL=3;
这里写图片描述
继续执行的是getMax(nums,0,1)这个函数中的第19行即maxR=getMax(nums,1,1),别忘了之前入栈的时候所有信息都被记录到了系统栈中~,返回maxR=6
这里写图片描述
执行完返回getMax(nums,0,1)这个函数的整体max=6;栈中最顶上的函数推栈,但是别忘了,此时栈顶函数是getMax(nums,0,1),是通过getMax(nums,0,2)调用的,其结果执行完返回到getMax(nums,0,2)中的maxL=6,栈顶元素退栈
这里写图片描述
然后执行getMax(nums,0,2)中的maxR,即getMax(nums,2,2),返回maxR=1,getMax(nums,0,2)的整体max就是6~getMax(nums,0,2)执行完推栈,此时仍需记得,getMax(nums,0,2)仅仅是getMax(nums,0,5)左侧部分的max,即getMax(nums,0,5)函数中maxL=6~
这里写图片描述
此时完成的就是left侧最大值,然后继续进行的就是getMax(nums,0,5)这个函数求右侧max的过程~maxR=getMax(nums,3,5),再走一遍递归最后可以求得getMax(nums,0,5)的maxR=21,然后整体max就是21,所有函数都执行完毕,推栈结束。

递归函数的时间复杂度公式
  • N 样本量
  • b 样本量被切分为几个子部分样本量,考虑的是规模,不具体到常数项,差几个无所谓
  • a 发生几次子过程
  • O(Nd) O ( N d ) 除去子过程之外的时间复杂度
    T(N)=aT(Nb)+O(Nd) T ( N ) = a T ( N b ) + O ( N d )

    本题的时间复杂度就是 T(N)=2T(N2)+O(1) T ( N ) = 2 T ( N 2 ) + O ( 1 )
  • logba>dO(Nlogba) l o g b a > d → O ( N l o g b a ) 比如 T(N)=2T(N2)+O(1) T ( N ) = 2 T ( N 2 ) + O ( 1 )
  • logba=dO(NdlogN) l o g b a = d → O ( N d l o g N ) 比如 T(N)=2T(N2)+O(N) T ( N ) = 2 T ( N 2 ) + O ( N )

归并排序

先把左侧排好序,再把右侧排好序,整体使用外排排好序列

归并排序代码
public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        mergeSort(arr, 0, arr.length - 1);
    }

    public static void mergeSort(int[] arr, int l, int r) {
        if (l == r) {
            return;
        }
        int mid = l + ((r - l) >> 1);
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

    public static void merge(int[] arr, int l, int m, int r) {
        int[] help = new int[r - l + 1];
        int i = 0;
        int p1 = l;
        int 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 (p2 <= r) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
        //l+i,不然排右半部分的时候就崩了
            arr[l + i] = help[i];
        }
    }

排序部分在归并的过程中,先把(0,1)的元素排序+归并,然后把(2,2)的元素排序+归并,然后在(0,2)范围内整体排序+归并,此时完成左半部分的排序,然后是右半部分(3,5)归并排序,在(3,4)归并排序,然后是(5,5)归并排序,然后把有半部分整体排好序+归并。最后是整体大范围(0,5)范围内归并排序了~

归并排序时间复杂度 O(NlogN) O ( N l o g N )

除去子过程外要遍历数组,因此是O(N)

T(N)=2T(N2)+O(N) T ( N ) = 2 T ( N 2 ) + O ( N )

logba=dO(NdlogN) l o g b a = d → O ( N d l o g N )
最后求的 O(NlogN) O ( N l o g N )

求数组小和

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。比如[1,3,4,2,5]的小和就是
1的左边比1小的数,没有 0;
3的左边比3小的数,1和为1;
4的左边比4小的数,1+3=4;
2的左边比2小的数,1;
5的左边比5小的数;1+2+3+4=10
总共小和为16
换个思路:
cur这个数右边有多少个比cur大,就加上几个cur;
1的右边有4个比1大,即+4;
3的右边有2个比3大,即+(3*2)
4的右边有1个比4大,即+4
2的右边有1个比2大,即+2
5的右边没有+0;
整体小和为4+6+4+2=16
在归并的过程中,先看1怎么算得,最开始就是1,然后(1,3)排序合并,然后(1,3)和4排序合并,然后还有(1,3,4)和(2,5)合并,这样就把1算完毕;然后看3怎么算得,(1,3)和4排序合并,然后还有(1,3,4)和(2,5)合并,这样3就算完毕;然后看4怎么算,(1,3,4)和(2,5)合并;
然后看2怎么算,(2,5),最后到5结束。。。。
这个过程其实和归并排序的那个归并过程是一样的,只不过在归并的时候算了个累加;

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;
        int p1 = l;
        int 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 (i = 0; i < help.length; i++) {
            arr[l + i] = help[i];
        }
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值