12月2日
1.暴力
以每个值为起点,记录从每个元素开始往后相乘出现的最大值。
public static int maxProduct(int[] nums) {
int len= nums.length;
int max=nums[0];
int cur=nums[0];
for (int i = 0; i < len; i++) {
cur=nums[i];
max=cur>max?cur:max;
for (int j = i+1; j < len; j++) {
cur=cur*nums[j];
max=cur>max?cur:max;
}
}
return max;
}
2.动态规划
public static int maxProduct(int[] nums) {
int max=Integer.MIN_VALUE,imax=1,imin=1;
//如果当前元素为负数,max和min互换
for (int i = 0; i < nums.length; i++) {
if (nums[i]<0){
int temp=imax;
imax=imin;
imin=temp;
}
imax=Math.max(imax*nums[i],nums[i]);
imin=Math.min(imin*nums[i],nums[i]);
max=Math.max(max,imax);
}
return max;
}
I
12月3日
LinkedList 是一个继承于AbstractSequentialList的双向链表。
LinkedList 可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,所以能对它进行队列操作。
LinkedList 实现 Deque 接口,能将LinkedList当作双端队列使用。
- stack是堆栈,继承自Vector,没有迭代器,特点是后进先出。用push()将元素压入栈中,top()返回栈顶元素,pop()移除栈顶元素。Stack设计不好的地方,既然只是为了实现栈,不用链表来单独实现。
- queue是队列,特点是先进先出,不支持迭代器,使用push()将元素排入对中,front()返回队首元素,pop()移除队首元素。
- Java中的deuqe,即“double ended queue”的缩写,是Java中的双端队列集合类型,它集成自queue,具备普通队列FIFO的功能,同时它也具备了Stack的LIFO功能,并且保留了push和pop函数.
注意:
实际在Java Doc里是建议用Deque替代Stack接口完成栈的功能 。
这个题的做法,有点迷
/**
* 建议用deque替代stack,因此stack继承自vector
* deque继承自queue,是双端队列,用linkedlist实现deque和stack(即FIFO又LIFO)
**/
class MinStack {
Deque<Integer> xStack ;
Deque<Integer> minStack;
public MinStack() {
xStack = new LinkedList<Integer>();
minStack = new LinkedList<Integer>();
minStack.push(Integer.MAX_VALUE);
}
// 此写法也可,但建议用Deque替代Stack接口完成栈的功能
// Stack<Integer> xStack ;
// Stack<Integer> minStack;
// public MinStack() {
// xStack = new Stack<Integer>();
// minStack = new Stack<Integer>();
// minStack.push(Integer.MAX_VALUE);
// }
public void push(int val) {
//将元素 x 推入栈中
xStack.push(val);
minStack.push(Math.min(minStack.peek(),val));
}
public void pop() {
// 删除栈顶的元素
xStack.pop();
minStack.pop();
}
public int top() {
//获取栈顶元素
return xStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
可以理解成两个人速度一致, 走过的路程一致。那么肯定会同一个时间点到达终点。如果到达终点的最后一段路两人都走的话,那么这段路上俩人肯定是肩并肩手牵手的。
若相交,链表A: a+c, 链表B : b+c。a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。
若不相交,a +b = b+a 。因此相遇处是末尾的NULL。
如果两链表相交,则走过的总路程为a+b,速度相同,路程相同,因此会同一时间到达终点,并且同一时间到达公共起点。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA==null || headB==null) return null;
ListNode preA=headA;ListNode preB=headB;
while (preA!=preB){
preA=preA==null?headB:preA.next;
preB=preB==null?headA:preB.next;
}
return preA;
}
12月6日
方法一:不够优化
//可通过,但需要简化
public static int majorityElement(int[] nums) {
int len=nums.length;
Arrays.sort(nums);
int curVal=nums[0];
int cruLen=1;
if (len==1) return curVal;
for (int i = 0; i < len-1; i++) {
if (nums[i] == nums[i + 1]) {
cruLen++;
//curVal=nums[i];
}
if (cruLen > (int) len / 2) {
return nums[i];
}
}
return -1;
}
方法二:
由于多数是出现次数 大于 ⌊ n/2 ⌋
的元素,因此我们只需要返回排序后的 第⌊ n/2 ⌋元素即可
public static int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
方法三:哈希表
由于Map中存放的元素均为键值对,故每一个键值对必然存在一个映射关系。
Map中采用Entry内部类来表示一个映射项,映射包括Key和Value。
Map.Entry里面包含getKey()和getValue()Set<Entry<T,V>>entrySet()还方法返回值就是这个map中各个键值对映射关系的集合,可使用它对map进行遍历。
public static Map<Integer, Integer> countMap(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
int len = nums.length;
//统计各个元素出现的次数
for (int i = 0; i < len; i++) {
if (map.containsKey(nums[i])){
map.put(nums[i],map.get(nums[i])+1);
}else {
map.put(nums[i],1);
}
}
return map;
}
public static int majorityElement(int[] nums) {
Map<Integer, Integer> map =countMap(nums);
Map.Entry<Integer,Integer> maxEntry =null;
for (Map.Entry<Integer,Integer> entry:map.entrySet()) {
if (maxEntry==null || entry.getValue()>maxEntry.getValue()){
maxEntry=entry;
}
}
return maxEntry.getKey();
}
动态规划
public static int rob(int[] nums) {
if (nums==null || nums.length==0)return -1;
int len = nums.length;
if (len==1)return nums[0];
int[] dp = new int[len];
dp[0]=nums[0];
dp[1]=Math.max(nums[0],nums[1]);
for (int i = 2; i < len; i++) {
dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[len-1];
}
使用滚动数组:
public static int rob(int[] nums) {
if (nums==null || nums.length==0)return -1;
int len = nums.length;
if (len==1)return nums[0];
//滚动数组
int pre0=nums[0];
int pre1=Math.max(nums[0],nums[1]);
for (int i = 2; i < len; i++) {
int temp=pre1;
pre1=Math.max(pre0+nums[i],pre1);
pre0=temp;
}
return pre1;
}
12月7日
int[][] direction={{-1,0},{1,0},{0,-1},{0,1}};//上下左右四个方向
private int m,n;
public int numIslands(char[][] grid) {
m=grid.length;
n=grid[0].length;
int islandNum=0;
if (grid==null ||m==0 || n==0) return 0;
for (int i = 0; i < m; i++) {//竖着遍历
for (int j = 0; j < n; j++) {
if (grid[i][j]=='1'){
dfs(grid,i,j);
islandNum++;
}
}
}
return islandNum;
}
public void dfs(char[][] grid,int row,int col){
if (row<0 || row>=m || col<0 ||col>=n || grid[row][col]=='0'){
return;
}
grid[row][col]='0';
for (int[] dir: direction) {
dfs(grid,row+dir[0],col+dir[1]);
}
}
public ListNode reverseList(ListNode head) {
if (head==null) return null;
ListNode cur=head.next;
ListNode pre=null;
ListNode newList=new ListNode(head.val);
while (cur!=null){
pre=cur.next;
cur.next=newList;
newList=cur;
cur=pre;
}
return newList;
}
12月8日
拓扑排序判断是否是有向无环图。
java.util.ArrayDeque.poll() 此方法检索并删除此双端队列表示的队列的头部,如果此双端队列为空,则返回null。
/**
* @description :因为题目中说[1, 2]的意思是要学1,先得学2,所以是2->1
* [3, 2] [4,2] : 2->3 2->4
* adjacency[2]存的就是3,4(即2指向的元素)
*
* 参考答案
**/
public static boolean canFinish(int numCourses, int[][] prerequisites) {
int[] indegrees=new int[numCourses];//用于存储每个点(数字)的入度
List<List<Integer>> adjacency=new ArrayList<>();//邻接表的准备
Queue<Integer> queue=new LinkedList<>();
for(int i=0;i<numCourses;i++){
adjacency.add(new ArrayList<>());
}//事先创建空的arraylist
for(int[]cp:prerequisites){
indegrees[cp[0]]++;//cp[0]这个数前面连接了一个数cp[1],所以度+1
adjacency.get(cp[1]).add(cp[0]);//创建连接表,在cp[1]对应的位置,放入后连接的一个数字也就是cp[0],
//一个cp[1]可能对应多个cp[0],例如 [3, 2] [4,2] : 2->3 2->4
}
for(int i=0;i<numCourses;i++){
if(indegrees[i]==0)//如果入度为0入队列
queue.add(i);
}
while(!queue.isEmpty()){
int pre=queue.poll();//弹出度为0的数,其实也就是准备删除
numCourses--;//删除后个数减少1,对应作者的图可以理解
for(int cur:adjacency.get(pre)){//找到入度0连接后面的数字们遍历,例如遍历上面2后连接的3,4
if(--indegrees[cur]==0){//并且将他们的入度同时-1,如果此时度为0,继续入队列
queue.add(cur);
}
}
}
return numCourses==0;
}
字典树:
class Trie {
private Trie[] children;
private boolean isEnd;
public Trie() {
children =new Trie[26];
isEnd=false;
}
public void insert(String word) {
Trie trie =this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if(trie.children[ch-'a']==null){
trie.children[ch-'a']=new Trie();
}
trie=trie.children[ch-'a'];
}
trie.isEnd=true;
}
//不为空,并且是单词结尾
public boolean search(String word) {
Trie node=searchPrefit(word);
return node!=null && node.isEnd;
}
//不为空即可
public boolean startsWith(String prefix) {
return searchPrefit(prefix)!=null;
}
public Trie searchPrefit(String prefix){
Trie node =this;
for (int i = 0; i < prefix.length(); i++) {
if (node.children[prefix.charAt(i)-'a']==null){
return null;
}
node=node.children[prefix.charAt(i)-'a'];
}
return node;
}
}
12月24日
队列的add()方法和offer()方法的区别:
两者都是往队列尾部插入元素,不同的时候,当超出队列界限的时候,add()方法是抛出异常让你处理,而offer()方法是直接返回false
方法一:大顶堆
public static int findKthLargest(int[] nums, int k) {
int len = nums.length;
PriorityQueue<Integer> priorityQueue= new PriorityQueue<>(len,(a,b)->(b-a));//大顶堆
for (int i = 0; i < len; i++) {
priorityQueue.add(nums[i]);
}
for (int i = 0; i < k-1; i++) {
priorityQueue.poll();
}
return priorityQueue.peek();
}
方法二:小顶堆(空间复杂度低)
小根堆最多只需要k个元素在堆里,因此时间复杂度是 O(nlogk),大顶堆需要len个元素,因此使用k个元素的小顶堆,复杂度更低。
public static int findKthLargest(int[] nums, int k) {
int len = nums.length;
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, (a, b) -> (a - b));//小顶堆
for (int i = 0; i < k; i++) {
priorityQueue.add(nums[i]);
}
for (int i = k; i <len ; i++) {
int top=priorityQueue.peek();//只取值,不弹出
if (nums[i]>top){
priorityQueue.poll();
priorityQueue.add(nums[i]);
}
}
return priorityQueue.peek();
}
方法三:自己实现大顶堆
public static int findKthLargest(int[] nums, int k) {
int len = nums.length;
int headSize=len;
buildMaxTree(nums,headSize);
for (int i = len-1; i >=len-k+1 ; i--) {
swap(nums,0,i);
headSize--;
maxHeapify(nums,0,headSize);
}
return nums[0];
}
private static void buildMaxTree(int[] nums, int headSize) {
for (int i = headSize/2; i >=0; i--) {
maxHeapify(nums,i,headSize);
}
}
private static void maxHeapify(int[] nums,int index,int headSize){
int left=index*2+1;int right =index*2+2;int largest=index;
if (left<headSize && nums[left]>nums[largest]){//写为largest
largest=left;
}
if (right<headSize && nums[right]>nums[largest]){
largest=right;
}
if (largest!=index){
swap(nums,largest,index);
maxHeapify(nums,largest,headSize);
}
}
private static void swap(int[] arr, int a,int b) {
int temp=arr[a];
arr[a]=arr[b];
arr[b]=temp;
}
方法一:动态规划
public int maximalSquare(char[][] matrix) {
if (matrix==null || matrix.length==0 || matrix[0].length==0) return 0;
int row=matrix.length;int col=matrix[0].length;
int maxSize=0;
int dp[][]=new int[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (matrix[i][j]=='1' ){
if (i==0 || j==0) {
dp[i][j]=1;
}else {
dp[i][j]=Math.min(Math.min(dp[i-1][j-1],dp[i-1][j]),dp[i][j-1])+1;
}
maxSize=Math.max(maxSize,dp[i][j]);
}
}
}
return maxSize*maxSize;
}
12月26日
此题解是先交换左右叶子节点,再交换左右子树。
public TreeNode invertTree(TreeNode root) {
if (root==null) return null;
TreeNode left =invertTree(root.left);
TreeNode right =invertTree(root.right);
root.left=right;
root.right=left;
return root;
}
也可以先交换左右子树,再交换左右节点。
public TreeNode invertTree(TreeNode root) {
if (root==null) return null;
TreeNode temp= root.left;
root.left=root.right;
root.right=temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
方法一:将值复制到数组中后用双指针法
public boolean isPalindrome(ListNode head) {
List<Integer> array = new ArrayList<Integer>();
if (head == null) return true;
ListNode cur = head;
while (cur != null) {
array.add(cur.val);
cur = cur.next;
}
int len = array.size();
for (int i = 0, j = len - 1; i < len / 2 && j >= len / 2; i++, j--) {
if (array.get(i) != array.get(j)) {
return false;
}
}
return true;
}
快慢指针,都从head开始,slow返回的是中间(奇)或者中间元素的前一个(偶)
举例:若有三个元素返回的是第二个元素,若有四个元素返回的是第二个元素。
12月28日
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//p或q为根节点,则返回root,不能比其更深了
if (root==null || root == p || root==q){
return root;
}
//q,p都不是根节点,则分别在左右子树中寻找q,p
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
//左右子树中都没有p,q
if (left==null && right==null){
return null;
}
//左子树中没有q,也没有p,在右子树中,则返回右子树的结果
if (left==null){
return right;
}
//右子树中没有,在左子树中,则返回左子树的结果
if (right==null){
return left;
}
//左右子树都有值,则q和p分别在左右子树上
return root;
}
不能使用除法是因为如果有的元素为0,则程序会报错。
方法一:
用num[i]为分界点,分为前缀和后缀。
public int[] productExceptSelf(int[] nums) {
int[] output = new int[nums.length];
output[0]=1;
for (int i = 1; i < nums.length; i++) {
output[i]=nums[i-1]*output[i-1];
}
int R=1;
for (int i = nums.length-1; i >=0 ; i--) {
output[i]=output[i]*R;
R=R*nums[i];
}
return output;
}
12月29日
239. 滑动窗口最大值
public E peek()
:检索但不删除此列表的头部(第一个元素)。
public E peekFirst()
:检索但不删除此列表的第一个元素,如果此列表为空,则返回null
。
public E peekLast()
:检索但不删除此列表的最后一个元素,如果此列表为空,则返回null
。
//加入队尾,
public static int[] maxSlidingWindow(int[] nums, int k) {
LinkedList<Integer> queue = new LinkedList<>();
if (nums==null || nums.length<=1){
return nums;
}
int[] ans = new int[nums.length-k+1];
for (int i = 0; i < nums.length; i++) {
//保证从大到小,前面的数小则先弹出
while (!queue.isEmpty() && nums[queue.peekLast()]<=nums[i]){
queue.pollLast();
}
queue.addLast(i);//队尾进
//不在窗口内,弹出
if (queue.peek()<i+1-k){
queue.poll();
}
//当前长度大于窗口长度,就可以填入最大值了(加一的原因是i从零开始)
if (i+1>k){
ans[i+1-k]= nums[queue.peek()];
}
}
return ans;
}
12月30日
方法一:直接查找
public static boolean searchMatrix(int[][] matrix, int target) {
int row= matrix.length;int col=matrix[0].length;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (matrix[i][j]==target){
return true;
}
}
}
return false;
}
方法二:每行二分查找
public static boolean searchMatrix(int[][] matrix, int target) {
for (int[] i:matrix) {
int index = seach(i, target);
if (index>=0){return true;}
}
return false;
}
private static int seach(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
方法三:Z型查找
public static boolean searchMatrix(int[][] matrix, int target) {
int row= matrix.length;int col=matrix[0].length;
int i=0,j=col-1;//右上角
while (i<row && j>=0){
if (matrix[i][j]==target) return true;
else if (matrix[i][j]< target){
//此行不要了,下移一行
i++;
}else if (matrix[i][j]> target){
j--;//此列不要了,左移一列
}
}
return false;
}
方法:动态规划
public int numSquares(int n){
int[] dp=new int[n+1];
for (int i = 1; i <= n; i++) {
dp[i]=i;//全是1,例如3=1+1+1
for (int j = 1; i-j*j>=0 ; j++) {
dp[i]=Math.min(dp[i],dp[i-j*j]+1);
}
}
return dp[n];
}
public static void moveZeroes(int[] nums) {
int zeor=0;
//统计0的个数,并且遇到几个零,就往前移动几位
for (int i = 0; i < nums.length; i++) {
if (nums[i]==0){
zeor++;
continue;
}
if (i>0) nums[i-zeor]=nums[i];
}
//末尾赋0
for (int i = nums.length-1; i > nums.length-1-zeor ;i--) {
nums[i]=0;
}
}
方法一:空间复杂度为O(n)
//空间复杂度为O(n)
public int findDuplicate(int[] nums) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; i++) {
if (!map.containsKey(nums[i])){
map.put(nums[i],1);
}else return nums[i];
}
return -1;
}
方法二:改变了数组
//先排序,若相邻两个元素值相等,则返回
public int findDuplicate(int[] nums) {
Arrays.sort(nums);
for (int i = 0; i < nums.length-1; i++) {
if (nums[i]==nums[i+1]){
return nums[i];
}
}
return -1;
}
//或者
public int findDuplicate(int[] nums) {
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i]==i){
return nums[i];
}
}
return -1;
}
方法三:(推荐)
空间复杂度为:O(1),且不改变原数组。
快慢指针在入环点相遇:
设:从起点到环的入口的步数是 a,slow从环的入口继续走 b 步到达相遇位置,从相遇位置继续走 c 步回到环的入口
因此先找到相遇点,让慢指针从起点开始, 让快指针从相遇点开始。 这种情况下,想让快指针想要追上慢指针,就是慢指针走a步,快慢指针就在环的入口相遇(即再次相遇的点就是环入口)。
//快慢指针,初始化都从0开始
public int findDuplicate(int[] nums) {
int fast=0,slow=0;
slow=nums[slow];//慢指针走一步
fast=nums[nums[fast]];//快指针走两步
while (slow!=fast){
slow=nums[slow];
fast=nums[nums[fast]];
}
//让慢指针从起点开始,快指针从相遇点开始,都只走一步
int pre=slow;
int pre2 =0;
while (pre!=pre2){
pre=nums[pre];
pre2=nums[pre2];
}
return pre;
}
12月31日
// fp[i][0]: 手上持有股票的最大收益
// fp[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
// fp[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
public int maxProfit(int[] prices) {
int len =prices.length;
int[][] fp = new int[len][3];
fp[0][0]=-prices[0];
// fp[0][0]=0;fp[0][1]=0;//默认为0
for (int i = 1; i < len; i++) {
fp[i][0]=Math.max(fp[i-1][0],fp[i-1][2]-prices[i]);
fp[i][1]=fp[i-1][0]+prices[i];
fp[i][2]=Math.max(fp[i-1][1],fp[i-1][2]);
}
return Math.max(fp[len-1][1],fp[len-1][2]);
}
public int maxProfit(int[] prices) {
int len =prices.length;
int f0=-prices[0];
int f1=0,f2=0;
for (int i = 1; i < len; i++) {
int new0=Math.max(f0,f2-prices[i]);
int new1=f0+prices[i];
int new2=Math.max(f1,f2);
f0=new0;
f1=new1;
f2=new2;
}
return Math.max(f1,f2);
}
动态规划
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount+1];//dp[i]放入的是i需要几个硬币
//dp[i]=dp[m]+dp[i-m];
Arrays.fill(dp,amount+1);
dp[0]=0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j]<=i){
dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);
}
}
}
return dp[amount]>amount?-1:dp[amount];
}
class Solution {
HashMap<TreeNode, Integer> f = new HashMap<>();
HashMap<TreeNode, Integer> g = new HashMap<>();
public int rob(TreeNode root) {
dfs(root);
return Math.max(f.getOrDefault(root,0),g.getOrDefault(root,0));
}
public void dfs(TreeNode root){
if (root==null) return ;
dfs(root.left);
dfs(root.right);
f.put(root,root.val+g.getOrDefault(root.left,0)+g.getOrDefault(root.right,0));
g.put(root,Math.max(f.getOrDefault(root.left,0), g.getOrDefault(root.left,0))+Math.max(f.getOrDefault(root.right,0), g.getOrDefault(root.right,0)));
}
}
22年啦
1月2日
方法一:枚举
public int subarraySum(int[] nums, int k) {
int count=0;
for (int i = 0; i < nums.length; i++) {
int sum=0;
for (int j = i; j >=0 ; j--) {
sum+=nums[j];
if (sum==k){
count++;
}
}
}
return count;
}
方法二:前缀和 + 哈希表优化
pre为现在的总和, map[i][j]意思是,和为i的连续数组,有j组(pre最终结果为nums.length个元素的和,map最后一行的key也是)
public static int subarraySum(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0,1);
int pre=0;
int count=0;
for (int i = 0; i <nums.length ; i++) {
pre+=nums[i];
// if (map.containsKey(pre-k)){
// count+=map.get(pre-k);
// }
count+=map.getOrDefault(pre-k,0);//注意写的是key,不是map(key)
map.put(pre,map.getOrDefault(pre,0)+1);
}
return count;
}
根据560题解,改编
map[i][j]意思是,路径和为i的路径,有j条。
pre是当前遍历的总路径和。
//map[i][j]意思是,路径和为i的路径,有j条
class Solution {
int pre =0;//路径和
int count=0;//路径数目
public int pathSum(TreeNode root, int targetSum) {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0,1);
path(root,targetSum,map);
return count;
}
public void path(TreeNode root, int targetSum, HashMap<Integer, Integer> map){
if (root==null) return;
TreeNode curNode=root;
pre+=curNode.val;
// if (map.containsKey(pre-targetSum)){
// count+=map.get(pre-targetSum);
// }
count+=map.getOrDefault(pre-targetSum,0);
map.put(pre,map.getOrDefault(pre,0)+1);//参数是key,不是map.get(key)
//遍历左右子树
path(root.left,targetSum,map);
path(root.right,targetSum,map);
//返回上一层节点
map.put(pre,map.get(pre)-1);
pre-=curNode.val;
}
}
方法一:
public int[] countBits(int n) {
int[] ans=new int[n+1];
for (int i = 0; i <=n; i++) {
ans[i]=ands(i);
}
return ans;
}
public int ands(int n){
int sum=0;
while(n>0){
n=n&(n-1);
sum++;
}
return sum;
}
方法二:最低有效位(推荐)
x 除以 2 的余数可以通过 x & 1 得到:
public int[] countBits(int n) {
int[] ans=new int[n+1];
for (int i = 1; i <= n; i++) {
ans[i]=ans[i>>1]+(i&1);
}
return ans;
}
1月3日
* 左括号之前的字符,栈stack_res。 * 临时栈stack,遇到“[”则放入,stack.put(括号内字符重复次数multi,左括号之前的字符res),将此分为两个栈stack_multi和stack_res分别记录次数和字符。 * 数字mutil记录数字。 * res记录计算当前的字符string类型,res在遇到“[”前,记录的是“["的字符串,遇到“[”res入栈,并且res归为空,res继续记录“]”后的元素。
/**
*
* 左括号之前的字符,栈stack_res
* 临时栈stack,遇到“[”则放入,stack.put(括号内字符重复次数multi,左括号之前的字符res),将此分为两个栈stack_multi和stack_res分别记录次数和字符
* 数字mutil记录数字
* res记录计算当前的字符string类型,res在遇到“[”前,记录的是“["的字符串,遇到“[”res入栈,并且res归为空,res继续记录“]”后的元素
*/
public String decodeString(String s) {
LinkedList<Integer> stack_multi = new LinkedList<>();//重复次数
LinkedList<String> stack_res = new LinkedList<>();//"["前面的字符串
StringBuffer res = new StringBuffer();//当前字符串
int multi=0;//记录当前次数
for (char a: s.toCharArray()) {
if (a=='['){
stack_multi.addLast(multi);
stack_res.addLast(res.toString());
multi=0;
res=new StringBuffer();
}else if (a==']'){
StringBuffer tem = new StringBuffer();//当前k[]还原后的字符串
int cur_multi=stack_multi.pollLast();//结果是Integer类型
for (int i = 0; i < cur_multi; i++) tem.append(res);
//for (int i = 0; i < stack_multi.pollLast(); i++) tem.append(res);
res=new StringBuffer(stack_res.pollLast()+tem);//如果在遇到],则重复res
}
else if (a>='0' && a<='9') {
multi=multi*10+Integer.parseInt(a+"");//参数是String类型
}
else res.append(a);
}
return res.toString();
}
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
int size = equations.size();
UnionFind unionFind = new UnionFind(2 * size);
//第一步,讲变量值与id进行映射
HashMap<String, Integer> map = new HashMap<>(2 * size);
int id=0;
for (int i = 0; i < size; i++) {
List<String> equation=equations.get(i);
String var1 = equation.get(0);
String var2 = equation.get(1);
if (!map.containsKey(var1)){
map.put(var1,id++);
}
if (!map.containsKey(var2)){
map.put(var2,id++);
}
unionFind.union(map.get(var1),map.get(var2),values[i]);
}
//第二步,查询
int qsize = queries.size();
double[] res=new double[qsize];
for (int i = 0; i < qsize; i++) {
String var1 = queries.get(i).get(0);
String var2 = queries.get(i).get(1);
Integer id1 = map.get(var1);
Integer id2 = map.get(var2);
if (id1==null || id2==null){
res[i]=-1.0d;
}else {
res[i]= unionFind.isConnected(id1,id2);
}
}
return res;
}
//并查集
private class UnionFind{
private int[] parent;
private double[] weight;//指向的父结点的权值
//1.初始化并查集
public UnionFind(int n){
this.parent=new int[n];
this.weight=new double[n];
for (int i = 0; i < n; i++) {
parent[i]=i;
weight[i]=1.0d;
}
}
//2.查找根节点,路径压缩
public int find(int x){
if (x!=parent[x]){
int origin =parent[x];
parent[x]=find(parent[x]);//路径压缩,遍历过程中的所有双亲结点直接指向根结点,减少后续查找次数
weight[x]*=weight[origin];
}
return parent[x];
}
//3. 合并
public void union(int x,int y,double value){
int rootX=find(x);//x的父节点
int rootY=find(y);
//父节点是同一个,则不需要合并了
if (rootX==rootY){
return;
}
parent[rootX]=rootY;//让x指向y的父节点
//计算新的路径距离,y指向父节点的权重,x指向y的权重为value
weight[rootX]=weight[y]*value/weight[x];
}
//查看x,y是否属于一个集合
public double isConnected(int x,int y){
int rootX=find(x);
int rootY=find(y);
if (rootX==rootY){
return weight[x]/weight[y];
}else {
return -1.0d;
}
}
}
1月4日
根据第一个元素降序排列,第二个元素升序排列,然后再根据k插入第k个位置(假如:前面有三个比它大的,因为降序排列前面都大于它,因此放在p[3],0,1,2位置的三个元素都大于它)
public int[][] reconstructQueue(int[][] people) {
//根据第一个元素降序排列,第二个元素升序排列
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] preson1, int[] preson2) {
//第一个元素不相等时,第一个元素降序
if (preson2[0]!=preson1[0]) return preson2[0]-preson1[0];
//第一个元素相等时,第二个元素升序
else return preson1[1]-preson2[1];
}
});
LinkedList<int[]> list = new LinkedList<>();
for (int i = 0; i < people.length; i++) {
if (people[i][1]< list.size()){
//假如有m个大于第i个人,则将第i个人放在m位置上。因为首元素降序,i面前的人都高于i(先遍历的人都比其高)
list.add(people[i][1],people[i]);
}else list.add(list.size(), people[i]);//否则放在列表末尾
}
return list.toArray(new int[list.size()][]);
}
public boolean canPartition(int[] nums) {
int sum=0;
for (int i = 0; i < nums.length; i++) {
sum+=nums[i];
}
//奇数返回false
if ((sum&1)==1) return false;
int target=sum/2;
boolean dp[][] = new boolean[nums.length][target+1];
if (nums[0]<=target) dp[0][nums[0]]=true;
for (int i = 1; i < nums.length ; i++) {
for (int j = 0; j <=target ; j++) {
//先把上一行先抄下来,再赋值
dp[i][j]=dp[i-1][j];
if (j==nums[i]) {
dp[i][j]=true;
continue;
}
if (j>nums[i]){
dp[i][j]=dp[i-1][j] || dp[i-1][j-nums[i]];
}
}
}
return dp[nums.length-1][target];
}
public List<Integer> findAnagrams(String s, String p) {
List<Integer> list=new LinkedList<>();
int slen=s.length();int plen=p.length();
if (slen<plen) return list;
int schar[]=new int[26];
int pchar[]=new int[26];
for (int i = 0; i < plen; i++) {
pchar[p.charAt(i)-'a']++;
}
int left=0;
for (int right = 0; right < slen; right++) {
schar[s.charAt(right)-'a']++;
//大于说明,当前遍历的出现了p中未出现的字符或者s中出现的字符次数超过了p中的次数,则左边界右移
while (schar[s.charAt(right)-'a']>pchar[s.charAt(right)-'a']){
schar[s.charAt(left)-'a']--;
left++;
}
if (right-left+1==p.length()){
list.add(left);
}
}
return list;
}
1月5日
用map来存储每个元素出现的次数, 空间复杂度为O(n)
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> list = new LinkedList<>();
Map<Integer, Integer> map = new HashMap<>();
int len= nums.length;
Arrays.sort(nums);
for (int i = 0; i < len; i++) {
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
for (int i = 1; i <= len; i++) {
if (!map.containsKey(i)){
list.add(i);
}
}
return list;
}
方法二:
空间复杂度为O(1)
nums的数组长度为n,我们可以考虑用nums存储,每个元素出现的次数,
因为元素范围是[1,n],则若出现元素k,我们将nums[k-1]+n,最后判断数组元素值,若超过n则说明出现过。num[i]%len取的是数组原来的值(即不加len之前的值)。
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> list = new LinkedList<>();
int len= nums.length;
for (int i = 0; i < len; i++) {
int x=(nums[i]-1)%len;//因为元素是[1,n],但是数组下标是[0,n-1]
nums[x]+=len;
}
for (int i = 0; i < len; i++) {
if (nums[i]<=len){
list.add(i+1);//num[i]存储的是i+1是否存在
}
}
return list;
}
方法一:内置方法
^是异或,bitcount(i)是计算i的二进制中1的个数。
public int hammingDistance(int x, int y) {
//^是异或,bitcount(i)是计算i的二进制中1的个数
return Integer.bitCount(x^y);
}
方法二:每次计算异或后二进制最后一位的1,然后右移一位
二进制没有正负,补码有正负
public int hammingDistance(int x, int y) {
int s=x^y;
int count=0;
//或者while(s!=0)均可
while(s>0){
count+=s&1;//计算末尾是否为0
s=s>>1;
}
return count;
}
方法三:
x&(x-1)是删除最右侧的1。
public int hammingDistance(int x, int y) {
int s =x^y;
int count=0;
while (s!=0){
s=s&(s-1);//删除最右侧的1
count++;
}
return count;
}
方法一:回溯
class Solution {
int count=0;
public int findTargetSumWays(int[] nums, int target) {
backing(nums,target,0,0);
return count;
}
private void backing(int[] nums, int target, int index,int cur) {
if (index==nums.length){
if (cur==target)count++;
}else {
backing(nums,target,index+1,cur+nums[index]);
backing(nums,target,index+1,cur-nums[index]);
}
}
}
方法二:动态规划
int sum=0;
public int findTargetSumWays(int[] nums, int target) {
int len=nums.length;
for (int i = 0; i < len; i++) {
sum+=nums[i];
}
int diff=sum-target;
if (diff<0 || diff%2!=0)return 0;
int neg=diff/2;
int[][] dp=new int[len+1][neg+1];//dp[i][j]代表前i个值的和为j的方案数
dp[0][0]=1;
for (int i = 1; i <= len; i++) {
int num=nums[i-1];//前i个值是nums[0,i-1]
for (int j = 0; j < neg+1; j++) {
dp[i][j]=dp[i-1][j];//不要第i个值
if (j>=num){
dp[i][j]+=dp[i-1][j-num];
}
}
}
return dp[len][neg];
}
1月6日
dp[i][j]
表示开区间 (i,j)
内你能拿到的最多金币。
public int maxCoins(int[] nums) {
//给nums两端各加一个1,让数组运算时不越界
int n=nums.length;
int temp[]=new int[n+2];
temp[0]=temp[n+1]=1;
for (int i = 0; i < n; i++) {
temp[i+1]=nums[i];
}
//dp[i][j] 表示开区间 (i,j) 内你能拿到的最多金币
int dp[][]=new int[n+2][n+2];
//len表示开区间长度[3,n+2],最短为3,因为两边为开区间
for (int len = 3; len <=n+2 ; len++) {
//i表示开区间的左端点[0,n+2-len]
for (int i = 0; i <= n+2-len; i++) {
int res=0;
//k表示开区间内的索引[i+1,i+len-1],因为最右侧可能是临界的1
for (int k = i+1; k < i+len-1; k++) {
int left=dp[i][k];
int right=dp[k][i+len-1];
res=Math.max(res,left+right+temp[i]*temp[k]*temp[i+len-1]);
}
dp[i][i+len-1]=res;
}
}
return dp[0][n+1];
}
反中序遍历:先遍历右子树,再遍历根节点,最后遍历左子树。
//先遍历右子树,再遍历根节点,最后遍历左子树,反中序遍历
class Solution {
int sum=0;
public TreeNode convertBST(TreeNode root) {
if (root==null) return null;
convertBST(root.right);
sum += root.val;
root.val=sum;
convertBST(root.left);
return root;
}
}
int ans=0;
public int diameterOfBinaryTree(TreeNode root) {
depth(root);
return ans;
}
//1——》2——》3(是三层)
public int depth(TreeNode root){
if (root==null) return 0;
int left=depth(root.left);//左子树的深度
int right=depth(root.right);//右子树的深度
ans=Math.max(ans,left+right);//最长路径
return Math.max(left,right)+1;//返回以此root为根的层数
}
1月9日
方法一:排序,记录不相同的起始和末尾节点
class Solution {
public int findUnsortedSubarray(int[] nums) {
int len=nums.length;
if (isSort(nums))return 0;
int[] numsort=new int[len];
for (int i = 0; i < len; i++) {
numsort[i]=nums[i];
}
Arrays.sort(numsort);
int left=0;int right=len-1;
while (numsort[left]==nums[left]){
left++;
}
while (numsort[right]==nums[right]){
right--;
}
return right-left+1;
}
public boolean isSort(int[] nums){
int len=nums.length;
for (int i = 0; i < len-1; i++) {
if (nums[i]>nums[i+1]){
return false;
}
}
return true;
}
}
注意end,start的初始值
如果为升序,最终结果为0,又因为end=-1,所以start要为0;
public static int findUnsortedSubarray(int[] nums) {
int len=nums.length;
int end=-1,start=0;
int min=nums[len-1],max=nums[0];
for (int i = 0; i < len; i++) {
//从左往右找右边界最大值,逆序记录右边界
if (nums[i] < max) {
end = i;
} else {
//正常情况记录最大值
max = nums[i];
}
//找左边界最小值,逆序时记录左边界
if (nums[len-1-i]>min){
start=len-1-i;
}else {//正常时记录最小值
min=nums[len-1-i];
}
}
//如果为升序,最终结果为0,又因为end=-1,所以start要为0;
return end-start+1;
}
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1==null) return root2;
if (root2==null) return root1;
TreeNode merge=new TreeNode(root1.val+ root2.val);
merge.left=mergeTrees(root1.left,root2.left);
merge.right=mergeTrees(root1.right,root2.right);
return merge;
}
public int leastInterval(char[] tasks, int n) {
int[] ch=new int[26];//记录每个任务出现的次数
int maxTask=0;//出现最大次数的任务个数(列数)
int coutMaxTask=0;//(最后一行的列数)
for (int i = 0; i < tasks.length; i++) {
ch[tasks[i]-'A']++;
maxTask=Math.max(maxTask,ch[tasks[i]-'A']);
}
for (int i = 0; i < ch.length; i++) {
if (maxTask==ch[i]){
coutMaxTask++;
}
}
return Math.max(tasks.length,(maxTask-1)*(n+1)+coutMaxTask);
}
方法一:动态规划
dp[i][j] 表示 [i,j] 子串是否是回文串,判断该子串是否是回文串需要两个条件:1. s[i] == s[j] 2. 掐头去尾看是否还是回文,即 dp[i+1][j-1] 是否是回文。意思就是 dp[i][j] 需要 dp[i+1][j-1] 来判断,即当前元素需要它的左下角元素来判断。正常我们是 for i 里面套 for j,这样循环是一层一层的,就出现了矛盾,即求 dp[i][j] 的时候 dp[i+1][j-1] 此时还是未知的
综上,所以我们 for j 里面套 for i,这样循环是一列一列的,求 dp[i][j] 的时候 dp[i+1][j-1] 已经知道了.
public int countSubstrings(String s) {
// 动态规划法
boolean[][] dp = new boolean[s.length()][s.length()];
int ans = 0;
for (int j = 0; j < s.length(); j++) {
for (int i = 0; i <= j; i++) {
if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
ans++;
}
}
}
return ans;
}
方法二:中心扩展法
public int countSubstrings(String s) {
int ans=0;
for (int center = 0; center < 2*s.length()-1; center++) {
int left=center/2;
int right=left+center%2;
while (left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){
ans++;
left--;//从中心扩散,左--,右++
right++;
}
}
return ans;
}
方法三:递归
class Solution647 {
int count=0;
public int countSubstrings(String s) {
for (int i = 0; i < s.length(); i++) {
for (int j = i; j <=i+1 ; j++) {
backing(s,i,j);//abc,既可以从b开始,也可以从ab开始
}
}
return count;
}
private void backing(String s, int left,int right) {
if (left<0 || right>=s.length()) return;
if (s.charAt(left)==s.charAt(right)){
count++;
backing(s,left-1,right+1);
}else return;
}
}
方法一:暴力
public int[] dailyTemperatures(int[] temperatures) {
int count=0;
int len=temperatures.length;
int[] ans=new int[len];
for (int i = 0; i < len-1; i++) {
for (int j = i+1; j < len; j++) {
if (temperatures[j]>temperatures[i]){
ans[i]=j-i;
break;
}
}
}
return ans;
}
方法二:递减栈
public int[] dailyTemperatures(int[] temperatures) {
int len=temperatures.length;
Stack<Integer> stack = new Stack<>();
int[] ans=new int[len];
//一次遍历,比当前栈顶元素小入栈,编号进栈
for (int i = 0; i < len; i++) {
while (!stack.isEmpty() && temperatures[i]>temperatures[stack.peek()]){//取栈顶
int pre=stack.pop();//出栈
ans[pre]=i-pre;
}
stack.add(i);
}
return ans;
}
public static String minWindow(String s, String t) {
int[] need=new int[128];//t中所需频率
int left=0,right=0,start=0;
int minLen=Integer.MAX_VALUE;
int needCnt=t.length();
for (int i = 0; i < t.length(); i++) {
need[t.charAt(i)]++;
}
while (right<s.length()){
char ch=s.charAt(right);
if (need[ch]>0){
needCnt--;
}
need[ch]--;//放入need数组
//needCnt等于0,说明t中所有元素被包含进去,则变化左边界
if (needCnt==0){
while (need[s.charAt(left)]<0){
need[s.charAt(left)]++;//先给need对于位置++,再left++
left++;
}
//更新最小距离,和记录的起始点
if (right-left+1<minLen){
minLen=right-left+1;
start=left;
}
need[s.charAt(left)]++;//先变回去,再left++
left++;
needCnt++;
}
right++;
}
return minLen==Integer.MAX_VALUE ? "":s.substring(start,start+minLen);
}
1月12日
stack存的是下标, 栈是单调递增栈,遇到比其小的都弹出, 比栈顶元素大的才直接入栈。
若栈顶元素大于等于当前遍历元素,则依次弹出,直到满足条件(当前大于栈顶时停止),更新左边界数组。
注意:刚开始都为右边界赋值len,因此当全为相同元素时,左边界为-1。(当heights[]={5,5,5},则left[]={-1,-1,-1},right[]={0,1,2})
public int largestRectangleArea(int[] heights) {
int len=heights.length;
int[] right=new int[len];
int[] left=new int[len];
Arrays.fill(right,len);//不要忘记赋值,最后一个元素的右边界
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < len; i++) {
//严格单调递增栈,
while (!stack.isEmpty() && heights[stack.peek()]>=heights[i]){
right[stack.peek()]=i;
stack.pop();
}
left[i]=stack.isEmpty()?-1:stack.peek();
stack.push(i);
}
int ans=0;
for (int i = 0; i < len; i++) {
ans=Math.max(ans,(right[i]-left[i]-1)*heights[i]);
}
return ans;
}
将此题转换为柱体,与84题的解法完全一样。
每更新一层,更新一次最大面积。
public int maximalRectangle(char[][] matrix) {
int row=matrix.length;
if (row==0) return 0;
int col=matrix[0].length;
int maxArea=0;
int[] height=new int[col];
//计算高度
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (matrix[i][j]=='1'){
height[j]++;
}else {
height[j]=0;
}
}
//位置要放在for循环内,内更新一层计算一次最大面面积
maxArea=Math.max(maxArea,largestRectangleArea(height));
}
return maxArea;
}
//84题解
public int largestRectangleArea(int[] height){
Deque<Integer> stack = new ArrayDeque<>();
int len=height.length;
int[] left=new int[len];
int[] right=new int[len];
Arrays.fill(right,len);//不要忘记赋值,最后一个元素的右边界
for (int i = 0; i < len; i++) {
//栈顶元素大于等于当前元素,弹出(完全单调递增栈)
while (!stack.isEmpty() && height[stack.peek()]>=height[i]) {
right[stack.peek()] = i;
stack.pop();
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
int maxArea=0;
for (int i = 0; i < len; i++) {
maxArea=Math.max(maxArea,(right[i]-left[i]-1)*height[i]);
}
return maxArea;
}
maxGain返回的值,因为要和再上一层的节点相连,只能选当前根节点+左/右(只能选左或者右或者都不选,因为如果是左中右则是闭环,没办法加上一层
左中右的值记录在maxSum中,最终主函数返回的maxSum
int maxSum=Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return maxSum;
}
public int maxGain(TreeNode node){
if (node==null)return 0;
//如果左右节点最大贡献值为负数,则舍弃(只要根节点,不要左右负节点)
int leftGain=Math.max(maxGain(node.left),0);
int rightGain=Math.max(maxGain(node.right),0);
//节点做大路径取决于,当前节点和左右子树的和
int lmr= node.val+leftGain+rightGain;//左中右
maxSum=Math.max(maxSum,lmr);
//因为要和再上一层的节点相连,只能选当前根节点+左/右(只能选左或者右或者都不选,因为如果是左中右则是闭环,没办法加上一层)
return node.val+Math.max(leftGain,rightGain);
}
1月14日
统计要移除括号的个数,再回溯,移除括号(回溯考虑停止条件)。
回溯的时候,把最终结果res不要当参数传递,容易出错,写成全局变量。
class Solution {
List<String> res = new ArrayList<>();
public List<String> removeInvalidParentheses(String s) {
//1.统计要移除括号的个数
int lr = 0;//leftRemove移除左括号的个数
int rr = 0;//rightRemove移除右括号的个数
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') lr++;
//除了左括号、右括号,还有字母呢
if (s.charAt(i) == ')') {
if (lr > 0) lr--;
else rr++;
}
}
//2.回溯,移除括号
backTrack(0, lr, rr, s);
return res;
}
//回溯,移除括号
//回溯的时候,把最终结果res不要当参数传递,容易出错,写成全局变量
public void backTrack(int start, int lr, int rr, String s) {
//完成条件
if (lr == 0 && rr == 0) {
if (isVaild(s)) res.add(s);
}
for (int i = start; i < s.length(); i++) {
//重复情况()),此时两个右括号,去掉第一个和第二个结果一样,若都遍历一次,则结果重复
//if (i < s.length() - 1 && s.charAt(i) == s.charAt(i + 1)) continue;//此处))不通过
if(i > start && s.charAt(i) == s.charAt(i - 1))continue;
if (s.charAt(i) == '(' && lr > 0) {
backTrack(i, lr - 1, rr, s.substring(0, i) + s.substring(i + 1));
}
if (s.charAt(i) == ')' && rr > 0) {
backTrack(i, lr, rr - 1, s.substring(0, i) + s.substring(i + 1));
}
}
}
//判断是否为有效括号
public boolean isVaild(String s) {
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
if (!stack.isEmpty() && s.charAt(i) == ')' && stack.peek() == '(') {
stack.pop();
continue;
}
if (s.charAt(i) != '(' && s.charAt(i) != ')') continue;
stack.push(s.charAt(i));
}
return stack.isEmpty();
}
}
反序列化
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root==null) return "None,";//可换为任意字符,*、A、X、None
String leftSerialize=serialize(root.left);
String rightSerialize=serialize(root.right);
return root.val+","+leftSerialize+rightSerialize;
}
/*按照前序遍历的顺序:先构建根节点,再构建左子树、右子树
* 将 list 数组的首项弹出,它是当前子树的 root 节点
* 如果它为 'None' ,返回 null
* 如果它不为 'None',则为它创建节点,并递归调用 buildTree 构建左右子树,当前子树构建完毕,向上返回
*/
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] split = data.split(",");
Deque<String> deque = new LinkedList<>(Arrays.asList(split));
return buildTree(deque);
}
private TreeNode buildTree(Deque<String> deque){
String s=deque.poll();
if (s.equals("None")) return null;
TreeNode node = new TreeNode(Integer.parseInt(s));
node.left=buildTree(deque);
node.right=buildTree(deque);
return node;
}
}
public int lengthOfLIS(int[] nums) {
if (nums.length==0) return 0;
int dp[] = new int[nums.length];
Arrays.fill(dp, 1);
int res=0;
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
dp[i]=Math.max(dp[i], dp[j]+1);
}
}
res=Math.max(res,dp[i]);
}
return res;//选出dp中最大的
}