博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
摒除一切封建迷信之递归并不难------排序2
阅读量:3936 次
发布时间:2019-05-23

本文共 3889 字,大约阅读时间需要 12 分钟。

剖析递归行为和递归行为时间复杂度的估算

一个递归行为的例子

master公式的使用

T(N) = a*T(N/b) + O(N^d)

1) log(b,a) > d -> 复杂度为O(N^log(b,a))

2) log(b,a) = d -> 复杂度为O(N^d * logN)

3) log(b,a) < d -> 复杂度为O(N^d)

补充阅读:www.gocalf.com/blog/algorithm-complexity-and-mastertheorem.html

T(N)表示父问题样本量,T(N/b)表示被拆成子问题的样本量,a表是一共发生多少次,O(n^d)表示除去调用子过程之外,剩下的代价是多少。

什么时候用master公式:划分成子过程的规模是一样的

举例:从一个数组中找最大值

思路:将数组分为左右俩部分,左边(L)最大值和右边(R)最大值比较,最大的数就是最大值。具体代码如下:

public static void main(String[] args) {    int[] arr = {4,3,2,1};    System.out.println(getMax(arr,0,arr.length-1));}private static int getMax(int[] arr, int L, int R) {    if(L == R){        return arr[L];    }    int mid = (L+R)/2;    //maxLeft这个参数指望着   getMax方法返回     保存所有的递归信息,将所有的参数,所有的变量压入栈中    int maxLeft = getMax(arr, L, mid);        int maxRight = getMax(arr, mid + 1, R);    return Math.max(maxLeft,maxRight);}

 

递归行为时间复杂度

式子一:T(N)= aT(n/b)+O(n^d)

等式左边表示样本大小为n的时间复杂度。

在找出数组最大值的过程中,有等式T(N)= 2*T(n/2)+O(1),样本量为N/2的过程跑了2次,当跑完这个子过程后,比对较大的数并且返回,是一个常数操作,时间复杂度为O(1)。

所以式子一中  T(n)表示原始大小样本量,n/b 表示子过程的样本量,O(n^d)表示除去调用子过程之外,剩下的代价是多少。

所以,例子中a=2,b=2,d=0

 

归并排序的细节讲解与复杂度分析

时间复杂度O(N*logN),额外空间复杂度O(N)

归并排序在上面讲解了递归行为就很简单了。一个数组先左侧部分排好序,再右侧部分排好序。然后整体利用外排的方式排好序。

假设一个数组:5,3,6,)2,0,1,

0)先左侧排好序,再右侧排好序,整体再排好序。左侧部分排好序是3,5,6,而右侧部分排好序是0,1,2,接下来整体排序。

1)准备一个辅助数组,采用外排方式

2)左边部分准备一个下标a指向3,右边准备一个下标b指向0,哪一个小,就向辅助数组插入

则分析公式T(N)= aT(n/b)+O(n^d),其中a=2,b=2,d=1,所以归并排序时间复杂度为O(N*logN)

具体代码如下:

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;   //准备左侧数组p1指针,指向左侧数组第一个数(即p1是左侧数组最小的值)   int p1 = l;   //准备右侧数组p2指针,指向右侧数组第一个数(即p2是右侧数组最小的值)   int p2 = m + 1;   //这个循环表示谁小,填谁   while (p1 <= m && p2 <= r) {      help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];   }   //当p2已经到达数组末尾,继续填充p1,俩个必有且只有一个越界   while (p1 <= m) {      help[i++] = arr[p1++];   }   while (p2 <= r) {      help[i++] = arr[p2++];   }   //将help数组填回arr   for (i = 0; i < help.length; i++) {      arr[l + i] = help[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

逆序对问题

在一个数组中,左边的数如果比右边的数大,则折两个数构成一个逆序对,请打印所有逆序对。

这种题可以用归并排序做出解,问题的实质:如果当前数是current,我们找的是,我们查找右边有多少个数比current大,个数*current

举例:

数组1,3,4,2,5

1)先分成左右部分,长度为奇数,左部分可以是3个,也可以是2个,假设分成左边3个。左边1,3,4,右边2,5

2)1,3,4再分治,分为1,3和4;2,5拆成2和5

3)1,3分为1,3

4)merge过程中求所有的小和。

5)准备辅助数组,拷贝1,3,计算小和   1

6)merge1,3和 4,准备一个辅助数组, 计算小和   1,3

右侧部分同理,产生小和2

左侧1,3,4,右侧2,5进行最后merge过程产生小和,

1)准备一个辅助数组,左侧a指向1,右侧b指向2

2)a比b小,在右侧有  多少个比1   大  ,包括2在内有2个数  ,产生  2*1的小和

3)a来到3位置,b是2,a是3,不产生任何小和,然后b来到5位置

4)3比5小,产生一个 1*3 的小和,同时a来到4位置

5)产生一个  1*4 的小和

具体代码如下:

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) {//如果p1比p2小,产生的小和      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;}

除了红色部分,小和问题和归并排序代码一模一样

能够形成1,2,3说明1在当时merge的时候已经计算了

转载地址:http://jnuwi.baihongyu.com/

你可能感兴趣的文章
二叉树根节点到叶子节点的路径数字之和
查看>>
根节点到叶子节点的节点值之和等于 sum的路径
查看>>
判断二叉树是否有从根节点到叶子节点的节点值之和等于sum的路径
查看>>
反转字符串
查看>>
环形链表
查看>>
删除链表的倒数第N个节点
查看>>
回文链表
查看>>
容器盛水问题
查看>>
滑动窗口最大值
查看>>
win7 文件删除后要刷新后才会消失
查看>>
用ffmpeg转多音轨的mkv文件
查看>>
ubuntu12.04 安装VLC,在root用户下不能使用的问题
查看>>
简单而又完整的Makefile
查看>>
GNU/Linux下如何卸载源码安装的软件
查看>>
ffmpeg 常用 命令随手记
查看>>
av_seek_frame中flags值的意义
查看>>
git 学习笔记
查看>>
C++类中的static的用法
查看>>
vector 释放内存 swap
查看>>
在linux下新增一块硬盘的操作。(包含大于2T的硬盘在linux下挂载操作)
查看>>