最长递增子序列(动态规划实现)

来源:互联网 发布:北京大学域名 编辑:程序博客网 时间:2024/06/05 14:15
题目描述:

(题目来源于牛客网)对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2...,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。

给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。

测试样例:
[2,1,4,3,1,5,6],7
返回:4解法:题目要求复杂度为O(nlogn),我们可以这样想,肯定是要遍历数组,这里的复杂度就是O(n),那么我们在每个数那里就最多用到O(logn)的复杂度,其实我思路也挺乱的,直接说做法吧,我们先用一个辅助数组b,(长度为a.length+1,不能用list链表,复杂度会超过要求),b[0]=0(这个没用),b[1] =  a[0],length =2,length记录数组b的实际长度,以后b[1]记录长度为1 的有序序列的的最小值,b[2]记录长度为2有序序列的最小值(例如1,3,2)b[2]就记录2,因为长度为2的有序序列有(1,3)和(1,2),依次类推,得来的b数组肯定是非递增有序序列,我们在遍历到a[k]的时候,只需要在b数组中去查找比a[k]大的第一个最小数的,返回其位置,在数组b中若这一位置还是0,说明a[k]是最多的,可以将其置为a[k],若有值,可以将其改为a[k],最后数组b的长度-1就是结果(减一是因为b[0]没有用)
package dynamic;public class dynamic2{public static void main(String[] args){// TODO Auto-generated method stubint a[] ={ 2, 1, 4, 3, 1, 5, 6 };int n = 7;int num = da(a, n);System.out.println(num);}// 算法思想,用一个数组,记录最长子序列长度从1到n对应的最小数private static int da(int[] A, int n){// TODO Auto-generated method stubint b[] = new int[8];// b[0]不用,那就刚好,b[1]纪录长度1对应的最小数,b数组必定是一个从小到大的有序序列int length = 1;// 数组b的实际长度,初始化为1b[1] = A[0];length++;for (int i = 1; i < A.length; i++){int num = select(b, 1, length - 1, A[i]);// 查找从1到length-1的值if (b[num] == 0)length++;b[num] = A[i];}return length-1;}// 查找a[i]在b中对应的情况,找到比a[i]小的那个数的最大数,例如b[2]==3,现在查找4,// 那么b[3]=4,最长子序列可以加1private static int select(int[] b, int start, int end, int k){int middle = (start + end) / 2;int i = 0;int num = 0;if (start > middle)return 0;if (b[middle] == k){for (i = middle; i <= end; i++){// 防止连续好多都与k相等,我们取下标最大的那个if (b[i] != k)break;}return i;// 返回那个与k相等的那个数(若有多个,取最大的额那个)的下标} else if (b[middle] > k){num = select(b, start, middle - 1, k);if (num == 0)// 如果比这里面所有的数都大,返回0{return start;// 返回那个与k相等的那个数(若有多个,取最大的额那个)的下标} else{return num;}} else{num = select(b, middle + 1, end, k);if (num == 0)// 如果比这里面所有的数都大,返回0,num==0表示递归要结束,找不到那个要查找的数{return end + 1;// 返回那个与k相等的那个数(若有多个,取最大的额那个)的下标} else{return num;}}}}


原创粉丝点击