大大控

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

最近学习了这个基础算法原型,最长递增子序列,所谓子序列,就是可以不连续,去除数组中任意元素获得的子数组都是子序列。

举个栗子,现在有数组arr,长度为5,分别是10,4,5,12,8

那么这个数组的最长递增子序列长度为3。

解法一:

动态规划思想,原问题分成子问题,那么自问题就是到结尾为arr[i]时的最大递增子序列长度。

创建一个辅助数组dp,和原数组长度相等,这个数组中存放的是以arr[i]结尾的时候,获得的最大递增子序列长度。

还是上面的栗子,arr数组中的元素为10,4,5,12,8,

那么dp[0]=1,这个子序列数字10本身

dp[1]=1,因为4比10小,再来看dp[2],这时候5>4,但是比10小,所以dp[2]=dp[1]+1。

dp[3],12比4和5都大,所以dp[3]=max(dp[1],dp[2])+1……最终得出dp数组为1,1,2,3,3

所以得到结果3。这种解法时间复杂度O(n²)

解法二:

解法二属于那种打死我我自己也想不出那种,同样的,这种方法也需要一个辅助数组,我们还叫做dp好了。

但是这次dp存放的不一样了,dp这次存放的是当最长递增子序列为i是,这个序列的最小结尾,注意了,是结尾!

继续看上面的栗子,10,4,5,12,8,

dp[0]=10,先把第一个放进去,因为当我们检索到10的时候,最长子序列长度为1,且最小结尾是10,所以存放这个10,

遍历到4的时候,我们发现,最长长度还是1,但是4比10小,所以4替换掉10,

继续遍历,5的时候,发现最长长度是2了,ok,那有有效区扩大为2,把5放进去,这时候数组里是4,5,

12的时候一样,数组变成4,5,12,然后最后一个8比12小,替换掉12,遍历结束,有效区长度为3,所以结果为3

这个方法的主要思想就是遍历到arr[i]的时候,去找之前的dp数组中第一个比他大的数,有的话替换掉,没有的话,扩大有效区。

这么一想其实这个方法也是两个循环,但是这个方法比方法一快,原因就在于我们可以加速第二个循环。

第二层循环我们去找比他大的数,可以用二分啊,所以时间复杂度加速到O(nlogn)。

下面上代码,

 1 public class GetLis {
 2     // O(n²)
 3     public static int getLis1(int[] arr) {
 4         int maxLen = 1;
 5         int[] dp = new int[arr.length];
 6         for (int i = 0; i < arr.length; i++) {
 7             dp[i] = 1;
 8             for (int j = 0; j < i; j++) {
 9                 if (arr[i] > arr[j] && dp[j] + 1 > dp[i]) {
10                     dp[i] = dp[j] + 1;
11                 }
12             }
13             maxLen = Math.max(maxLen, dp[i]);
14         }
15         return maxLen;
16     }
17 
18     // O(nlogn)
19     public static int getLis2(int[] arr) {
20         int maxLen = 1;
21         int[] dp = new int[arr.length];
22         int right = 0;
23         int l = 0;
24         int r = 0;
25         int m = 0;
26         dp[0] = arr[0];
27         for (int i = 1; i < arr.length; i++) {
28             l = 0;
29             r = right;
30             while (l <= r) {
31                 m = (l + r) / 2;
32                 if (arr[i] > dp[m]) {
33                     l = m + 1;
34                 } else {
35                     r = m - 1;
36                 }
37             }
38             right = Math.max(l, right);
39             dp[l] = arr[i];
40 
41         }
42         maxLen = right + 1;
43         return maxLen;
44     }
45 
46     public static void main(String[] args) {
47 
48         /*
49          * Scanner scan = new Scanner(System.in); int n = scan.nextInt(); int[]
50          * arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] =
51          * scan.nextInt(); } System.out.println(getLis1(arr));
52          * System.out.println(getLis2(arr)); scan.close();
53          */
54 
55         // 对数器验证一波
56 
57         for (int i = 0; i < 100000; i++) {
58             int n = (int) (Math.random() * 98 + 2);
59             int[] arr = new int[n];
60             for (int j = 0; j < n; j++) {
61                 arr[j] = (int) (Math.random() * 200);
62             }
63             int res1 = getLis1(arr);
64             int res2 = getLis2(arr);
65             if (res1 != res2) {
66                 System.out.println("出错了!");
67             }
68         }
69 
70     }
71 }
posted on 2017-08-27 21:30  大大控  阅读(307)  评论(0编辑  收藏  举报