说到这个问题呢,其实我们脑子里是不是第一感受是:
递归吗,自己调用自己咯。
在逻辑上讲,这没错,但事实又是如何呢,那我们进入第一个问题
我们先来看一段代码吧
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(nlog(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));
}
}
小和问题呢,个人感觉,难又不难,简单又不简单,虽然是写的不太好,但还是希望大家见谅吧。