题目:
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
思路:这个题是使用动态规划.数组dp[n]用来表示以第n个数组元素结尾的最长的递增子序列的长度。递推公式是dp[n]=max(dp[i]+1,1).其中需要遍历所有下标小于n 的数组元素。这种思路的时间复杂度是O(n^2)。
这个题我刚开始就想简单了,以为只比较它的前一个就行,实际上需要比较下标比n小的所有的元素。
我自己又做了一遍这个题,结果还是不对。。。。应该直接用动态规划做。
除此之外还可以使用二分法将复杂度降低到O(nlogn).代码如代码1.数组tail[n]表示长度为n+1的最长子序列最后的一个数字。我自己写代码的时候,把二分查找int mid = l + (h - l) / 2;中的l写成了1.。。。。。。
代码:
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0)
return 0;
if(nums.length==1)
return 1;
int n=nums.length;
int []dp=new int[n];
for(int i=0;i<n;i++){
int max=1;
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
max=Math.max(dp[j]+1,max);
}
}
dp[i]=max;//刚开始的时候我把这个写在j的循环里面了
}
int tem=0;
for(int i=0;i<n;i++){
tem=Math.max(tem,dp[i]);
}
return tem;
}
}
代码1:
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] tails = new int[n];
int len = 0;
for (int num : nums) {
int index = binarySearch(tails, len, num);
tails[index] = num;
if (index == len) {
len++;
}
}
return len;
}
//这个好像是使用二分法将新的num插入到tails数组中
private int binarySearch(int[] tails, int len, int key) {
int l = 0, h = len;
while (l < h) {
int mid = l + (h - l) / 2;
if (tails[mid] == key) {
return mid;
} else if (tails[mid] > key) {
h = mid;
} else {
l = mid + 1;
}
}
return l;
}
}
这个里面还是是不会有相同的数,顺丰的笔试题有一个是可能有相同的数,把牛课上大神的代码写在这里:
import java.util.Scanner; public class sf2 { //这是一个静态内部类。有一个数值一个索引 private static class Point implements Comparable { int num; int index; Point(int num, int index) { this.num = num; this.index = index; } //内部实现compare方法。这个方法是用来处理相同的数字的。数字相同,直接比较索引。 public int compareTo(Object o) { Point p = (Point) o; if (p.num == this.num) { return this.index - p.index; } return this.num - p.num; } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); Point[] points = new Point[n]; for (int i = 0; i < n; i++) { int num = scanner.nextInt(); points[i] = new Point(num, i); } System.out.print(maxLengthOfRisingSubArray(points)); } private static int maxLengthOfRisingSubArray(Point[] nums) { int n = nums.length; Point[] array = new Point[n]; int index = 0; for (int i = 0; i < n; i++) { if (i == 0) { array[index++] = nums[i]; } else { int ceilIndex = ceil(array, index, nums[i]); if (ceilIndex == index) { array[index++] = nums[i]; } else if (array[ceilIndex].compareTo(nums[i]) > 0) { array[ceilIndex] = nums[i]; } } } return index; } //这个应该是用二分法实现的 private static int ceil(Point[] array, int index, Point target) { int left = 0, right = index; while (left < right) { int mid = left + ((right - left) >> 1); if (array[mid].compareTo(target) <= 0) { left = mid + 1; } else { right = mid; } } if (left - 1 >= 0 && array[left - 1].compareTo(target) == 0) { return left - 1; } return left; } }