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

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;
	}
	
}

你可能感兴趣的:(编程之美)