最长递增子序列 LIS 时间复杂度O(nlogn)的Java实现

关于最长递增子序列时间复杂度O(n^2)的实现方法在博客http://blog.csdn.net/iniegang/article/details/47379873(最长递增子序列 Java实现)中已经做了实现,但是这种方法时间复杂度太高,查阅相关资料后我发现有人提出的算法可以将时间复杂度降低为O(nlogn),这种算法的核心思想就是替换(二分法替换),以下为我对这中算法的理解: 
假设随机生成的一个具有10个元素的数组arrayIn[1-10]如[2, 3, 3, 4, 7, 3, 1, 6, 6, 4],求这个数组的最长递增子序列。 
首先定义一个数组arrayOut[1-10]来逐个寻找arrayIn[1-10]中以第i个元素结尾的最长递增子序列的长度。 
定义len来计算相应的长度 
(1)将arrayIn[1]放入arrayOut,此时arrayOut[1]=arrayIn[1]=2,此时len=1; 
(2)将arrayIn[2]放入arrayOut,此时要先寻找arrayIn[2]应该放入的位置,由于arrayIn[2]=3>arrayOut[1]=2,那么arrayIn[2]应该放入的位置为arrayOut[2],这时arrayOut[2]=arrayIn[2]=3,此时len=2; 
(3)将arrayIn[3]放入arrayOut,此时要先寻找arrayIn[3]应该放入的位置,由于arrayIn[3]=3=arrayOut[2]=3,那么arrayIn[3]应该放入的位置为arrayOut[2],这时arrayOut[2]=arrayIn[3]=3,即此时进来的arrayIn[3]替换掉了arrayOut[2],此时len仍然为2; 
(4)对数组arrayIn的后续元素执行以上类似的操作即 
如果arrayIn要放入的元素比arrayOut最后一个元素大的话就放在其后; 
否则寻找一个替换的位置 
这样以来arrayIn元素放入的位置即为len的值,然后判断这次得到的len值与上次的len值的大小,向大的方向更新即可。 
使用二分法来查找arrayIn元素应该放入的位置即可将时间复杂度降为O(nlogn)。 
以下为具体的实现代码(java)

import java.util.Arrays;
import java.util.Random;

public class LISUpdate {

    public static void main(String[] args){

        System.out.println("Generating a random array...");
        LISUpdate lisUpdate=new LISUpdate();
        int[] oldArray=new int[10];
        oldArray=lisUpdate.randomArray();
        System.out.println(Arrays.toString(oldArray)); //输出生成的随机数组
        System.out.println("each LIS array:"); //输出每次计算时arrayOut数组的内容,便于观察
        System.out.println("LIS length nlogn is:"+lisUpdate.getLength(oldArray)); //输出最长递增子序列的长度
    }

    public int[] randomArray(){ //生成一个10以内的数组,长度为10
        Random random=new Random();
        int[] randomArray=new int[10];
        for (int i = 0; i < 10; i++) {
            randomArray[i]=random.nextInt(10);
        }

        return randomArray;
    }

    public int BinarySearchPosition(int arrayOut[],int left,int right,int key){ //二分查找要替换的位置

        int mid;

        if (arrayOut[right]<key) {
            return right+1;
        }else {
            while(left<right){
                mid=(left+right)/2;
                if (arrayOut[mid]<key) {
                    left=mid+1;
                }else {
                    right=mid;
                }
            }
                return left;
        }

    }

    public int getLength(int[] arrayIn){ //获取最长递增子序列的长度

        int position;
        int len=1;

        int[] arrayOut=new int[arrayIn.length+1];//arrayOut[0]没有存放数据
        arrayOut[1]=arrayIn[0]; //初始化,长度为1的LIS末尾为arrayIn[0]
        for (int i = 1; i < arrayIn.length; i++) {
            position=BinarySearchPosition(arrayOut, 1, len, arrayIn[i]);
            arrayOut[position]=arrayIn[i];
            System.out.println(Arrays.toString(arrayOut));
            if (len<position) {
                len=position;
            }
        }

        return len;
    }

需要注意的是:上面代码中输出的arrayOut数组并不是最长递增子序列,我这里选择将其输出只是为了验证算法的执行过程。

对于求最长递减子序列,则可以直接将原数组进行“反转”操作,然后求出反转之后的数组的最长递增子序列的长度即为最长递减子序列的长度。

时间: 2024-10-09 20:46:20

最长递增子序列 LIS 时间复杂度O(nlogn)的Java实现的相关文章

【难】求数组中最长递增子序列,时间复杂度O(nlogn)

题目:<编程之美> P194 写一个时间复杂度尽可能低的程序,求一个数组(length个元素)中最长递增子序列的长度. 注意,本题认为最长递增子序列可以有相等的元素,如 (1,2,2,3,3,4,5,6). 时间复杂度为O(n^2)的程序思路很简单,参考书上的解法一.针对O(n^2)的解法进行改进,利用有序数组的二分查找,可以使得时间复杂度降低.本题的难点在于,建立一个长度为length+1的数组MinV,MinV[i]代表着长度为i的递增子序列最大元素的最小值.而且数组MinV是升序的,理解

动态规划(DP),最长递增子序列(LIS)

题目链接:http://poj.org/problem?id=2533 解题报告: 状态转移方程: dp[i]表示以a[i]为结尾的LIS长度 状态转移方程: dp[0]=1; dp[i]=max(dp[k])+1,(k<i),(a[k]<a[i]) #include <stdio.h> #define MAX 1005 int a[MAX];///存数据 int dp[MAX];///dp[i]表示以a[i]为结尾的最长递增子序列(LIS)的长度 int main() { int

算法--字符串:最长递增子序列LIS

转自:labuladong公众号 很多读者反应,就算看了前文 动态规划详解,了解了动态规划的套路,也不会写状态转移方程,没有思路,怎么办?本文就借助「最长递增子序列」来讲一种设计动态规划的通用技巧:数学归纳思想.  最长递增子序列(Longest Increasing Subsequence,简写 LIS)是比较经典的一个问题,比较容易想到的是动态规划解法,时间复杂度 O(N^2),我们借这个问题来由浅入深讲解如何写动态规划. 比较难想到的是利用二分查找,时间复杂度是 O(NlogN),我们通过

算法面试题 之 最长递增子序列 LIS

找出最长递增序列 O(NlogN)(不一定连续!) 参考 http://www.felix021.com/blog/read.php?1587%E5%8F%AF%E6%98%AF%E8%BF%9E%E6%95%B0%E7%BB%84%E9%83%BD%E6%B2%A1%E7%BB%99%E5%87%BA%E6%9D%A5 我就是理解了一下他的分析 用更通俗易懂的话来说说题目是这样 d[1..9] = 2 1 5 3 6 4 8 9 7 要求找到最长的递增子序列首先用一个数组b[] 依次的将d里面

POJ 1836 Alignment 最长递增子序列(LIS)的变形

大致题意:给出一队士兵的身高,一开始不是按身高排序的.要求最少的人出列,使原序列的士兵的身高先递增后递减. 求递增和递减不难想到递增子序列,要求最少的人出列,也就是原队列的人要最多. 1 2 3 4 5 4 3 2 1 这个序列从左至右看前半部分是递增,从右至左看前半部分也是递增.所以我们先把从左只右和从右至左的LIS分别求出来. 如果结果是这样的: A[i]={1.86 1.86 1.30621 2 1.4 1 1.97 2.2} //原队列 a[i]={1 1 1 2 2 1 3 4} b[

poj 2533 Longest Ordered Subsequence 最长递增子序列(LIS)

两种算法 1.  O(n^2) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 int a[1005]; 7 int dp[1005]; 8 int main() 9 { 10 int n, maxn; 11 while(scanf("%d", &n) != EOF) 12 { 13 maxn = 0; 14 for(

最长递增子序列 (LIS) Longest Increasing Subsequence

问题描述: 有一个长为n的数列a0, a1,..., an-1.请求出这个序列中最长的上升子序列.请求出这个序列中最长的上升子序列. 上升子序列:对于任意i<j都满足ai<aj的子序列. 限制条件 i <= n <= 1000 0 <= ai <= 1000000 两种定义方式 具体看程序注释 1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #inc

动态规划 - 最长递增子序列LIS

问题:一个序列有N个数:A[1],A[2],-,A[N],求出最长非降子序列的长度 样例输入:3 1 2 6 5 4 思路: 首先把问题简单化.可以先求A[1],...A[i]的最长非降子序列,令dp[i]为以A[i]结尾的最长非降子序列.当i = 1 时, 明显是长度dp[1] = 1 : i = 2 时,前面没有比1小的数字,故dp[2]=1 , 此时的最长非降子序列为1 ; i = 3 时,比数字2小的数是1 ,并且只有1 , 分析可知 dp[3] = dp[2]+1:当i = 4 时,找

动态规划系列【2】最长递增子序列LIS

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