mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-09 11:34:46 +08:00
merge
This commit is contained in:
38
README.md
38
README.md
@ -23,11 +23,17 @@
|
||||
|
||||
很多刚开始刷题的同学都有一个困惑:面对leetcode上近两千道题目,从何刷起。
|
||||
|
||||
大家平时刷题感觉效率低,浪费的时间主要在三点:
|
||||
|
||||
* 找题
|
||||
* 找到了不应该现阶段做的题
|
||||
* 没有全套的优质题解可以参考
|
||||
|
||||
其实我之前在知乎上回答过这个问题,回答内容大概是按照如下类型来刷数组-> 链表-> 哈希表->字符串->栈与队列->树->回溯->贪心->动态规划->图论->高级数据结构,再从简单刷起,做了几个类型题目之后,再慢慢做中等题目、困难题目。
|
||||
|
||||
但我能设身处地的感受到:即使有这样一个整体规划,对于一位初学者甚至算法老手寻找合适自己的题目也是很困难,时间成本很高,而且题目还不一定就是经典题目。
|
||||
|
||||
对于刷题,我们都是想用最短的时间把经典题目都做一篇,这样效率才是最高的!
|
||||
对于刷题,我们都是想用最短的时间**按照循序渐进的难度顺序把经典题目都做一遍**,这样效率才是最高的!
|
||||
|
||||
所以我整理了leetcode刷题攻略:一个超级详细的刷题顺序,**每道题目都是我精心筛选,都是经典题目高频面试题**,大家只要按照这个顺序刷就可以了,**你没看错,就是题目顺序都排好了,文章顺序就是刷题顺序!挨个刷就可以,不用自己再去题海里选题了!**
|
||||
|
||||
@ -49,15 +55,18 @@
|
||||
|
||||
**目前已经更新了,数组-> 链表-> 哈希表->字符串->栈与队列->树->回溯->贪心,八个专题了,正在讲解动态规划!**
|
||||
|
||||
在刷题指南中,每个专题开始都有理论基础篇,并不像是教科书般的理论介绍,而是从实战中归纳需要的基础知识。每个专题结束都有总结篇,最这个专题的归纳总结。
|
||||
在刷题攻略中,每个专题开始都有理论基础篇,并不像是教科书般的理论介绍,而是从实战中归纳需要的基础知识。每个专题结束都有总结篇,最这个专题的归纳总结。
|
||||
|
||||
如果你是算法老手,这篇攻略也是复习的最佳资料,如果把每个系列对应的总结篇,快速过一遍,整个算法知识体系以及各种解法就重现脑海了。
|
||||
|
||||
在按照如下顺序刷题的过程中,每一道题解一定要看对应文章下面的留言(留言目前只能在手机端查看)。
|
||||
|
||||
如果你有疑问或者发现文章哪里有不对的地方,都可以在留言区都能找到答案,还有很多录友的总结非常赞,看完之后也很有收获。
|
||||
目前「代码随想录」刷题攻略更新了:**200多篇文章,精讲了200道经典算法题目,共60w字的详细图解,部分难点题目还搭配了20分钟左右的视频讲解**。
|
||||
|
||||
目前「代码随想录」刷题指南更新了:**200多篇文章,精讲了200道经典算法题目,共60w字的详细图解,部分难点题目还搭配了20分钟左右的视频讲解**。
|
||||
**这里每一篇题解,都是精品,值得仔细琢磨**。
|
||||
|
||||
我在题目讲解中统一用C++语言,但你会发现下面几乎每篇题解都配有其他语言版本,Java、Python、Go、JavaScript等等,这正是热心小伙们的贡献的代码,当然我也会严格把控代码质量。
|
||||
|
||||
**所以也欢迎大家参与进来,完善题解的各个语言版本,拥抱开源,让更多小伙伴们收益**。
|
||||
|
||||
准备好了么,刷题攻略开始咯,go go go!
|
||||
|
||||
@ -66,7 +75,7 @@
|
||||
## 前序
|
||||
|
||||
* [「代码随想录」后序安排](https://mp.weixin.qq.com/s/4eeGJREy6E-v6D7cR_5A4g)
|
||||
* [「代码随想录」学习社区](https://mp.weixin.qq.com/s/X1XCH-KevURi3LnakJsCkA)
|
||||
* [「代码随想录」学习社区](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
|
||||
|
||||
* 编程语言
|
||||
@ -119,8 +128,9 @@
|
||||
4. [链表:听说过两天反转链表又写不出来了?](./problems/0206.翻转链表.md)
|
||||
5. [链表:两两交换链表中的节点](./problems/0024.两两交换链表中的节点.md)
|
||||
6. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md)
|
||||
7. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md)
|
||||
8. [链表:总结篇!](./problems/链表总结篇.md)
|
||||
7. [链表:链表相交](./problems/面试题02.07.链表相交.md)
|
||||
8. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md)
|
||||
9. [链表:总结篇!](./problems/链表总结篇.md)
|
||||
|
||||
## 哈希表
|
||||
|
||||
@ -156,11 +166,13 @@
|
||||
3. [字符串:替换空格](./problems/剑指Offer05.替换空格.md)
|
||||
4. [字符串:花式反转还不够!](./problems/0151.翻转字符串里的单词.md)
|
||||
5. [链表:听说过两天反转链表又写不出来了?](./problems/0206.翻转链表.md)
|
||||
6. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md)
|
||||
7. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md)
|
||||
8. [哈希表:解决了两数之和,那么能解决三数之和么?](./problems/0015.三数之和.md)
|
||||
9. [双指针法:一样的道理,能解决四数之和](./problems/0018.四数之和.md)
|
||||
10. [双指针法:总结篇!](./problems/双指针总结.md)
|
||||
6. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md)
|
||||
7. [链表:链表相交](./problems/面试题02.07.链表相交.md)
|
||||
8. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md)
|
||||
9. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md)
|
||||
10. [哈希表:解决了两数之和,那么能解决三数之和么?](./problems/0015.三数之和.md)
|
||||
11. [双指针法:一样的道理,能解决四数之和](./problems/0018.四数之和.md)
|
||||
12. [双指针法:总结篇!](./problems/双指针总结.md)
|
||||
|
||||
## 栈与队列
|
||||
|
||||
|
@ -162,6 +162,33 @@ class Solution {
|
||||
return deque.isEmpty();
|
||||
}
|
||||
}
|
||||
// 方法2
|
||||
class Solution {
|
||||
public boolean isValid(String s) {
|
||||
|
||||
Stack<Character> stack = new Stack<>();
|
||||
Map<Character, Character> map = new HashMap<Character, Character>() {
|
||||
{
|
||||
put('}', '{');
|
||||
put(']', '[');
|
||||
put(')', '(');
|
||||
}
|
||||
};
|
||||
|
||||
for (Character c : s.toCharArray()) { // 顺序读取字符
|
||||
if (!stack.isEmpty() && map.containsKey(c)) { // 是右括号 && 栈不为空
|
||||
if (stack.peek() == map.get(c)) { // 取其对应的左括号直接和栈顶比
|
||||
stack.pop(); // 相同则抵消,出栈
|
||||
} else {
|
||||
return false; // 不同则直接返回
|
||||
}
|
||||
} else {
|
||||
stack.push(c); // 左括号,直接入栈
|
||||
}
|
||||
}
|
||||
return stack.isEmpty(); // 看左右是否抵消完
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
@ -87,6 +87,46 @@ public:
|
||||
|
||||
Java:
|
||||
|
||||
```Java
|
||||
// 递归版本
|
||||
class Solution {
|
||||
public ListNode swapPairs(ListNode head) {
|
||||
// base case 退出提交
|
||||
if(head == null || head.next == null) return head;
|
||||
// 获取当前节点的下一个节点
|
||||
ListNode next = head.next;
|
||||
// 进行递归
|
||||
ListNode newNode = swapPairs(next.next);
|
||||
// 这里进行交换
|
||||
next.next = head;
|
||||
head.next = newNode;
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 虚拟头结点
|
||||
class Solution {
|
||||
public ListNode swapPairs(ListNode head) {
|
||||
|
||||
ListNode dummyNode = new ListNode(0);
|
||||
dummyNode.next = head;
|
||||
ListNode prev = dummyNode;
|
||||
|
||||
while (prev.next != null && prev.next.next != null) {
|
||||
ListNode temp = head.next.next; // 缓存 next
|
||||
prev.next = head.next; // 将 prev 的 next 改为 head 的 next
|
||||
head.next.next = head; // 将 head.next(prev.next) 的next,指向 head
|
||||
head.next = temp; // 将head 的 next 接上缓存的temp
|
||||
prev = head; // 步进1位
|
||||
head = head.next; // 步进1位
|
||||
}
|
||||
return dummyNode.next;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -116,7 +116,7 @@ public:
|
||||
|
||||
**大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1**。
|
||||
|
||||
```
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
int searchInsert(vector<int>& nums, int target) {
|
||||
@ -158,7 +158,7 @@ public:
|
||||
|
||||
**大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = middle**。
|
||||
|
||||
```
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
int searchInsert(vector<int>& nums, int target) {
|
||||
|
@ -175,7 +175,22 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def jump(self, nums: List[int]) -> int:
|
||||
if len(nums) == 1: return 0
|
||||
ans = 0
|
||||
curDistance = 0
|
||||
nextDistance = 0
|
||||
for i in range(len(nums)):
|
||||
nextDistance = max(i + nums[i], nextDistance)
|
||||
if i == curDistance:
|
||||
if curDistance != len(nums) - 1:
|
||||
ans += 1
|
||||
curDistance = nextDistance
|
||||
if nextDistance >= len(nums) - 1: break
|
||||
return ans
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
|
@ -149,6 +149,7 @@ public:
|
||||
Java:
|
||||
```java
|
||||
class Solution {
|
||||
|
||||
List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
|
||||
LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
|
||||
boolean[] used;
|
||||
@ -167,9 +168,6 @@ class Solution {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < nums.length; i++){
|
||||
// if (path.contains(nums[i])){
|
||||
// continue;
|
||||
// }
|
||||
if (used[i]){
|
||||
continue;
|
||||
}
|
||||
@ -187,6 +185,26 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
var result [][]int
|
||||
func backtrack(nums,pathNums []int,used []bool){
|
||||
if len(nums)==len(pathNums){
|
||||
tmp:=make([]int,len(nums))
|
||||
copy(tmp,pathNums)
|
||||
result=append(result,tmp)
|
||||
//result=append(result,pathNums)
|
||||
return
|
||||
}
|
||||
for i:=0;i<len(nums);i++{
|
||||
if !used[i]{
|
||||
used[i]=true
|
||||
pathNums=append(pathNums,nums[i])
|
||||
backtrack(nums,pathNums,used)
|
||||
pathNums=pathNums[:len(pathNums)-1]
|
||||
used[i]=false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Javascript:
|
||||
|
||||
|
@ -85,7 +85,7 @@ public:
|
||||
path.clear();
|
||||
sort(nums.begin(), nums.end()); // 排序
|
||||
vector<bool> used(nums.size(), false);
|
||||
backtracking(nums, vec, used);
|
||||
backtracking(nums, used);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
@ -363,7 +363,72 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
import "strings"
|
||||
var res [][]string
|
||||
|
||||
func isValid(board [][]string, row, col int) (res bool){
|
||||
n := len(board)
|
||||
for i:=0; i < row; i++ {
|
||||
if board[i][col] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := 0; i < n; i++{
|
||||
if board[row][i] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i ,j := row, col; i >= 0 && j >=0 ; i, j = i - 1, j- 1{
|
||||
if board[i][j] == "Q"{
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i, j := row, col; i >=0 && j < n; i,j = i-1, j+1 {
|
||||
if board[i][j] == "Q" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func backtrack(board [][]string, row int) {
|
||||
size := len(board)
|
||||
if row == size{
|
||||
temp := make([]string, size)
|
||||
for i := 0; i<size;i++{
|
||||
temp[i] = strings.Join(board[i],"")
|
||||
}
|
||||
res =append(res,temp)
|
||||
return
|
||||
}
|
||||
for col := 0; col < size; col++ {
|
||||
if !isValid(board, row, col){
|
||||
continue
|
||||
}
|
||||
board[row][col] = "Q"
|
||||
backtrack(board, row+1)
|
||||
board[row][col] = "."
|
||||
}
|
||||
}
|
||||
|
||||
func solveNQueens(n int) [][]string {
|
||||
res = [][]string{}
|
||||
board := make([][]string, n)
|
||||
for i := 0; i < n; i++{
|
||||
board[i] = make([]string, n)
|
||||
}
|
||||
for i := 0; i < n; i++{
|
||||
for j := 0; j<n;j++{
|
||||
board[i][j] = "."
|
||||
}
|
||||
}
|
||||
backtrack(board, 0)
|
||||
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -175,31 +175,25 @@ class Solution:
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func maxSubArray(nums []int) int {
|
||||
if len(nums)<1{
|
||||
return 0
|
||||
}
|
||||
dp:=make([]int,len(nums))
|
||||
result:=nums[0]
|
||||
dp[0]=nums[0]
|
||||
for i:=1;i<len(nums);i++{
|
||||
dp[i]=max(dp[i-1]+nums[i],nums[i])
|
||||
result=max(dp[i],result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func max(a,b int)int{
|
||||
if a>b{
|
||||
return a
|
||||
}else{
|
||||
return b
|
||||
```go
|
||||
func maxSubArray(nums []int) int {
|
||||
maxSum := nums[0]
|
||||
for i := 1; i < len(nums); i++ {
|
||||
if nums[i] + nums[i-1] > nums[i] {
|
||||
nums[i] += nums[i-1]
|
||||
}
|
||||
if nums[i] > maxSum {
|
||||
maxSum = nums[i]
|
||||
}
|
||||
}
|
||||
return maxSum
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -212,7 +212,45 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```Java
|
||||
class Solution {
|
||||
public int climbStairs(int n) {
|
||||
// 跟斐波那契数列一样
|
||||
if(n <= 2) return n;
|
||||
int a = 1, b = 2, sum = 0;
|
||||
|
||||
for(int i = 3; i <= n; i++){
|
||||
sum = a + b;
|
||||
a = b;
|
||||
b = sum;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 常规方式
|
||||
public int climbStairs(int n) {
|
||||
int[] dp = new int[n + 1];
|
||||
dp[0] = 1;
|
||||
dp[1] = 1;
|
||||
for (int i = 2; i <= n; i++) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
// 用变量记录代替数组
|
||||
public int climbStairs(int n) {
|
||||
int a = 0, b = 1, c = 0; // 默认需要1次
|
||||
for (int i = 1; i <= n; i++) {
|
||||
c = a + b; // f(i - 1) + f(n - 2)
|
||||
a = b; // 记录上一轮的值
|
||||
b = c; // 向后步进1个数
|
||||
}
|
||||
return c;
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -198,13 +198,71 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
```java
|
||||
public int minDistance(String word1, String word2) {
|
||||
int m = word1.length();
|
||||
int n = word2.length();
|
||||
int[][] dp = new int[m + 1][n + 1];
|
||||
// 初始化
|
||||
for (int i = 1; i <= m; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (int j = 1; j <= n; j++) {
|
||||
dp[0][j] = j;
|
||||
}
|
||||
for (int i = 1; i <= m; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
// 因为dp数组有效位从1开始
|
||||
// 所以当前遍历到的字符串的位置为i-1 | j-1
|
||||
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[m][n];
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```Go
|
||||
func minDistance(word1 string, word2 string) int {
|
||||
m, n := len(word1), len(word2)
|
||||
dp := make([][]int, m+1)
|
||||
for i := range dp {
|
||||
dp[i] = make([]int, n+1)
|
||||
}
|
||||
for i := 0; i < m+1; i++ {
|
||||
dp[i][0] = i // word1[i] 变成 word2[0], 删掉 word1[i], 需要 i 部操作
|
||||
}
|
||||
for j := 0; j < n+1; j++ {
|
||||
dp[0][j] = j // word1[0] 变成 word2[j], 插入 word1[j],需要 j 部操作
|
||||
}
|
||||
for i := 1; i < m+1; i++ {
|
||||
for j := 1; j < n+1; j++ {
|
||||
if word1[i-1] == word2[j-1] {
|
||||
dp[i][j] = dp[i-1][j-1]
|
||||
} else { // Min(插入,删除,替换)
|
||||
dp[i][j] = Min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[m][n]
|
||||
}
|
||||
func Min(args ...int) int {
|
||||
min := args[0]
|
||||
for _, item := range args {
|
||||
if item < min {
|
||||
min = item
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -373,6 +373,32 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
var res [][]int
|
||||
func combine(n int, k int) [][]int {
|
||||
res=[][]int{}
|
||||
if n <= 0 || k <= 0 || k > n {
|
||||
return res
|
||||
}
|
||||
backtrack(n, k, 1, []int{})
|
||||
return res
|
||||
}
|
||||
func backtrack(n,k,start int,track []int){
|
||||
if len(track)==k{
|
||||
temp:=make([]int,k)
|
||||
copy(temp,track)
|
||||
res=append(res,temp)
|
||||
}
|
||||
if len(track)+n-start+1 < k {
|
||||
return
|
||||
}
|
||||
for i:=start;i<=n;i++{
|
||||
track=append(track,i)
|
||||
backtrack(n,k,i+1,track)
|
||||
track=track[:len(track)-1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -189,7 +189,18 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
```Go
|
||||
func numTrees(n int)int{
|
||||
dp:=make([]int,n+1)
|
||||
dp[0]=1
|
||||
for i:=1;i<=n;i++{
|
||||
for j:=1;j<=i;j++{
|
||||
dp[i]+=dp[j-1]*dp[i-j]
|
||||
}
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -304,6 +304,35 @@ class Solution {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 简洁实现·递归解法
|
||||
class Solution {
|
||||
public boolean isValidBST(TreeNode root) {
|
||||
return validBST(Long.MIN_VALUE, Long.MAX_VALUE, root);
|
||||
}
|
||||
boolean validBST(long lower, long upper, TreeNode root) {
|
||||
if (root == null) return true;
|
||||
if (root.val <= lower || root.val >= upper) return false;
|
||||
return validBST(lower, root.val, root.left) && validBST(root.val, upper, root.right);
|
||||
}
|
||||
}
|
||||
// 简洁实现·中序遍历
|
||||
class Solution {
|
||||
private long prev = Long.MIN_VALUE;
|
||||
public boolean isValidBST(TreeNode root) {
|
||||
if (root == null) {
|
||||
return true;
|
||||
}
|
||||
if (!isValidBST(root.left)) {
|
||||
return false;
|
||||
}
|
||||
if (root.val <= prev) { // 不满足二叉搜索树条件
|
||||
return false;
|
||||
}
|
||||
prev = root.val;
|
||||
return isValidBST(root.right);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
@ -657,8 +657,36 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func levelOrder(root *TreeNode) [][]int {
|
||||
result:=make([][]int,0)
|
||||
if root==nil{
|
||||
return result
|
||||
}
|
||||
|
||||
queue:=make([]*TreeNode,0)
|
||||
queue=append(queue,root)
|
||||
|
||||
for len(queue)>0{
|
||||
list:=make([]int,0)
|
||||
l:=len(queue)
|
||||
|
||||
for i:=0;i<l;i++{
|
||||
level:=queue[0]
|
||||
queue=queue[1:]
|
||||
list=append(list,level.Val)
|
||||
if level.Left!=nil{
|
||||
queue=append(queue,level.Left)
|
||||
}
|
||||
if level.Right!=nil{
|
||||
queue=append(queue,level.Right)
|
||||
}
|
||||
}
|
||||
result=append(result,list)
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -623,7 +623,21 @@ Python:
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
JavaScript
|
||||
```javascript
|
||||
var buildTree = function(inorder, postorder) {
|
||||
if (!postorder.length) return null
|
||||
|
||||
let root = new TreeNode(postorder[postorder.length - 1])
|
||||
|
||||
let index = inorder.findIndex(number => number === root.val)
|
||||
|
||||
root.left = buildTree(inorder.slice(0, index), postorder.slice(0, index))
|
||||
root.right = buildTree(inorder.slice(index + 1, inorder.length), postorder.slice(index, postorder.length - 1))
|
||||
|
||||
return root
|
||||
};
|
||||
```
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
|
@ -332,6 +332,19 @@ class Solution {
|
||||
}
|
||||
}
|
||||
|
||||
// LC112 简洁方法
|
||||
class Solution {
|
||||
public boolean hasPathSum(TreeNode root, int targetSum) {
|
||||
|
||||
if (root == null) return false; // 为空退出
|
||||
|
||||
// 叶子节点判断是否符合
|
||||
if (root.left == null && root.right == null) return root.val == targetSum;
|
||||
|
||||
// 求两侧分支的路径和
|
||||
return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
@ -198,12 +198,52 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int maxProfit(int[] prices) {
|
||||
int minprice = Integer.MAX_VALUE;
|
||||
int maxprofit = 0;
|
||||
for (int i = 0; i < prices.length; i++) {
|
||||
if (prices[i] < minprice) {
|
||||
minprice = prices[i];
|
||||
} else if (prices[i] - minprice > maxprofit) {
|
||||
maxprofit = prices[i] - minprice;
|
||||
}
|
||||
}
|
||||
return maxprofit;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func maxProfit(prices []int) int {
|
||||
length:=len(prices)
|
||||
if length==0{return 0}
|
||||
dp:=make([][]int,length)
|
||||
for i:=0;i<length;i++{
|
||||
dp[i]=make([]int,2)
|
||||
}
|
||||
|
||||
dp[0][0]=-prices[0]
|
||||
dp[0][1]=0
|
||||
for i:=1;i<length;i++{
|
||||
dp[i][0]=max(dp[i-1][0],-prices[i])
|
||||
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
|
||||
}
|
||||
return dp[length-1][1]
|
||||
}
|
||||
|
||||
func max(a,b int)int {
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -133,7 +133,41 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```java
|
||||
// 动态规划
|
||||
class Solution
|
||||
// 实现1:二维数组存储
|
||||
// 可以将每天持有与否的情况分别用 dp[i][0] 和 dp[i][1] 来进行存储
|
||||
// 时间复杂度:O(n),空间复杂度O(n)
|
||||
public int maxProfit(int[] prices) {
|
||||
int n = prices.length;
|
||||
int[][] dp = new int[n][2]; // 创建二维数组存储状态
|
||||
dp[0][0] = 0; // 初始状态
|
||||
dp[0][1] = -prices[0];
|
||||
for (int i = 1; i < n; ++i) {
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]); // 第 i 天,没有股票
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); // 第 i 天,持有股票
|
||||
}
|
||||
return dp[n - 1][0]; // 卖出股票收益高于持有股票收益,因此取[0]
|
||||
}
|
||||
|
||||
// 实现2:变量存储
|
||||
// 第一种方法需要用二维数组存储,有空间开销,其实关心的仅仅是前一天的状态,不关注更多的历史信息
|
||||
// 因此,可以仅保存前一天的信息存入 dp0、dp1 这 2 个变量即可
|
||||
// 时间复杂度:O(n),空间复杂度O(1)
|
||||
public int maxProfit(int[] prices) {
|
||||
int n = prices.length;
|
||||
int dp0 = 0, dp1 = -prices[0]; // 定义变量,存储初始状态
|
||||
for (int i = 1; i < n; ++i) {
|
||||
int newDp0 = Math.max(dp0, dp1 + prices[i]); // 第 i 天,没有股票
|
||||
int newDp1 = Math.max(dp1, dp0 - prices[i]); // 第 i 天,持有股票
|
||||
dp0 = newDp0;
|
||||
dp1 = newDp1;
|
||||
}
|
||||
return dp0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -223,7 +223,21 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
|
||||
start = 0
|
||||
curSum = 0
|
||||
totalSum = 0
|
||||
for i in range(len(gas)):
|
||||
curSum += gas[i] - cost[i]
|
||||
totalSum += gas[i] - cost[i]
|
||||
if curSum < 0:
|
||||
curSum = 0
|
||||
start = i + 1
|
||||
if totalSum < 0: return -1
|
||||
return start
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
@ -234,4 +248,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -161,7 +161,18 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def candy(self, ratings: List[int]) -> int:
|
||||
candyVec = [1] * len(ratings)
|
||||
for i in range(1, len(ratings)):
|
||||
if ratings[i] > ratings[i - 1]:
|
||||
candyVec[i] = candyVec[i - 1] + 1
|
||||
for j in range(len(ratings) - 2, -1, -1):
|
||||
if ratings[j] > ratings[j + 1]:
|
||||
candyVec[j] = max(candyVec[j], candyVec[j + 1] + 1)
|
||||
return sum(candyVec)
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
@ -172,4 +183,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -170,7 +170,32 @@ public class EvalRPN {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func evalRPN(tokens []string) int {
|
||||
stack := []int{}
|
||||
for _, token := range tokens {
|
||||
val, err := strconv.Atoi(token)
|
||||
if err == nil {
|
||||
stack = append(stack, val)
|
||||
} else {
|
||||
num1, num2 := stack[len(stack)-2], stack[(len(stack))-1]
|
||||
stack = stack[:len(stack)-2]
|
||||
switch token {
|
||||
case "+":
|
||||
stack = append(stack, num1+num2)
|
||||
case "-":
|
||||
stack = append(stack, num1-num2)
|
||||
case "*":
|
||||
stack = append(stack, num1*num2)
|
||||
case "/":
|
||||
stack = append(stack, num1/num2)
|
||||
}
|
||||
}
|
||||
}
|
||||
return stack[0]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -141,7 +141,8 @@ void reverse(string& s, int start, int end) {
|
||||
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/151_%E7%BF%BB%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84%E5%8D%95%E8%AF%8D.png' width=600> </img></div>
|
||||
|
||||
```
|
||||
```C++
|
||||
// 版本一
|
||||
class Solution {
|
||||
public:
|
||||
// 反转字符串s中左闭又闭的区间[start, end]
|
||||
@ -182,7 +183,7 @@ public:
|
||||
int end = 0; // 反转的单词在字符串里终止位置
|
||||
bool entry = false; // 标记枚举字符串的过程中是否已经进入了单词区间
|
||||
for (int i = 0; i < s.size(); i++) { // 开始反转单词
|
||||
if ((!entry) || (s[i] != ' ' && s[i - 1] == ' ')) {
|
||||
if ((!entry))) {
|
||||
start = i; // 确定单词起始位置
|
||||
entry = true; // 进入单词区间
|
||||
}
|
||||
@ -201,9 +202,42 @@ public:
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* 主函数简单写法
|
||||
string reverseWords(string s) {
|
||||
removeExtraSpaces(s);
|
||||
reverse(s, 0, s.size() - 1);
|
||||
for(int i = 0; i < s.size(); i++) {
|
||||
int j = i;
|
||||
// 查找单词间的空格,翻转单词
|
||||
while(j < s.size() && s[j] != ' ') j++;
|
||||
reverse(s, i, j - 1);
|
||||
i = j;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
*/
|
||||
};
|
||||
```
|
||||
|
||||
当然这里的主函数reverseWords写的有一些冗余的,可以精简一些,精简之后的主函数为:
|
||||
|
||||
```C++
|
||||
// 注意这里仅仅是主函数,其他函数和版本一一致
|
||||
string reverseWords(string s) {
|
||||
removeExtraSpaces(s);
|
||||
reverse(s, 0, s.size() - 1);
|
||||
for(int i = 0; i < s.size(); i++) {
|
||||
int j = i;
|
||||
// 查找单词间的空格,翻转单词
|
||||
while(j < s.size() && s[j] != ' ') j++;
|
||||
reverse(s, i, j - 1);
|
||||
i = j;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -111,7 +111,24 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```Java
|
||||
// 动态规划
|
||||
class Solution {
|
||||
public int rob(int[] nums) {
|
||||
if (nums == null || nums.length == 0) return 0;
|
||||
if (nums.length == 1) return nums[0];
|
||||
|
||||
int[] dp = new int[nums.length + 1];
|
||||
dp[0] = nums[0];
|
||||
dp[1] = Math.max(dp[0], nums[1]);
|
||||
for (int i = 2; i < nums.length; i++) {
|
||||
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
|
||||
}
|
||||
|
||||
return dp[nums.length - 1];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -98,7 +98,28 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```Java
|
||||
class Solution {
|
||||
public int rob(int[] nums) {
|
||||
if (nums == null || nums.length == 0)
|
||||
return 0;
|
||||
int len = nums.length;
|
||||
if (len == 1)
|
||||
return nums[0];
|
||||
return Math.max(robAction(nums, 0, len - 1), robAction(nums, 1, len));
|
||||
}
|
||||
|
||||
int robAction(int[] nums, int start, int end) {
|
||||
int x = 0, y = 0, z = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
y = z;
|
||||
z = Math.max(y, x + nums[i]);
|
||||
x = y;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -266,7 +266,34 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
|
||||
// check
|
||||
if root == nil {
|
||||
return root
|
||||
}
|
||||
// 相等 直接返回root节点即可
|
||||
if root == p || root == q {
|
||||
return root
|
||||
}
|
||||
// Divide
|
||||
left := lowestCommonAncestor(root.Left, p, q)
|
||||
right := lowestCommonAncestor(root.Right, p, q)
|
||||
|
||||
// Conquer
|
||||
// 左右两边都不为空,则根节点为祖先
|
||||
if left != nil && right != nil {
|
||||
return root
|
||||
}
|
||||
if left != nil {
|
||||
return left
|
||||
}
|
||||
if right != nil {
|
||||
return right
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -274,4 +301,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -77,7 +77,7 @@ if (cur->left == NULL && cur->right == NULL) {
|
||||
|
||||
这里我们先使用vector<int>结构的path容器来记录路径,那么终止处理逻辑如下:
|
||||
|
||||
```
|
||||
```C++
|
||||
if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点
|
||||
string sPath;
|
||||
for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式
|
||||
@ -113,7 +113,7 @@ if (cur->right) {
|
||||
|
||||
那么回溯要怎么回溯呢,一些同学会这么写,如下:
|
||||
|
||||
```
|
||||
```C++
|
||||
if (cur->left) {
|
||||
traversal(cur->left, path, result);
|
||||
}
|
||||
@ -129,7 +129,7 @@ path.pop_back();
|
||||
|
||||
那么代码应该这么写:
|
||||
|
||||
```
|
||||
```C++
|
||||
if (cur->left) {
|
||||
traversal(cur->left, path, result);
|
||||
path.pop_back(); // 回溯
|
||||
@ -335,4 +335,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -98,8 +98,6 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
杨老师的这个专栏很不错,他本身也是Oracle 首席工程师,对Java有极其深刻的理解,讲的内容很硬核,适合使用Java语言的录友们用来进阶!作为面试突击手册非常合适, 所以推荐给大家!现在下单输入口令:javahexin,可以省40元那[机智]
|
||||
|
||||
## 总结
|
||||
|
||||
本题最关键的是要想到dp[i]由哪些状态可以推出来,并取最大值,那么很自然就能想到递推公式:dp[i] = max(dp[i], dp[j] + 1);
|
||||
@ -110,14 +108,58 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
public int lengthOfLIS(int[] nums) {
|
||||
int[] dp = new int[nums.length];
|
||||
Arrays.fill(dp, 1);
|
||||
for (int i = 0; i < dp.length; i++) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[i] > nums[j]) {
|
||||
dp[i] = Math.max(dp[i], dp[j] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
int res = 0;
|
||||
for (int i = 0; i < dp.length; i++) {
|
||||
res = Math.max(res, dp[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
||||
```go
|
||||
func lengthOfLIS(nums []int ) int {
|
||||
dp := []int{}
|
||||
for _, num := range nums {
|
||||
if len(dp) ==0 || dp[len(dp) - 1] < num {
|
||||
dp = append(dp, num)
|
||||
} else {
|
||||
l, r := 0, len(dp) - 1
|
||||
pos := r
|
||||
for l <= r {
|
||||
mid := (l + r) >> 1
|
||||
if dp[mid] >= num {
|
||||
pos = mid;
|
||||
r = mid - 1
|
||||
} else {
|
||||
l = mid + 1
|
||||
}
|
||||
}
|
||||
dp[pos] = num
|
||||
}//二分查找
|
||||
}
|
||||
return len(dp)
|
||||
}
|
||||
```
|
||||
*复杂度分析*
|
||||
- 时间复杂度:O(nlogn)。数组 nums 的长度为 n,我们依次用数组中的元素去更新 dp 数组,相当于插入最后递增的元素,而更新 dp 数组时需要进行 O(logn) 的二分搜索,所以总时间复杂度为 O(nlogn)。
|
||||
- 空间复杂度:O(n),需要额外使用长度为 n 的 dp 数组。
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -218,7 +218,72 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
```Java
|
||||
class Solution {
|
||||
// 1.递归去偷,超时
|
||||
public int rob(TreeNode root) {
|
||||
if (root == null)
|
||||
return 0;
|
||||
int money = root.val;
|
||||
if (root.left != null) {
|
||||
money += rob(root.left.left) + rob(root.left.right);
|
||||
}
|
||||
if (root.right != null) {
|
||||
money += rob(root.right.left) + rob(root.right.right);
|
||||
}
|
||||
return Math.max(money, rob(root.left) + rob(root.right));
|
||||
}
|
||||
|
||||
// 2.递归去偷,记录状态
|
||||
// 执行用时:3 ms , 在所有 Java 提交中击败了 56.24% 的用户
|
||||
public int rob1(TreeNode root) {
|
||||
Map<TreeNode, Integer> memo = new HashMap<>();
|
||||
return robAction(root, memo);
|
||||
}
|
||||
|
||||
int robAction(TreeNode root, Map<TreeNode, Integer> memo) {
|
||||
if (root == null)
|
||||
return 0;
|
||||
if (memo.containsKey(root))
|
||||
return memo.get(root);
|
||||
int money = root.val;
|
||||
if (root.left != null) {
|
||||
money += robAction(root.left.left, memo) + robAction(root.left.right, memo);
|
||||
}
|
||||
if (root.right != null) {
|
||||
money += robAction(root.right.left, memo) + robAction(root.right.right, memo);
|
||||
}
|
||||
int res = Math.max(money, robAction(root.left, memo) + robAction(root.right, memo));
|
||||
memo.put(root, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// 3.状态标记递归
|
||||
// 执行用时:0 ms , 在所有 Java 提交中击败了 100% 的用户
|
||||
// 不偷:Max(左孩子不偷,左孩子偷) + Max(又孩子不偷,右孩子偷)
|
||||
// root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) +
|
||||
// Math.max(rob(root.right)[0], rob(root.right)[1])
|
||||
// 偷:左孩子不偷+ 右孩子不偷 + 当前节点偷
|
||||
// root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;
|
||||
public int rob3(TreeNode root) {
|
||||
int[] res = robAction1(root);
|
||||
return Math.max(res[0], res[1]);
|
||||
}
|
||||
|
||||
int[] robAction1(TreeNode root) {
|
||||
int res[] = new int[2];
|
||||
if (root == null)
|
||||
return res;
|
||||
|
||||
int[] left = robAction1(root.left);
|
||||
int[] right = robAction1(root.right);
|
||||
|
||||
res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
|
||||
res[1] = root.val + left[0] + right[0];
|
||||
return res;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -171,7 +171,20 @@ public:
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
```Java
|
||||
class Solution {
|
||||
public int fib(int n) {
|
||||
if (n < 2) return n;
|
||||
int a = 0, b = 1, c = 0;
|
||||
for (int i = 1; i < n; i++) {
|
||||
c = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -227,6 +227,30 @@ Python:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func countSubstrings(s string) int {
|
||||
res:=0
|
||||
dp:=make([][]bool,len(s))
|
||||
for i:=0;i<len(s);i++{
|
||||
dp[i]=make([]bool,len(s))
|
||||
}
|
||||
|
||||
for i:=len(s)-1;i>=0;i--{
|
||||
for j:=i;j<len(s);j++{
|
||||
if s[i]==s[j]{
|
||||
if j-i<=1{
|
||||
res++
|
||||
dp[i][j]=true
|
||||
}else if dp[i+1][j-1]{
|
||||
res++
|
||||
dp[i][j]=true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -271,6 +271,20 @@ class Solution:
|
||||
|
||||
|
||||
Go:
|
||||
```Go
|
||||
func insertIntoBST(root *TreeNode, val int) *TreeNode {
|
||||
if root == nil {
|
||||
root = &TreeNode{Val: val}
|
||||
return root
|
||||
}
|
||||
if root.Val > val {
|
||||
root.Left = insertIntoBST(root.Left, val)
|
||||
} else {
|
||||
root.Right = insertIntoBST(root.Right, val)
|
||||
}
|
||||
return root
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -279,4 +293,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -124,7 +124,18 @@ class Solution {
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
```python
|
||||
class Solution:
|
||||
def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
|
||||
A = sorted(A, key=abs, reverse=True) # 将A按绝对值从大到小排列
|
||||
for i in range(len(A)):
|
||||
if K > 0 and A[i] < 0:
|
||||
A[i] *= -1
|
||||
K -= 1
|
||||
if K > 0:
|
||||
A[len(A) - 1] *= ((-1)**K)
|
||||
return sum(A)
|
||||
```
|
||||
|
||||
Go:
|
||||
|
||||
@ -135,4 +146,4 @@ Go:
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
||||
|
@ -115,7 +115,59 @@ void traversal(TreeNode* cur, vector<int>& vec) {
|
||||
|
||||
|
||||
Java:
|
||||
```Java
|
||||
// 前序遍历·递归·LC144_二叉树的前序遍历
|
||||
class Solution {
|
||||
ArrayList<Integer> preOrderReverse(TreeNode root) {
|
||||
ArrayList<Integer> result = new ArrayList<Integer>();
|
||||
preOrder(root, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void preOrder(TreeNode root, ArrayList<Integer> result) {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
result.add(root.val); // 注意这一句
|
||||
preOrder(root.left, result);
|
||||
preOrder(root.right, result);
|
||||
}
|
||||
}
|
||||
// 中序遍历·递归·LC94_二叉树的中序遍历
|
||||
class Solution {
|
||||
public List<Integer> inorderTraversal(TreeNode root) {
|
||||
List<Integer> res = new ArrayList<>();
|
||||
inorder(root, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void inorder(TreeNode root, List<Integer> list) {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
inorder(root.left, list);
|
||||
list.add(root.val); // 注意这一句
|
||||
inorder(root.right, list);
|
||||
}
|
||||
}
|
||||
// 后序遍历·递归·LC145_二叉树的后序遍历
|
||||
class Solution {
|
||||
public List<Integer> postorderTraversal(TreeNode root) {
|
||||
List<Integer> res = new ArrayList<>();
|
||||
postorder(root, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void postorder(TreeNode root, List<Integer> list) {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
postorder(root.left, list);
|
||||
postorder(root.right, list);
|
||||
list.add(root.val); // 注意这一句
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Python:
|
||||
|
||||
|
@ -9,15 +9,17 @@
|
||||
|
||||
--------------------------
|
||||
|
||||
# 看了这么多代码,谈一谈代码风格!
|
||||
# 看了这么多代码,谈一谈代码风格!
|
||||
|
||||
其实在交流群里经常能看到大家发出来的代码,可以看出很多录友对代码规范应该不甚了解,代码看起来并不舒服。
|
||||
最近看了很多录友在[leetcode-master](https://mp.weixin.qq.com/s/wZRTrA9Rbvgq1yEkSw4vfQ)上提交的代码,发现很多录友的代码其实并不规范,这一点平时在交流群和知识星球里也能看出来。
|
||||
|
||||
很多录友对代码规范应该不甚了解,代码看起来并不舒服。
|
||||
|
||||
所以呢,我给大家讲一讲代码规范,我主要以C++代码为例。
|
||||
|
||||
需要强调一下,代码规范并不是仅仅是让代码看着舒服,这是一个很重要的习惯。
|
||||
|
||||
# 题外话
|
||||
## 题外话
|
||||
|
||||
工作之后,**特别是在大厂,看谁的技术牛不牛逼,不用看谁写出多牛逼的代码,就代码风格扫一眼,立刻就能看出来是正规军还是野生程序员**。
|
||||
|
||||
@ -25,15 +27,15 @@
|
||||
|
||||
现在一些小公司,甚至大公司里的某些技术团队也不注重代码规范,赶进度撸出功能就完事,这种情况就要分两方面看:
|
||||
|
||||
* 第一种情况:这个项目在业务上赚到钱了,每年年终好几十万,那项目前期还关心啥代码风格,赶进度把功能撸出来,赚钱就完事了,例如15年的王者荣耀。
|
||||
* 第一种情况:这个项目在业务上具有巨大潜力,需要抢占市场,只要先站住市场就能赚到钱,每年年终好几十万,那项目前期还关心啥代码风格,赶进度把功能撸出来,赚钱就完事了,例如12年的微信,15年的王者荣耀。这些项目都是后期在不断优化的。
|
||||
|
||||
* 第二种情况:这个项目没赚到钱,半死不活的,代码还没有设计也没有规范,这样对技术人员的伤害就非常大了。
|
||||
|
||||
**而不注重代码风格的团队,99.99%都是第二种情况**,如果你赶上了第一种情况,那就恭喜你了,本文下面的内容可以不用看了,哈哈。
|
||||
|
||||
# 代码规范
|
||||
## 代码规范
|
||||
|
||||
## 变量命名
|
||||
### 变量命名
|
||||
|
||||
这里我简单说一说规范问题。
|
||||
|
||||
@ -67,7 +69,7 @@
|
||||
|
||||

|
||||
|
||||
## 水平留白(代码空格)
|
||||
### 水平留白(代码空格)
|
||||
|
||||
经常看到有的同学的代码都堆在一起,看起来都费劲,或者是有的间隔有空格,有的没有空格,很不统一,有的同学甚至为了让代码精简,把所有空格都省略掉了。
|
||||
|
||||
@ -89,7 +91,7 @@ int i, j;
|
||||
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++)
|
||||
```
|
||||
|
||||
花括号和函数保持同一行,并有一个空格例如:
|
||||
大括号和函数保持同一行,并有一个空格例如:
|
||||
|
||||
```
|
||||
while (n) {
|
||||
@ -123,9 +125,13 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
当然我并不是说一定要按照Google的规范来,代码风格其实统一就行,没有严格的说谁对谁错。
|
||||
这里关于大括号是否要重启一行?
|
||||
|
||||
# 总结
|
||||
Google规范是 大括号和 控制语句保持同一行的,我个人也很认可这种写法,因为可以缩短代码的行数,特别是项目中代码行数很多的情况下,这种写法是可以提高阅读代码的效率。
|
||||
|
||||
当然我并不是说一定要按照Google的规范来,**代码风格其实统一就行,没有严格的说谁对谁错**。
|
||||
|
||||
## 总结
|
||||
|
||||
如果还是学生,使用C++的话,可以按照题解中我的代码风格来,还是比较标准的。
|
||||
|
||||
|
@ -118,21 +118,6 @@ public static void test_arr() {
|
||||
|
||||
这里面试中数组相关的理论知识就介绍完了。
|
||||
|
||||
后续我将介绍面试中数组相关的五道经典面试题目,敬请期待!
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
109
problems/面试题02.07.链表相交.md
Normal file
109
problems/面试题02.07.链表相交.md
Normal file
@ -0,0 +1,109 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||||
</p>
|
||||
<p align="center"><strong>欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||||
|
||||
## 面试题 02.07. 链表相交
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/
|
||||
|
||||
给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。换句话说,如果一个链表的第k个节点与另一个链表的第j个节点是同一节点(引用完全相同),则这两个链表相交。
|
||||
|
||||
示例 1:
|
||||
|
||||
输入:listA = [4,1,8,4,5], listB = [5,0,1,8,4,5]
|
||||
|
||||
输出:Reference of the node with value = 8
|
||||
|
||||
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
|
||||
|
||||
|
||||
## 思路
|
||||
|
||||
本来很简洁明了的一道题,让题目描述搞的云里雾里的。
|
||||
|
||||
简单来说,就是求两个链表交点节点的**指针**。 这里同学们要注意,交点不是数值相等,而是指针相等。
|
||||
|
||||
为了方便举例,假设节点元素数值相等,则节点指针相等。
|
||||
|
||||
看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
|
||||
|
||||
v
|
||||
|
||||
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
|
||||
|
||||

|
||||
|
||||
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到焦点。
|
||||
|
||||
否则循环退出返回空指针。
|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
|
||||
ListNode* curA = headA;
|
||||
ListNode* curB = headB;
|
||||
int lenA = 0, lenB = 0;
|
||||
while (curA != NULL) { // 求链表A的长度
|
||||
lenA++;
|
||||
curA = curA->next;
|
||||
}
|
||||
while (curB != NULL) { // 求链表B的长度
|
||||
lenB++;
|
||||
curB = curB->next;
|
||||
}
|
||||
curA = headA;
|
||||
curB = headB;
|
||||
// 让curA为最长链表的头,lenA为其长度
|
||||
if (lenB > lenA) {
|
||||
swap (lenA, lenB);
|
||||
swap (curA, curB);
|
||||
}
|
||||
// 求长度差
|
||||
int gap = lenA - lenB;
|
||||
// 让curA和curB在同一起点上(末尾位置对齐)
|
||||
while (gap--) {
|
||||
curA = curA->next;
|
||||
}
|
||||
// 遍历curA 和 curB,遇到相同则直接返回
|
||||
while (curA != NULL) {
|
||||
if (curA == curB) {
|
||||
return curA;
|
||||
}
|
||||
curA = curA->next;
|
||||
curB = curB->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
* 时间复杂度:$O(n + m)$
|
||||
* 空间复杂度:$O(1)$
|
||||
|
||||
## 其他语言版本
|
||||
|
||||
|
||||
Java:
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|
Reference in New Issue
Block a user