算法学习笔记2-递归问题的深入探讨/归并排序算法/小和问题算法

算法学习笔记-递归问题的深入探讨/归并排序算法/小和问题算法

说到这个问题呢,其实我们脑子里是不是第一感受是:
递归吗,自己调用自己咯。
在逻辑上讲,这没错,但事实又是如何呢,那我们进入第一个问题

递归问题的深入探讨

我们先来看一段代码吧

    int GetMax(int*Array,int Leftindex,int Rightindex){
        if(Leftindex==Rightindex)return Array[Leftindex];
        int minndex=(Leftindex+Rightindex)/2;
        return max(GetMax(Array,Leftindex,minndex),GetMax(Array,minndex+1,Rightindex));
    }

    int GetMin(int*Array,int Leftindex,int Rightindex){
        if(Leftindex==Rightindex)return Array[Leftindex];
        int minndex=(Leftindex+Rightindex)/2;
        return min(GetMin(Array,Leftindex,minndex),GetMin(Array,minndex+1,Rightindex));
    }

其实我们不难发现,其实递归问题的算法可以改成非递归算法。
那递归的实质又是什么呢?
递归的实质是栈的自动的压入和弹出
不懂的话,请参见栈的压入和弹出。
master公式及其应用

t(n)=at(n/b)+O(n^d);
n为数据总量,b为平均分成几份,a为出现的次数,O(n^d)为额外时间复杂度
if(log(b,a))>d=>O(n
log(b,a);
else if(log(b,a)==d)=>O(n^d*log(b,a));
else log(b,a)O(n^d);
master前提:你划分的子过程的规模是一样的。

归并排序

对于上面的问题就说这么多吧,我们来思考一下,我们对于排序应该有一定理解吧,那相信大家一定也对归并排序有一定理解吧,那请允许我在这里给大家总结一下吧。
其实我们可以把归并排序看成二分法的用法之一,先左侧排好序,在右侧排好序,再用一个辅助数组进行操作。那它的时间复杂度又是多少呢。
它的时间复杂度为:O(nlog(b,a))*

话不多说,上代码吧:

 void MergerSort(int Array[], int Lenght){
        int *Arrayhelp = new int[Lenght];
        mergeSortHelper(Array, Arrayhelp, 0, Lenght - 1);
        delete[] Arrayhelp;
    }
 void Merger(int Array[], int Arrayhelp[], int Left, int Mid, int Right){
        int i = Left,j = Mid + 1,k = Left;
        while (k <= Right) {
            if (i > Mid)Arrayhelp[k++] = Array[j++];
            else if (j > Right)Arrayhelp[k++] = Array[i++];
            else Arrayhelp[k++]=Array[i]>Array[j]?Array[j++]:Array[i++];
        }
        for (int k = Left; k <= Right; k++)Array[k] = Arrayhelp[k];
    }
 void mergeSortHelper(int Array[], int Arrayhelp[], int Left, int Right){
        if (Left >= Right) return;
        int Mid = (Left + Right)/2;
        mergeSortHelper(Array, Arrayhelp, Left, Mid);
        mergeSortHelper(Array, Arrayhelp, Mid + 1, Right);
        Merger(Array, Arrayhelp, Left, Mid, Right);
    }   

小和问题算法问题

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

[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

那思路又是什么呢:

这道题换个角度来想,题目要求的是每个数左边有哪些数比自己小,其实不就是右边有多少个数比自己大,那么产生的小和就是当前值乘以多少个吗?还是以上面的样例举例,1右边有4个比1大的数,所以产生小和14;3右边有2个比3大的数,所以产生小和32;4右边有一个比4大的数,所以产生小和41;2右边没有比2大的数,所以产生小和为20;5右边也没有比5大的数,所以产生小和5*0

算了,上代码吧:

    int LittleSums(int a[], int len){
        int *b = new int[len];
        int Liltlesum=littlesum(a, b, 0, len - 1);
        delete[] b;
        return Liltlesum;
    }
    
     int mergeSum(int a[], int b[], int l, int m, int r){
	    int Sum=0,i = l,j = m + 1,k = l;
        while (k <= r) {
            if (i > m) b[k++] = a[j++];
            else if (j > r) b[k++] = a[i++];
            else {
                if (a[i] > a[j]) b[k++] = a[j++];
                else {
            	    Sum+=a[i]*(r-j+1);
                    b[k++] = a[i++];
                }
            }
        }

        for(int k = l; k <= r; k++) a[k] = b[k];
        return Sum;
    }   

java代码实现:

class SmallSum {
    public static int smallSum(int[] arr) {
        //如果数组为空,或者数组长度小于2,直接返回
        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 mid, int R) {
        //辅助数组
        int[] help = new int[R - L + 1];
        //辅助数组下标
        int i = 0;
        //左半边数组的指针
        int p1 = L;
        //右半边数组的指针
        int p2 = mid + 1;
        //小和
        int res = 0;
        //指针没有越界
        while(p1 <= mid && p2 <= R) {
            //如果左边指向的值小于右边指向的值,那么p1位置的值一定小于p2以后的所有值,因为是有序的,这时候产生小和
            if (arr[p1] < arr[p2]) {
                //计算小和
                res = res + arr[p1] * (R - p2 + 1);
                //排序过程
                help[i++] = arr[p1++];
            }else {
                //不产生小和
                res = res + 0;
                //排序过程
                help[i++] = arr[p2++];
            }
        }
        //p1没有越界,说明p2越界了,将左边剩余元素拷贝到辅助数组
        while(p1 <= mid) {
            help[i++] = arr[p1++];
        }
        //p2没有越界,说明p1越界了
        while(p2 <= R) {
            help[i++] = arr[p2++];
        }
        //将辅助数组元素拷贝会原数组
        for(int j = 0; j < help.length; j++) {
            arr[L + j] = help[j];
        }
        return res;
    }
    public static void main(String[] args) {
        int[] arr = {1,3,4,2,5};
        System.out.println(smallSum(arr));
    }

}

小和问题呢,个人感觉,难又不难,简单又不简单,虽然是写的不太好,但还是希望大家见谅吧。

你可能感兴趣的:(算法学习笔记,算法)