编程之美-数组中最长递增子序列


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

public class LongestAccendingSubSequence {

/**
* 编程之美 数组中最长递增子序列
* 书上的解法容易理解
* 另一方法书上没有提到的是,可以将数组排序(由小到大)得到新的数组,
* 然后求排序后的数组与原数组的最长公共子序列
* 最长公共子序列可用动态规则求解,见http://bylijinnan.iteye.com/blog/1450435
* 最后,可以扩展一下:求最长递增子序列的长度的同时,求出这个子序列
*/

private static final int MAX_NUM = 10;
private static int[] source = new int[2 * MAX_NUM]; //source存放-10~10这20个元素
static {
int len = 2 * MAX_NUM;
for (int i = 0; i < len; i++) {
source[i] = i - MAX_NUM;
}
}

public static void main(String[] args) throws Exception {
// int[] a = { 1, -1, 2, -3, 4, -5, 6, -7 };
int i = 0;
while (i++ < 30) { //测试30次
int[] a = generateArray(5); // 在source数组里面随机选取5个不同元素
System.out.println(Arrays.toString(a));
int length = lisA(a);
System.out.println(length);
int length2 = lisB(a);
System.out.println(length2);
if (length != length2) {
throw new Exception("error");
}
}
}

public static int lisA(int[] a) {
if (a == null || a.length == 0) {
return -1;
}
int result = 0;
int len = a.length;
int[] LIS = new int[len];
for (int i = 0; i < len; i++) {
LIS[i] = 1;
for (int j = 0; j <= i; j++) {
if (a[j] < a[i] && (LIS[j] + 1) > LIS[i]) {
LIS[i] = LIS[j] + 1;
}
}
}
for (int i = 0; i < len; i++) { // 找出LIS[i]的最大值
if (LIS[i] > result) {
result = LIS[i];
}
}
// System.out.println(Arrays.toString(LIS));
return result;
}

public static int lisB(int[] a) {
if (a == null || a.length == 0) {
return -1;
}

int len = a.length;
int[] minLast = new int[len + 1]; // minLast[i]表示长度为i的递增子序列里(长度相等的递增子序列可能有多个),各个子序列最后一个元素的集合里面的最小值
minLast[1] = a[0];
minLast[0] = Integer.MIN_VALUE;
int[] LIS = new int[len];
for (int i = 0; i < len; i++) {
LIS[i] = 1;
}
int maxLIS = 1; // 递增子序列的最长长度
for (int i = 1; i < len; i++) {
int j = 0;
//从后往前找。另外,对于i<j,总有minLast[i]<minLast[j],因此minLast是个有序的递增数组。可用二分查找加快速度
//书上说可以改成for (j = LIS[i-1]; j >= 1; j--) 我认为是不对的,因为maxLIS不一定等于LIS[i-1],且对于m<=n,LIS[m]<=LIS[n]不一定成立
for (j = maxLIS; j >= 1; j--) {
if (a[i] > minLast[j]) {
LIS[i] = j + 1;
break;
}
}
if (LIS[i] > maxLIS) {
maxLIS = LIS[i];
minLast[LIS[i]] = a[i];
} else if (minLast[j] < a[i] && a[i] < minLast[j + 1]) {
minLast[j + 1] = a[i];
}
}
// System.out.println(Arrays.toString(LIS));
return maxLIS;
}

private static int[] generateArray(int length) {
assert length > 0;
int[] a = new int[length];
Random r = new Random();
int len = source.length;
int[] copyOfSource = new int[len];
System.arraycopy(source, 0, copyOfSource, 0, len);
for (int i = 0; i < length; i++) {
int k = r.nextInt(len);
a[i] = copyOfSource[k];
copyOfSource[k] = copyOfSource[len - 1];
len--;
}
return a;
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值