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

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

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

递归问题的深入探讨

我们先来看一段代码吧

#include<iostream>
#include<algorithm>
using namespace std;

int Maxnumber(int Array[],int left,int right){
	if(left==right){
		return Array[left];
	}else{
		int Mid=(left+right)/2;
		return max(Maxnumber(Array,left,Mid),Maxnumber(Array,Mid+1,right));
	}
}


int main(){
	int Array[]={12,34,1,5};
	int lenght=sizeof(Array)/sizeof(int);
	cout<<Maxnumber(Array,0,lenght-1);
	return 0;
}

其实我们不难发现,其实递归问题的算法可以改成非递归算法。
那递归的实质又是什么呢?
递归的实质是栈的自动的压入和弹出
不懂的话,请参见栈的压入和弹出
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)<d =>O(n^d);
master前提:你划分的子过程的规模是一样的。

归并排序

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

话不多说,上代码吧:

#include <iostream>
using namespace std;

void merge(int a[], int b[], int l, int m, int r)
{
    int i = l;
    int j = m + 1;
    int 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 {
                b[k++] = a[i++];
            }
        }
    }

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

void merge_sort_helper(int a[], int b[], int l, int r)
{
    if (l >= r) {
        return;
    }
    int m = (l + r)/2;
    merge_sort_helper(a, b, l, m);
    merge_sort_helper(a, b, m + 1, r);
    merge(a, b, l, m, r);
}

void merge_sort(int a[], int len)
{
    int *b = new int[len];
    merge_sort_helper(a, b, 0, len - 1);
    delete[] b;
}

int main()
{
    int a[] = {1, 1, 6, 6, 8, 3, 5, 100, 300, 200, 99, 99};
    merge_sort(a, sizeof(a)/sizeof(a[0]));
    
    for(int i=0;i<sizeof(a)/sizeof(a[0]);i++){
    	cout<<a[i]<<" ";
	}
    return 0;
}

小和问题算法问题

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

[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

算了,上代码吧:

#include <iostream>
using namespace std;

int merge(int a[], int b[], int l, int m, int r)
{
	int Sum=0;
    int i = l;
    int j = m + 1;
    int 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;
}

int merge_sort_helper(int a[], int b[], int l, int r)
{
    if (l >= r) {
        return 0;
    }
    int m = (l + r)/2;
    return merge_sort_helper(a, b, l, m)+merge_sort_helper(a, b, m + 1, r)+merge(a, b, l, m, r);
}

int merge_sort(int a[], int len)
{
    int *b = new int[len];
    int Liltlesum=merge_sort_helper(a, b, 0, len - 1);
    delete[] b;
    return Liltlesum;
}

int main()
{
    int a[] = {1,3,4,2,5};
    cout<<merge_sort(a, sizeof(a)/sizeof(a[0])); 
    return 0;
}

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));
    }

}

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

发布了27 篇原创文章 · 获赞 1 · 访问量 1050

猜你喜欢

转载自blog.csdn.net/qq_45205390/article/details/103467883