Longest Increasing Subsequence最长递增子序列(动态规划+二分查找)


Given an unsorted array of integers, find the length of longest increasing subsequence.

For example, 
Given [10, 9, 2, 5, 3, 7, 101, 18], 
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n) time complexity? 
原题链接

同时这也是完美世界的校招面试题。

给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱) 
例如:给定一个长度为8的数组A{1,3,5,2,4,6,7,8},则其最长的单调递增子序列为{1,2,4,6,7,8},长度为6.

输入描述:

第一行包含一个整数T,代表测试数据组数。

对于每组测试数据: 
N-数组的长度

a1 a2 … an (需要计算的数组)

保证:

1<=N<=3000,0<=ai<=MAX_INT.

输出描述:

对于每组数据,输出一个整数,代表最长递增子序列的长度。

输入例子: 


89 256 78 1 46 78 8 

6 4 8 2 17

输出例子: 

3

解法参考youtube视频:https://www.youtube.com/watch?v=1RpMc3fv0y4 该视频给出了时间复杂度为nlogn的算法,

该算法主要是基于动态规划+二分查找 (但该视频说是基于耐心排序)

dynamic programe+binary search


该视频讲到这道程序的核心是:

append if greater  ,replace if smaller

并且给出了append和replace如何进行的整个过程。替换是根据二分查找替换


current>last:  append current if greater than the last element

current<=last: replace  element >=current


O(nlogn)解法: 
这个解法应该是改自耐心排序。同样借助一个辅助数组sub,这个数组里记录了最长的递增子序列,尽可能保证这里面的元素最小。从头遍历nums,先把nums中第一个元素放到sub中,然后开始遍历数组。如果sub中最后一个元素小于第i个元素,说明sub中有元素应该更新,用折半查找找到第一个比第i个元素小的,替换掉。如果大,那么直接加入到sub的尾部。最后返回sub的长度即可。

class Solution {
public:
    int lengthOfLIS(vector& nums) {
        int *sub = new int[nums.size()];
        int length = 0;
        for(int i = 0;i nums.at(i))//折半插入
                {
                    insert(sub, length, nums.at(i));
                }
            }


        }
        return length;
    }


    void insert(int *sub, int &length, int &value)
    {

        int l = 0;
        int h = length - 1;
        while(l<=h)
        {
            int mid = (l+h)/2;
            if(value > sub[mid]) l = mid+1;
            else h = mid -1;
        }
        sub[l] = value;
    }
};

参考http://blog.csdn.net/a7055117a/article/details/51088647

java写法如下:

/**
 * 维护一个数组MaxV[i],记录长度为i的递增子序列中最大元素的最小值,
 * 并对于数组中的每个元素考察其是哪个子序列的最大元素,二分更新MaxV数组,
 * 最终i的值便是最长递增子序列的长度。
 */
public class Main {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int t = scan.nextInt();
        int n;
        while (--t >= 0) {
            n = scan.nextInt();
            int[] arr = new int[n];
            for (int i = 0; i < n; i++) {
                arr[i] = scan.nextInt();
            }
            System.out.println(LIS(arr,n));
        }
        scan.close();
    }

    private static int LIS(int[] arr, int n) {
        int[] maxValue = new int[n];
        maxValue[0] = arr[0];
        int len = 1;
        for (int i = 1; i < n; i++) {
            if (arr[i] > maxValue[len - 1]) {
                maxValue[len++] = arr[i];
            }else {
                int index = binarySearch(maxValue,len,arr[i]);
                maxValue[index] = arr[i];
            }
        }
        return len;
    }

    private static int binarySearch(int[] maxValue, int len, int x) {
        int left = 0;
        int right = len - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (maxValue[mid] < x) {
                left = mid + 1;
            }else if (maxValue[mid] > x){
                right = mid -1;
            }else {
                return mid;
            }
        }
        return left;
    }

}



参考:http://blog.csdn.net/lezg_bkbj/article/details/52035985

你可能感兴趣的:(面试)