LeetCode练习及自己理解记录(1)
文章目錄
- LeetCode練習及自己理解記錄(1)
- 516. 最長回文子序列
- 148. 排序鏈表
- 56. 合并區間
- 57. 插入區間
- 102. 二叉樹的層序遍歷
- 107. 二叉樹的層序遍歷 II
- 662. 二叉樹最大寬度
- 559. N 叉樹的最大深度
- 889. 根據前序和后序遍歷構造二叉樹
- 450. 刪除二叉搜索樹中的節點
- 1361. 驗證二叉樹
- 101. 對稱二叉樹
LeetCode練習及自己理解記錄(1)
516. 最長回文子序列
來源:https://leetcode-cn.com/problems/longest-palindromic-subsequence/solution/dai-ma-sui-xiang-lu-dai-ni-xue-tou-dpzi-dv83q/
1、確定dp數組以及其下標的含義
dp[i][j]:字符串s在[i, j]范圍內最長的回文子序列的長度為dp[i][j]。
2、確定遞推公式
如果s[i]與s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;
因為相同的話子序列里就有2個數字,也就是2個長度
如果s[i]與s[j]不相同,說明s[i]和s[j]的同時加入并不能增加[i,j]區間回文子串的長度,那么分別加入s[i]、s[j]看看哪一個可以組成最長的回文子序列。
加入s[j]的回文子序列長度為dp[i + 1][j]。
加入s[i]的回文子序列長度為dp[i][j - 1]。
那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
3、dp數組如何初始化
首先要考慮當i 和j 相同的情況,從遞推公式:dp[i][j] = dp[i + 1][j - 1] + 2; 可以看出 遞推公式是計算不到 i 和j相同時候的情況。
所以需要手動初始化一下,當i與j相同,那么dp[i][j]一定是等于1的,即:一個字符的回文子序列長度就是1。
其他情況dp[i][j]初始為0就行,這樣遞推公式:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 中dp[i][j]才不會被初始值覆蓋。
4、確定遍歷順序
從遞推公式dp[i][j] = dp[i + 1][j - 1] + 2 和 dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) 可以看出,dp[i][j]是依賴于dp[i + 1][j - 1] 和 dp[i + 1][j],
也就是從矩陣的角度來說,dp[i][j] 下一行的數據。 所以遍歷i的時候一定要從下到上遍歷,這樣才能保證,下一行的數據是經過計算的。
遞推公式:dp[i][j] = dp[i + 1][j - 1] + 2,dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) 分別對應著下圖中的紅色箭頭方向,如圖:
5、舉例推導dp數組
如果上面過程沒懂可以結合上面這個田字格的圖形去看,看箭頭流向的一個過程,就應該能明白整個動態規劃的過程了。
鏈接: https://pan.baidu.com/s/1E08ocgXThceqWEddOICoyg 密碼: ts8t 推薦其寫的關于動態規劃的文章。
148. 排序鏈表
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/ class Solution {public ListNode sortList(ListNode head) {return mergeSort(head,null);}public ListNode mergeSort(ListNode head,ListNode tail){if(head == null){return head;}if (head.next == tail) { //這里剛開始忽略了head.next = null;return head;}//找出中點ListNode slow = head, fast = head;while (fast != tail) {slow = slow.next;fast = fast.next;if (fast != tail) {fast = fast.next;}}ListNode mid = slow;ListNode l1=mergeSort(head,mid);ListNode l2=mergeSort(mid,tail);ListNode sorted = merge(l1,l2);return sorted;}public ListNode merge(ListNode head1,ListNode head2){ListNode dumpHead = new ListNode(0);ListNode temp=dumpHead,temp1=head1,temp2=head2;while(temp1 != null && temp2 != null){if(temp1.val <= temp2.val){temp.next = temp1;temp1 = temp1.next;}else{temp.next = temp2;temp2 = temp2.next;}temp = temp.next;}if (temp1 != null) {temp.next = temp1;} else if (temp2 != null) {temp.next = temp2;}return dumpHead.next;} }采用歸并排序(遞歸)的方式,只是這種節點找中點的方式第一次用(快慢指針),偶數則是中點的后一個,奇數則是中點,上面忽略的地方是關鍵,否則會陷入死循環。合并就是采用比較兩個節點的值的大小,最后返回有序鏈表的頭節點。
56. 合并區間
class Solution {public int[][] merge(int[][] intervals) {if(intervals.length < 2){return intervals;}Arrays.sort(intervals, new Comparator<int[]>() { //新知識public int compare(int[] a, int[] b) {if(a[0] == b[0]) {return a[1] - b[1];}return a[0] - b[0];}});List<int[]> result = new ArrayList<int[]>();for(int i=0;i<=intervals.length-1;i++){int L = intervals[i][0],R = intervals[i][1];if(result.size() == 0 || result.get(result.size()-1)[1] < L){result.add(intervals[i]);}else{result.get(result.size()-1)[1] = Math.max(result.get(result.size()-1)[1], R);}}return result.toArray(new int[result.size()][]);} }上面就是直接用Arrays對二維數組排序,重寫Comparator方法(第一次用,學到了),然后比較排序過后的[L,R],通過比較添加到ArrayList中,所以開始不要直接建二維數組,要建立鏈表,因為數組的大小是固定的。
57. 插入區間
class Solution {public int[][] insert(int[][] intervals, int[] newInterval) {List<int[]> list = new ArrayList<int[]>();List<int[]> result = new ArrayList<int[]>();for(int i=0;i<intervals.length;i++){list.add(intervals[i]);}list.add(newInterval);int[][] arr = list.toArray(new int[list.size()][]);Arrays.sort(arr,new Comparator<int[]>() {public int compare(int[] a,int[] b) {return a[0]-b[0];}});for(int i=0;i<arr.length;i++){int L = arr[i][0],R = arr[i][1];if(result.size() == 0 || result.get(result.size()-1)[1] < L){result.add(arr[i]);}else{result.get(result.size()-1)[1] = Math.max(result.get(result.size()-1)[1], R);}} return result.toArray(new int[result.size()][]);} }以上是在56題上改進的,不過不是最優,后面在題解里面看到更好的:
class Solution {public int[][] insert(int[][] intervals, int[] newInterval) {int left = newInterval[0];int right = newInterval[1];boolean placed = false;List<int[]> ansList = new ArrayList<int[]>();for (int[] interval : intervals) {if (interval[0] > right) {// 在插入區間的右側且無交集if (!placed) {ansList.add(new int[]{left, right});placed = true; }ansList.add(interval);} else if (interval[1] < left) {// 在插入區間的左側且無交集ansList.add(interval);} else {// 與插入區間有交集,計算它們的并集left = Math.min(left, interval[0]);right = Math.max(right, interval[1]);}}if (!placed) {ansList.add(new int[]{left, right});}int[][] ans = new int[ansList.size()][2];for (int i = 0; i < ansList.size(); ++i) {ans[i] = ansList.get(i);}return ans;} }102. 二叉樹的層序遍歷
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/ class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> list = new ArrayList<>();if(root == null) return list;List<Integer> temp = new ArrayList<>();temp.add(root.val);list.add(temp);Queue<TreeNode> queue = new LinkedList<>();Queue<TreeNode> queueTemp = new LinkedList<>();queue.offer(root);int levelSize = 1;while(!queue.isEmpty()){TreeNode node = queue.poll();levelSize--;if(node.left != null){queue.offer(node.left);queueTemp.offer(node.left);}if(node.right != null){queue.offer(node.right);queueTemp.offer(node.right);}if(levelSize == 0){levelSize = queue.size();List<Integer> temp2 = new ArrayList<>();while(!queueTemp.isEmpty()){TreeNode nodeTemp = queueTemp.poll();temp2.add(nodeTemp.val);}if(!temp2.isEmpty()){list.add(temp2);}}}return list;} }107. 二叉樹的層序遍歷 II
class Solution {public List<List<Integer>> levelOrderBottom(TreeNode root) {List<List<Integer>> list = new ArrayList<>();if(root == null) return list;List<Integer> temp = new ArrayList<>();temp.add(root.val);list.add(0,temp); //get到了一個新方法Queue<TreeNode> queue = new LinkedList<>();Queue<TreeNode> queueTemp = new LinkedList<>();queue.offer(root);int levelSize = 1;while(!queue.isEmpty()){TreeNode node = queue.poll();levelSize--;if(node.left != null){queue.offer(node.left);queueTemp.offer(node.left);}if(node.right != null){queue.offer(node.right);queueTemp.offer(node.right);}if(levelSize == 0){levelSize = queue.size();List<Integer> temp2 = new ArrayList<>();while(!queueTemp.isEmpty()){TreeNode nodeTemp = queueTemp.poll();temp2.add(nodeTemp.val);}if(!temp2.isEmpty()){list.add(0,temp2);}}}return list;} }662. 二叉樹最大寬度
用到了雙向隊列。才可有getFirst()和getLsat()方法
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/ class Solution {public int widthOfBinaryTree(TreeNode root) {if(root == null) return 0;// Queue<TreeNode> queue = new LinkedList<>();Deque<TreeNode> queue = new ArrayDeque<>();//要雙向隊列才可root.val = 0;queue.offer(root);int res = 0;while(!queue.isEmpty()){int size = queue.size(); res = Math.max(res,queue.getLast().val-queue.getFirst().val+1);for(int i=0;i<size;i++){TreeNode node = queue.poll();if(node.left != null){node.left.val = 2*node.val;queue.offer(node.left);}if(node.right != null){node.right.val = 2*node.val+1;queue.offer(node.right);}}}return res;} }559. N 叉樹的最大深度
/* // Definition for a Node. class Node {public int val;public List<Node> children;public Node() {}public Node(int _val) {val = _val;}public Node(int _val, List<Node> _children) {val = _val;children = _children;} }; */class Solution {public int maxDepth(Node root) {if(root == null) return 0;Queue<Node> queue = new LinkedList<>();queue.offer(root);int height = 0;int levelSize = 1;int sizetemp = 0;while(!queue.isEmpty()){Node node = queue.poll();levelSize--;for(int i=0;i<node.children.size();i++){if(node.children.get(i) != null){queue.offer(node.children.get(i));sizetemp++; //記錄每層的個數}}if(levelSize == 0){height++;levelSize = sizetemp; //重新賦值sizetemp = 0; //重新置為0}}return height;} }上面是自己的想法,跟題解的方法不太一樣,不過運行效率差了點。
889. 根據前序和后序遍歷構造二叉樹
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/ class Solution {public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {if(preorder == null || preorder.length == 0){return null;}return dfs(preorder,postorder);}public TreeNode dfs(int[] preorder, int[] postorder){if(preorder == null || preorder.length == 0){return null;}//數組長度為1時,直接返回即可!!!!!!if(preorder.length==1) {return new TreeNode(preorder[0]);}TreeNode root = new TreeNode(preorder[0]);int n = preorder.length;for(int i=0;i<postorder.length;i++){if(preorder[1] == postorder[i]){int line = i+1;int[] pre_left = Arrays.copyOfRange(preorder,1,line+1);int[] pre_right = Arrays.copyOfRange(preorder,line+1,n);int[] post_left = Arrays.copyOfRange(postorder,0,line);int[] post_right = Arrays.copyOfRange(postorder,line,n-1);root.left = dfs(pre_left,post_left);root.right = dfs(pre_right,post_right);break;}}return root;} }450. 刪除二叉搜索樹中的節點
class Solution {public int rightMin(TreeNode root) {//1.找到以某個結點為根節點的右子樹最小值。root = root.right;while (root.left != null) root = root.left;//1.1循環往左找到最大值。return root.val;}public int leftMax(TreeNode root) {//2.找到以某個結點為根節點的左子樹最大值。root = root.left;while (root.right != null) root = root.right;return root.val;}public TreeNode deleteNode(TreeNode root, int key) {if (root == null) {//3.遞歸終止條件return null;}if (key > root.val) {//4.如果查找的結點比根節點大,繼續在右子樹查找刪除該結點root.right = deleteNode(root.right, key);} else if (key < root.val) {//5.如果查找的結點比根節點小,繼續在左子樹查找刪除該結點root.left = deleteNode(root.left, key);} else {//6.如果找到了該結點,刪除它if (root.left == null && root.right == null) {//7.以葉子節點為根節點的二叉搜索樹只有一個元素,可以直接刪除。root = null;} else if (root.right != null) {//8.如果有右子樹,只要找到該右子樹的最小值來替換,之后將它刪除即可。root.val = rightMin(root);root.right = deleteNode(root.right, root.val);//9.將這個右子樹的最小值替換根節點,此時存在兩個相同節點,將這個節點刪除即可。} else {//10.如果有左子樹,只要找到該左子樹的最大值來替換,之后將它刪除即可。root.val = leftMax(root);root.left = deleteNode(root.left, root.val);//9.將這個左子樹的最大值替換根節點,此時存在兩個相同節點,將這個節點刪除即可。}}return root;} }1361. 驗證二叉樹
class Solution {public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {int[] findFather = new int[n]; //先找出入度為0的節點,即為rootfor(int i=0;i<n;i++){ if(leftChild[i] != -1){findFather[leftChild[i]]++;}if(rightChild[i] != -1){findFather[rightChild[i]]++;}}int root = -1;int count = 0;for(int i=0;i<n;i++){if(findFather[i] == 0){root = i;count++;}if(findFather[i] == 2){return false;}}if(root == -1 || count > 1){return false;}Queue<Integer> queue = new LinkedList<>();List<Integer> list = new ArrayList<>(); //記錄遍歷的節點queue.offer(root);list.add(root);while(!queue.isEmpty()){int node = queue.poll();if(leftChild[node] != -1){if(list.contains(leftChild[node])){return false;}list.add(leftChild[node]);queue.offer(leftChild[node]);}if(rightChild[node] != -1){if(list.contains(rightChild[node])){return false;}list.add(rightChild[node]);queue.offer(rightChild[node]);}}return list.size() == n;} }101. 對稱二叉樹
class Solution {public boolean isSymmetric(TreeNode root) {if(root == null) return true;return check(root.left,root.right); //遞歸檢查}public boolean check(TreeNode p,TreeNode q){if(p == null && q == null){return true;}if(p == null || q == null){return false;}return p.val == q.val && check(p.left,q.right) && check(p.right,q.left);} }總結
以上是生活随笔為你收集整理的LeetCode练习及自己理解记录(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 错误记录集锦(遇到则记下)
- 下一篇: LeetCode中常用语言的一些基本方法