diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md index ace6bc7d..f3c00536 100644 --- a/problems/0135.分发糖果.md +++ b/problems/0135.分发糖果.md @@ -132,30 +132,33 @@ public: Java: ```java class Solution { + /** + 分两个阶段 + 1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1 + 2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,此时 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值,这样才符合 它比它左边的大,也比它右边大 + */ public int candy(int[] ratings) { - int[] candy = new int[ratings.length]; - for (int i = 0; i < candy.length; i++) { - candy[i] = 1; - } - + int[] candyVec = new int[ratings.length]; + candyVec[0] = 1; for (int i = 1; i < ratings.length; i++) { if (ratings[i] > ratings[i - 1]) { - candy[i] = candy[i - 1] + 1; + candyVec[i] = candyVec[i - 1] + 1; + } else { + candyVec[i] = 1; } } for (int i = ratings.length - 2; i >= 0; i--) { if (ratings[i] > ratings[i + 1]) { - candy[i] = Math.max(candy[i],candy[i + 1] + 1); + candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1); } } - int count = 0; - for (int i = 0; i < candy.length; i++) { - count += candy[i]; + int ans = 0; + for (int s : candyVec) { + ans += s; } - - return count; + return ans; } } ``` diff --git a/problems/0143.重排链表.md b/problems/0143.重排链表.md index c5ac9bae..a6412d2e 100644 --- a/problems/0143.重排链表.md +++ b/problems/0143.重排链表.md @@ -50,10 +50,6 @@ public: cur = cur->next; count++; } - if (vec.size() % 2 == 0) { // 如果是偶数,还要多处理中间的一个 - cur->next = vec[i]; - cur = cur->next; - } cur->next = nullptr; // 注意结尾 } }; @@ -249,12 +245,6 @@ public class ReorderList { cur = cur.next; count++; } - // 当是偶数的话,需要做额外处理 - if (list.size() % 2== 0){ - cur.next = list.get(l); - cur = cur.next; - } - // 注意结尾要结束一波 cur.next = null; } @@ -376,11 +366,6 @@ var reorderList = function(head, s = [], tmp) { cur = cur.next; count++; } - // 当是偶数的话,需要做额外处理 - if(list.length % 2 == 0){ - cur.next = list[l]; - cur = cur.next; - } // 注意结尾要结束一波 cur.next = null; } diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md index ec6f3dca..3e2a22be 100644 --- a/problems/0206.翻转链表.md +++ b/problems/0206.翻转链表.md @@ -164,6 +164,25 @@ class Solution { } ``` +```java +// 从后向前递归 +class Solution { + ListNode reverseList(ListNode head) { + // 边缘条件判断 + if(head == null) return null; + if (head.next == null) return head; + + // 递归调用,翻转第二个节点开始往后的链表 + ListNode last = reverseList(head.next); + // 翻转头节点与第二个节点的指向 + head.next.next = head; + // 此时的 head 节点为尾节点,next 需要指向 NULL + head.next = null; + return last; + } +} +``` + Python迭代法: ```python #双指针 diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index 204c3815..79b3b485 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -370,46 +370,39 @@ class Solution: Go: -树形DP +动态规划 ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ func rob(root *TreeNode) int { - return max(robTree(root)) + res := robTree(root) + return max(res[0], res[1]) } -func robTree(root *TreeNode)(int,int){ - if root==nil{ - return 0,0 - } - //获取左节点的偷的值与不偷的值 - left0,left1:=robTree(root.Left) - //获取右节点的偷的值与不偷的值 - right0,right1:=robTree(root.Right) - //偷 - val1:=root.Val - val1+=left1+right1 - //不偷 - val2:=0 - val2+=max(left0,left1)+max(right0,right1) - return val1,val2 + +func max(a, b int) int { + if a > b { + return a + } + return b } -func max(a,b int)int{ - if a>b{ - return a - } - return b + +func robTree(cur *TreeNode) []int { + if cur == nil { + return []int{0, 0} + } + // 后序遍历 + left := robTree(cur.Left) + right := robTree(cur.Right) + + // 考虑去偷当前的屋子 + robCur := cur.Val + left[0] + right[0] + // 考虑不去偷当前的屋子 + notRobCur := max(left[0], left[1]) + max(right[0], right[1]) + + // 注意顺序:0:不偷,1:去偷 + return []int{notRobCur, robCur} } ``` - - JavaScript: > 动态规划 diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 05c272c6..e5750ff7 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -226,6 +226,32 @@ class Solution: return taraget == dp[taraget] ``` Go: +```go +// 分割等和子集 动态规划 +// 时间复杂度O(n^2) 空间复杂度O(n) +func canPartition(nums []int) bool { + sum := 0 + for _, num := range nums { + sum += num + } + // 如果 nums 的总和为奇数则不可能平分成两个子集 + if sum % 2 == 1 { + return false + } + + target := sum / 2 + dp := make([]int, target + 1) + + for _, num := range nums { + for j := target; j >= num; j-- { + if dp[j] < dp[j - num] + num { + dp[j] = dp[j - num] + num + } + } + } + return dp[target] == target +} +``` ```go func canPartition(nums []int) bool { diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md index 64472506..ba0e7e3b 100644 --- a/problems/0707.设计链表.md +++ b/problems/0707.设计链表.md @@ -282,12 +282,12 @@ Java: ```Java //单链表 class ListNode { -int val; -ListNode next; -ListNode(){} -ListNode(int val) { -this.val=val; -} + int val; + ListNode next; + ListNode(){} + ListNode(int val) { + this.val=val; + } } class MyLinkedList { //size存储链表元素的个数 diff --git a/problems/0763.划分字母区间.md b/problems/0763.划分字母区间.md index 863b569d..c64ff3c8 100644 --- a/problems/0763.划分字母区间.md +++ b/problems/0763.划分字母区间.md @@ -88,15 +88,15 @@ Java: class Solution { public List partitionLabels(String S) { List list = new LinkedList<>(); - int[] edge = new int[123]; + int[] edge = new int[26]; char[] chars = S.toCharArray(); for (int i = 0; i < chars.length; i++) { - edge[chars[i] - 0] = i; + edge[chars[i] - 'a'] = i; } int idx = 0; int last = -1; for (int i = 0; i < chars.length; i++) { - idx = Math.max(idx,edge[chars[i] - 0]); + idx = Math.max(idx,edge[chars[i] - 'a']); if (i == idx) { list.add(i - last); last = i; diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md index 0d9f2ac1..883b2f16 100644 --- a/problems/0977.有序数组的平方.md +++ b/problems/0977.有序数组的平方.md @@ -27,7 +27,7 @@ ## 暴力排序 -最直观的相反,莫过于:每个数平方之后,排个序,美滋滋,代码如下: +最直观的想法,莫过于:每个数平方之后,排个序,美滋滋,代码如下: ```CPP class Solution { diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md index 09e49c4f..44a02ceb 100644 --- a/problems/1002.查找常用字符.md +++ b/problems/1002.查找常用字符.md @@ -224,10 +224,7 @@ javaScript var commonChars = function (words) { let res = [] let size = 26 - let firstHash = new Array(size) - for (let i = 0; i < size; i++) { // 初始化 hash 数组 - firstHash[i] = 0 - } + let firstHash = new Array(size).fill(0) // 初始化 hash 数组 let a = "a".charCodeAt() let firstWord = words[0] @@ -235,21 +232,20 @@ var commonChars = function (words) { let idx = firstWord[i].charCodeAt() firstHash[idx - a] += 1 } - + + let otherHash = new Array(size).fill(0) // 初始化 hash 数组 for (let i = 1; i < words.length; i++) { // 1-n 个单词统计 - let otherHash = new Array(size) - for (let i = 0; i < size; i++) { // 初始化 hash 数组 - otherHash[i] = 0 - } - for (let j = 0; j < words[i].length; j++) { let idx = words[i][j].charCodeAt() otherHash[idx - a] += 1 } + for (let i = 0; i < size; i++) { firstHash[i] = Math.min(firstHash[i], otherHash[i]) } + otherHash.fill(0) } + for (let i = 0; i < size; i++) { while (firstHash[i] > 0) { res.push(String.fromCharCode(i + a)) diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md index 29243c6d..60f7115d 100644 --- a/problems/剑指Offer58-II.左旋转字符串.md +++ b/problems/剑指Offer58-II.左旋转字符串.md @@ -200,17 +200,14 @@ func reverse(b []byte, left, right int){ JavaScript: ```javascript -var reverseLeftWords = function (s, n) { - const reverse = (str, left, right) => { - let strArr = str.split(""); - for (; left < right; left++, right--) { - [strArr[left], strArr[right]] = [strArr[right], strArr[left]]; - } - return strArr.join(""); - } - s = reverse(s, 0, n - 1); - s = reverse(s, n, s.length - 1); - return reverse(s, 0, s.length - 1); +var reverseLeftWords = function(s, n) { + const length = s.length; + let i = 0; + while (i < length - n) { + s = s[length - 1] + s; + i++; + } + return s.slice(0, length); }; ``` diff --git a/problems/周总结/20200927二叉树周末总结.md b/problems/周总结/20200927二叉树周末总结.md index 5a7e398a..ff8f67d4 100644 --- a/problems/周总结/20200927二叉树周末总结.md +++ b/problems/周总结/20200927二叉树周末总结.md @@ -51,8 +51,8 @@ morris遍历是二叉树遍历算法的超强进阶算法,morris遍历可以 在[二叉树:一入递归深似海,从此offer是路人](https://programmercarl.com/二叉树的递归遍历.html)中讲到了递归三要素,以及前中后序的递归写法。 文章中我给出了leetcode上三道二叉树的前中后序题目,但是看完[二叉树:一入递归深似海,从此offer是路人](https://programmercarl.com/二叉树的递归遍历.html),依然可以解决n叉树的前后序遍历,在leetcode上分别是 -* 589. N叉树的前序遍历 -* 590. N叉树的后序遍历 +* [589. N叉树的前序遍历](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/) +* [590. N叉树的后序遍历](https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/) 大家可以再去把这两道题目做了。 diff --git a/problems/算法模板.md b/problems/算法模板.md index 216ae6b6..9fa4f1a8 100644 --- a/problems/算法模板.md +++ b/problems/算法模板.md @@ -248,7 +248,7 @@ void backtracking(参数) { ## 并查集 ```CPP - int n = 1005; // 更具题意而定 + int n = 1005; // 根据题意而定 int father[1005]; // 并查集初始化 @@ -280,6 +280,263 @@ void backtracking(参数) { (持续补充ing) ## 其他语言版本 +JavaScript: + +## 二分查找法 + +使用左闭右闭区间 + +```javascript +var search = function (nums, target) { + let left = 0, right = nums.length - 1; + // 使用左闭右闭区间 + while (left <= right) { + let mid = left + Math.floor((right - left)/2); + if (nums[mid] > target) { + right = mid - 1; // 去左面闭区间寻找 + } else if (nums[mid] < target) { + left = mid + 1; // 去右面闭区间寻找 + } else { + return mid; + } + } + return -1; +}; +``` + +使用左闭右开区间 + +```javascript +var search = function (nums, target) { + let left = 0, right = nums.length; + // 使用左闭右开区间 [left, right) + while (left < right) { + let mid = left + Math.floor((right - left)/2); + if (nums[mid] > target) { + right = mid; // 去左面闭区间寻找 + } else if (nums[mid] < target) { + left = mid + 1; // 去右面闭区间寻找 + } else { + return mid; + } + } + return -1; +}; +``` + +## KMP + +```javascript +var kmp = function (next, s) { + next[0] = -1; + let j = -1; + for(let i = 1; i < s.length; i++){ + while (j >= 0 && s[i] !== s[j + 1]) { + j = next[j]; + } + if (s[i] === s[j + 1]) { + j++; + } + next[i] = j; + } +} +``` + +## 二叉树 + +### 深度优先遍历(递归) + +二叉树节点定义: + +```javascript +function TreeNode (val, left, right) { + this.val = (val === undefined ? 0 : val); + this.left = (left === undefined ? null : left); + this.right = (right === undefined ? null : right); +} +``` + +前序遍历(中左右): + +```javascript +var preorder = function (root, list) { + if (root === null) return; + list.push(root.val); // 中 + preorder(root.left, list); // 左 + preorder(root.right, list); // 右 +} +``` + +中序遍历(左中右): + +```javascript +var inorder = function (root, list) { + if (root === null) return; + inorder(root.left, list); // 左 + list.push(root.val); // 中 + inorder(root.right, list); // 右 +} +``` + +后序遍历(左右中): + +```javascript +var postorder = function (root, list) { + if (root === null) return; + postorder(root.left, list); // 左 + postorder(root.right, list); // 右 + list.push(root.val); // 中 +} +``` + +### 深度优先遍历(迭代) + +前序遍历(中左右): + +```javascript +var preorderTraversal = function (root) { + let res = []; + if (root === null) return rs; + let stack = [root], + cur = null; + while (stack.length) { + cur = stack.pop(); + res.push(cur.val); + cur.right && stack.push(cur.right); + cur.left && stack.push(cur.left); + } + return res; +}; +``` + +中序遍历(左中右): + +```javascript +var inorderTraversal = function (root) { + let res = []; + if (root === null) return res; + let stack = []; + let cur = root; + while (stack.length !== 0 || cur !== null) { + if (cur !== null) { + stack.push(cur); + cur = cur.left; + } else { + cur = stack.pop(); + res.push(cur.val); + cur = cur.right; + } + } + return res; +}; +``` + +后序遍历(左右中): + +```javascript +var postorderTraversal = function (root) { + let res = []; + if (root === null) return res; + let stack = [root]; + let cur = null; + while (stack.length) { + cur = stack.pop(); + res.push(cur.val); + cur.left && stack.push(cur.left); + cur.right && stack.push(cur.right); + } + return res.reverse() +}; +``` + +### 广度优先遍历(队列) + +```javascript +var levelOrder = function (root) { + let res = []; + if (root === null) return res; + let queue = [root]; + while (queue.length) { + let n = queue.length; + let temp = []; + for (let i = 0; i < n; i++) { + let node = queue.shift(); + temp.push(node.val); + node.left &&queue.push(node.left); + node.right && queue.push(node.right); + } + res.push(temp); + } + return res; +}; +``` + +### 二叉树深度 + +```javascript +var getDepth = function (node) { + if (node === null) return 0; + return 1 + Math.max(getDepth(node.left), getDepth(node.right)); +} +``` + +### 二叉树节点数量 + +```javascript +var countNodes = function (root) { + if (root === null) return 0; + return 1 + countNodes(root.left) + countNodes(root.right); +} +``` + +## 回溯算法 + +```javascript +function backtracking(参数) { + if (终止条件) { + 存放结果; + return; + } + + for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { + 处理节点; + backtracking(路径,选择列表); // 递归 + 回溯,撤销处理结果 + } +} + +``` + +## 并查集 + +```javascript + let n = 1005; // 根据题意而定 + let father = new Array(n).fill(0); + + // 并查集初始化 + function init () { + for (int i = 0; i < n; ++i) { + father[i] = i; + } + } + // 并查集里寻根的过程 + function find (u) { + return u === father[u] ? u : father[u] = find(father[u]); + } + // 将v->u 这条边加入并查集 + function join(u, v) { + u = find(u); + v = find(v); + if (u === v) return ; + father[v] = u; + } + // 判断 u 和 v是否找到同一个根 + function same(u, v) { + u = find(u); + v = find(v); + return u === v; + } +``` Java: