diff --git a/16-LeetCode Hot 100/删除链表的倒数第N个结点.md b/16-LeetCode Hot 100/删除链表的倒数第N个结点.md index 6d8e2d3..84b3cd5 100644 --- a/16-LeetCode Hot 100/删除链表的倒数第N个结点.md +++ b/16-LeetCode Hot 100/删除链表的倒数第N个结点.md @@ -76,7 +76,7 @@ 4. 栈顶结点的 `next` 指向要删除结点的 `next` 5. 返回 `dummy.next` -## 代码实现 +## 解法 ### Go 实现(双指针法) @@ -177,110 +177,6 @@ func main() { } ``` -### 解法 - -```java -public class RemoveNthFromEnd { - - // 链表结点定义 - public static class ListNode { - int val; - ListNode next; - ListNode() {} - ListNode(int val) { this.val = val; } - ListNode(int val, ListNode next) { this.val = val; this.next = next; } - } - - public ListNode removeNthFromEnd(ListNode head, int n) { - // 创建哑结点,处理删除头结点的特殊情况 - ListNode dummy = new ListNode(0, head); - ListNode fast = dummy; - ListNode slow = dummy; - - // fast 先移动 n + 1 步 - for (int i = 0; i <= n; i++) { - fast = fast.next; - } - - // fast 和 slow 一起移动,直到 fast 为 null - while (fast != null) { - fast = fast.next; - slow = slow.next; - } - - // 删除 slow 的下一个结点 - slow.next = slow.next.next; - - return dummy.next; - } - - // 辅助函数:创建链表 - private ListNode createList(int[] nums) { - ListNode dummy = new ListNode(); - ListNode current = dummy; - for (int num : nums) { - current.next = new ListNode(num); - current = current.next; - } - return dummy.next; - } - - // 辅助函数:打印链表 - private void printList(ListNode head) { - ListNode current = head; - while (current != null) { - System.out.print(current.val); - if (current.next != null) { - System.out.print(" -> "); - } - current = current.next; - } - System.out.println(); - } - - // 测试用例 - public static void main(String[] args) { - RemoveNthFromEnd solution = new RemoveNthFromEnd(); - - // 测试用例1 - ListNode head1 = solution.createList(new int[]{1, 2, 3, 4, 5}); - System.out.print("输入: "); - solution.printList(head1); - System.out.println("n = 2"); - ListNode result1 = solution.removeNthFromEnd(head1, 2); - System.out.print("输出: "); - solution.printList(result1); - - // 测试用例2: 删除头结点 - ListNode head2 = solution.createList(new int[]{1}); - System.out.print("\n输入: "); - solution.printList(head2); - System.out.println("n = 1"); - ListNode result2 = solution.removeNthFromEnd(head2, 1); - System.out.print("输出: "); - solution.printList(result2); - - // 测试用例3: 删除最后一个结点 - ListNode head3 = solution.createList(new int[]{1, 2}); - System.out.print("\n输入: "); - solution.printList(head3); - System.out.println("n = 1"); - ListNode result3 = solution.removeNthFromEnd(head3, 1); - System.out.print("输出: "); - solution.printList(result3); - - // 测试用例4: 长链表 - ListNode head4 = solution.createList(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); - System.out.print("\n输入: "); - solution.printList(head4); - System.out.println("n = 5"); - ListNode result4 = solution.removeNthFromEnd(head4, 5); - System.out.print("输出: "); - solution.printList(result4); - } -} -``` - ### Go 实现(计算长度法) ```go @@ -316,33 +212,35 @@ func removeNthFromEndByLength(head *ListNode, n int) *ListNode { } ``` -### Java 实现(栈法) +### Go 实现(栈法) -```java -import java.util.Stack; +```go +func removeNthFromEndByStack(head *ListNode, n int) *ListNode { + if head == nil { + return nil + } -public ListNode removeNthFromEndByStack(ListNode head, int n) { - // 创建哑结点 - ListNode dummy = new ListNode(0, head); + // 创建哑结点 + dummy := &ListNode{0, head} - // 将所有结点压入栈 - Stack stack = new Stack<>(); - ListNode current = dummy; - while (current != null) { - stack.push(current); - current = current.next; - } + // 将所有结点压入栈 + var stack []*ListNode + current := dummy + for current != nil { + stack = append(stack, current) + current = current.Next + } - // 弹出 n 个结点 - for (int i = 0; i < n; i++) { - stack.pop(); - } + // 弹出 n 个结点 + for i := 0; i < n; i++ { + stack = stack[:len(stack)-1] + } - // 栈顶就是要删除结点的前一个结点 - ListNode prev = stack.peek(); - prev.next = prev.next.next; + // 栈顶就是要删除结点的前一个结点 + prev := stack[len(stack)-1] + prev.Next = prev.Next.Next - return dummy.next; + return dummy.Next } ``` @@ -380,7 +278,7 @@ public ListNode removeNthFromEndByStack(ListNode head, int n) { ### Q1: 如果链表是循环链表,应该如何处理? -**A:** 需要先判断是否为循环链表,如果是,需要找到尾结点并断开循环。 +**方法**:检测循环,计算长度,然后调整删除位置 ```go func removeNthFromEndCircular(head *ListNode, n int) *ListNode { @@ -388,75 +286,104 @@ func removeNthFromEndCircular(head *ListNode, n int) *ListNode { return nil } - // 检测是否有环 - hasCycle := detectCycle(head) - if !hasCycle { - return removeNthFromEnd(head, n) - } - - // 如果有环,需要先找到环的入口和长度 - // 然后根据 n 的值决定如何删除 - // 这是一个复杂的问题,需要更多边界条件处理 - - return head -} - -func detectCycle(head *ListNode) bool { - slow, fast := head, head - for fast != nil && fast.Next != nil { + // 计算链表长度并检测循环 + length := 1 + slow, fast := head, head.Next + for fast != nil && fast.Next != nil && slow != fast { slow = slow.Next fast = fast.Next.Next - if slow == fast { - return true - } + length++ } - return false + + // 如果有循环 + if slow == fast { + // 计算循环长度 + cycleLength := 1 + slow = slow.Next + for slow != fast { + slow = slow.Next + cycleLength++ + } + + // 总长度 + totalLength := length + cycleLength - 1 + pos := totalLength - n + + // 处理位置调整 + if pos < 0 { + pos += totalLength + } + + // 执行删除 + return removeNthFromEndByPosition(head, pos) + } + + // 没有循环,使用原有方法 + return removeNthFromEnd(head, n) } ``` ### Q2: 如果要求删除前 n 个结点,应该如何修改? -**A:** 直接遍历到第 n-1 个结点,然后删除后续所有结点。 +**方法**:直接删除前 n 个结点 ```go -func removeFirstN(head *ListNode, n int) *ListNode { - if n <= 0 { - return head +func removeFirstNNodes(head *ListNode, n int) *ListNode { + for i := 0; i < n && head != nil; i++ { + head = head.Next } - - dummy := &ListNode{0, head} - current := dummy - - // 移动到第 n 个结点的前一个结点 - for i := 0; i < n && current != nil; i++ { - current = current.Next - } - - if current != nil { - current.Next = nil - } - - return dummy.Next + return head } ``` ### Q3: 如果链表很长,如何优化内存使用? -**A:** 使用双指针法是最优的,因为它不需要额外的空间。另外,可以考虑使用尾递归优化(如果语言支持)。 +**方法**: +1. 使用固定大小的滑动窗口 +2. 避免存储整个链表 +3. 使用递归(但会增加栈空间) + +```go +func removeNthFromEndOptimized(head *ListNode, n int) *ListNode { + // 使用固定大小的窗口 + dummy := &ListNode{0, head} + slow, fast := dummy, dummy + + // fast 先移动 n + 1 步 + for i := 0; i <= n; i++ { + if fast == nil { + return head // n > 链表长度 + } + fast = fast.Next + } + + // 移动窗口 + for fast != nil { + slow = slow.Next + fast = fast.Next + } + + // 删除结点 + slow.Next = slow.Next.Next + + return dummy.Next +} +``` ## P7 加分项 ### 1. 深度理解:为什么需要哑结点? -**哑结点的作用:** -1. **统一处理:** 避免单独处理删除头结点的特殊情况 -2. **简化边界条件:** 当要删除的是头结点时,普通方法需要特殊处理 -3. **代码简洁:** 使用哑结点后,删除操作统一为 `prev.next = prev.next.next` +**关键点:** +- 处理删除头结点的特殊情况 +- 统一处理逻辑,减少边界条件判断 +- 简化代码,提高可读性 -**没有哑结点的问题:** +**哑结点的作用:** ```go -// 没有哑结点的版本(需要特殊处理删除头结点) +// 没有哑结点的情况 func removeNthFromEndWithoutDummy(head *ListNode, n int) *ListNode { + // 需要特殊处理删除头结点的情况 length := 0 current := head for current != nil { @@ -464,228 +391,97 @@ func removeNthFromEndWithoutDummy(head *ListNode, n int) *ListNode { current = current.Next } - if n == length { - // 要删除的是头结点,特殊处理 - return head.Next + if length == n { + return head.Next // 删除头结点 } - pos := length - n - current = head - for i := 0; i < pos-1; i++ { - current = current.Next - } - current.Next = current.Next.Next - + // ... 其他逻辑 return head } ``` ### 2. 实战扩展:链表操作的通用技巧 -#### 技巧1:快慢指针的应用 - -- **找中点:** fast 移动 2 步,slow 移动 1 步 -- **找倒数第 k 个:** fast 先移动 k 步 -- **检测环:** fast 移动 2 步,slow 移动 1 步 +**技巧总结:** +- 使用哑结点简化边界处理 +- 双指针技巧:快慢指针、前后指针 +- 递归处理链表问题 +- 栈辅助解决链表问题 +**通用模板:** ```go -// 找链表中点 -func findMiddle(head *ListNode) *ListNode { - slow, fast := head, head - for fast != nil && fast.Next != nil { - slow = slow.Next - fast = fast.Next.Next - } - return slow -} - -// 检测环 -func hasCycle(head *ListNode) bool { - slow, fast := head, head - for fast != nil && fast.Next != nil { - slow = slow.Next - fast = fast.Next.Next - if slow == fast { - return true - } - } - return false +func solveLinkedListProblem(head *ListNode) *ListNode { + dummy := &ListNode{0, head} + // ... 使用双指针或其他技巧 + return dummy.Next } ``` -#### 技巧2:虚拟头结点的使用 - -- **统一操作:** 避免边界条件判断 -- **简化代码:** 使删除、插入操作更简洁 -- **常见场景:** 删除操作、插入操作 - ### 3. 变形题目 -#### 变形1:删除链表中的重复元素 - -**LeetCode 83:** 删除排序链表中的重复元素,使得每个元素只出现一次。 - -```go -func deleteDuplicates(head *ListNode) *ListNode { - if head == nil { - return nil - } - - current := head - for current.Next != nil { - if current.Val == current.Next.Val { - current.Next = current.Next.Next - } else { - current = current.Next - } - } - - return head -} -``` - -#### 变形2:删除链表中的所有重复元素 - -**LeetCode 82:** 删除排序链表中所有重复的元素,只保留原始链表中没有重复出现的数字。 - -```go -func deleteDuplicatesAll(head *ListNode) *ListNode { - dummy := &ListNode{0, head} - prev := dummy - - for prev.Next != nil { - curr := prev.Next - // 检查是否有重复 - if curr.Next != nil && curr.Val == curr.Next.Val { - // 跳过所有重复的值 - val := curr.Val - for curr != nil && curr.Val == val { - curr = curr.Next - } - prev.Next = curr - } else { - prev = prev.Next - } - } - - return dummy.Next -} -``` - -#### 变形3:旋转链表 - -**LeetCode 61:** 将链表每个节点向右移动 k 个位置。 - -```go -func rotateRight(head *ListNode, k int) *ListNode { - if head == nil || k == 0 { - return head - } - - // 计算链表长度并连接成环 - length := 1 - tail := head - for tail.Next != nil { - tail = tail.Next - length++ - } - tail.Next = head - - // 计算新的尾结点位置 - k = k % length - stepsToNewTail := length - k - newTail := head - for i := 1; i < stepsToNewTail; i++ { - newTail = newTail.Next - } - - newHead := newTail.Next - newTail.Next = nil - - return newHead -} -``` +1. [19. 删除链表的倒数第N个节点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/) - 原题 +2. [82. 删除排序链表中的重复元素 II](https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/) - 删除所有重复元素 +3. [83. 删除排序链表中的重复元素](https://leetcode.cn/problems/remove-duplicates-from-sorted-list/) - 删除重复元素保留一个 +4. [203. 移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/) - 删除指定值的节点 ### 4. 优化技巧 -#### 优化1:一次遍历删除多个结点 +**空间优化:** +- 原地操作,不使用额外空间 +- 递归改为迭代 -如果需要删除多个位置的结点,可以在一次遍历中完成。 +**时间优化:** +- 一次遍历完成 +- 提前终止条件 -```go -func removeNodes(head *ListNode, positions []int) *ListNode { - dummy := &ListNode{0, head} - posMap := make(map[int]bool) - for _, pos := range positions { - posMap[pos] = true - } - - prev := dummy - curr := head - index := 1 - - for curr != nil { - if posMap[index] { - prev.Next = curr.Next - } else { - prev = curr - } - curr = curr.Next - index++ - } - - return dummy.Next -} -``` - -#### 优化2:递归解法(优雅但可能栈溢出) - -```go -func removeNthFromEndRecursive(head *ListNode, n int) *ListNode { - counter := 0 - return removeHelper(head, &counter, n) -} - -func removeHelper(node *ListNode, counter *int, n int) *ListNode { - if node == nil { - return nil - } - - node.Next = removeHelper(node.Next, counter, n) - *counter++ - - if *counter == n { - return node.Next - } - - return node -} -``` +**代码优化:** +- 合并重复逻辑 +- 减少不必要的变量 ### 5. 实际应用场景 -- **LRU 缓存:** 删除最近最少使用的数据 -- **浏览器历史记录:** 删除特定位置的历史记录 -- **文本编辑器:** 撤销操作(删除最近的修改) -- **任务队列:** 删除超时或取消的任务 +**应用场景:** +- 缓存淘汰策略(LRU) +- 音乐播放列表管理 +- 浏览器历史记录 +- 撤销/重做功能 + +**面试问题:** +- 如何处理并发访问的链表? +- 如何实现线程安全的链表操作? ### 6. 面试技巧 -**面试官可能会问:** -1. "为什么选择双指针法而不是计算长度法?" -2. "如果链表很长,递归解法会有什么问题?" -3. "如何证明你的算法是正确的?" +**常见面试问题:** +1. 时间/空间复杂度分析 +2. 边界条件处理 +3. 优化思路 +4. 相关题目变体 -**回答要点:** -1. 双指针法只需一次遍历,代码简洁,空间复杂度低 -2. 递归可能导致栈溢出,对于长链表不推荐 -3. 可以通过画图、举例、边界条件分析来证明正确性 +**回答技巧:** +- 先给出暴力解法 +- 逐步优化 +- 说明权衡取舍 ### 7. 相关题目推荐 -- LeetCode 19: 删除链表的倒数第 N 个结点(本题) -- LeetCode 61: 旋转链表 -- LeetCode 83: 删除排序链表中的重复元素 -- LeetCode 82: 删除排序链表中的所有重复元素 -- LeetCode 206: 反转链表 -- LeetCode 142: 环形链表 II +**相关题目:** +1. [206. 反转链表](https://leetcode.cn/problems/reverse-linked-list/) +2. [21. 合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists/) +3. [141. 环形链表](https://leetcode.cn/problems/linked-list-cycle/) +4. [142. 环形链表 II](https://leetcode.cn/problems/linked-list-cycle-ii/) + +## 总结 + +这道题的核心是: +1. **双指针法**:一次遍历,快慢指针配合 +2. **边界处理**:使用哑结点简化删除头结点的处理 +3. **多种解法**:双指针、计算长度、栈法各有优劣 + +**易错点**: +- 忘记处理删除头结点的情况 +- 快指针移动步数错误(应该是 n+1) +- 空链表的特殊情况处理 +- 循环链表的特殊情况 + +**最优解法**:双指针法,时间 O(L),空间 O(1) \ No newline at end of file diff --git a/16-LeetCode Hot 100/单词搜索.md b/16-LeetCode Hot 100/单词搜索.md index c7e7907..98e368b 100644 --- a/16-LeetCode Hot 100/单词搜索.md +++ b/16-LeetCode Hot 100/单词搜索.md @@ -97,49 +97,6 @@ func exist(board [][]byte, word string) bool { ### Java 实现 -```java -public class Solution { - private boolean[][] visited; - private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; - - public boolean exist(char[][] board, String word) { - int m = board.length, n = board[0].length; - visited = new boolean[m][n]; - - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (board[i][j] == word.charAt(0) && dfs(board, word, i, j, 0)) { - return true; - } - } - } - return false; - } - - private boolean dfs(char[][] board, String word, int i, int j, int k) { - if (k == word.length()) { - return true; - } - - if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || - visited[i][j] || board[i][j] != word.charAt(k)) { - return false; - } - - visited[i][j] = true; - - for (int[] dir : directions) { - if (dfs(board, word, i + dir[0], j + dir[1], k + 1)) { - visited[i][j] = false; - return true; - } - } - - visited[i][j] = false; - return false; - } -} -``` ## 复杂度分析 diff --git a/16-LeetCode Hot 100/子集.md b/16-LeetCode Hot 100/子集.md index f5aad52..dcb5d42 100644 --- a/16-LeetCode Hot 100/子集.md +++ b/16-LeetCode Hot 100/子集.md @@ -116,56 +116,6 @@ func main() { ### Java 实现(回溯法) -```java -import java.util.ArrayList; -import java.util.List; - -public class Subsets { - - public List> subsets(int[] nums) { - List> result = new ArrayList<>(); - List current = new ArrayList<>(); - backtrack(result, current, nums, 0); - return result; - } - - private void backtrack(List> result, List current, - int[] nums, int start) { - // 将当前子集加入结果 - result.add(new ArrayList<>(current)); - - // 从 start 开始尝试包含每个元素 - for (int i = start; i < nums.length; i++) { - // 选择当前元素 - current.add(nums[i]); - // 递归处理下一个元素 - backtrack(result, current, nums, i + 1); - // 撤销选择(回溯) - current.remove(current.size() - 1); - } - } - - // 测试用例 - public static void main(String[] args) { - Subsets solution = new Subsets(); - - // 测试用例1 - int[] nums1 = {1, 2, 3}; - System.out.println("输入: [1, 2, 3]"); - System.out.println("输出: " + solution.subsets(nums1)); - - // 测试用例2 - int[] nums2 = {0}; - System.out.println("\n输入: [0]"); - System.out.println("输出: " + solution.subsets(nums2)); - - // 测试用例3 - int[] nums3 = {1, 2}; - System.out.println("\n输入: [1, 2]"); - System.out.println("输出: " + solution.subsets(nums3)); - } -} -``` ### Go 实现(迭代法-位掩码) @@ -192,26 +142,6 @@ func subsetsBitMask(nums []int) [][]int { ### Java 实现(迭代法-位掩码) -```java -public List> subsetsBitMask(int[] nums) { - int n = nums.length; - int total = 1 << n; // 2^n 个子集 - List> result = new ArrayList<>(); - - for (int mask = 0; mask < total; mask++) { - List subset = new ArrayList<>(); - for (int i = 0; i < n; i++) { - // 检查第 i 位是否为 1 - if ((mask & (1 << i)) != 0) { - subset.add(nums[i]); - } - } - result.add(subset); - } - - return result; -} -``` ### Go 实现(级联法) diff --git a/16-LeetCode Hot 100/括号生成.md b/16-LeetCode Hot 100/括号生成.md index 906bcb5..423a05e 100644 --- a/16-LeetCode Hot 100/括号生成.md +++ b/16-LeetCode Hot 100/括号生成.md @@ -139,72 +139,6 @@ func main() { ### Java 实现(回溯法) -```java -import java.util.ArrayList; -import java.util.List; - -public class GenerateParentheses { - - public List generateParenthesis(int n) { - List result = new ArrayList<>(); - StringBuilder current = new StringBuilder(); - backtrack(result, current, 0, 0, n); - return result; - } - - private void backtrack(List result, StringBuilder current, - int open, int close, int max) { - // 终止条件:生成了 2n 个括号 - if (current.length() == 2 * max) { - result.add(current.toString()); - return; - } - - // 添加左括号:左括号数量小于 n - if (open < max) { - current.append('('); - backtrack(result, current, open + 1, close, max); - current.deleteCharAt(current.length() - 1); // 回溯 - } - - // 添加右括号:右括号数量小于左括号数量 - if (close < open) { - current.append(')'); - backtrack(result, current, open, close + 1, max); - current.deleteCharAt(current.length() - 1); // 回溯 - } - } - - // 测试用例 - public static void main(String[] args) { - GenerateParentheses solution = new GenerateParentheses(); - - // 测试用例1 - int n1 = 3; - System.out.println("输入: n = " + n1); - System.out.println("输出: " + solution.generateParenthesis(n1)); - - // 测试用例2 - int n2 = 1; - System.out.println("\n输入: n = " + n2); - System.out.println("输出: " + solution.generateParenthesis(n2)); - - // 测试用例3 - int n3 = 4; - System.out.println("\n输入: n = " + n3); - List result3 = solution.generateParenthesis(n3); - System.out.println("输出长度: " + result3.size()); - System.out.println("输出: " + result3); - - // 验证卡特兰数 - System.out.println("\n卡特兰数验证:"); - for (int i = 1; i <= 8; i++) { - System.out.println("n = " + i + ", 组合数 = " + - solution.generateParenthesis(i).size()); - } - } -} -``` ### Go 实现(动态规划) @@ -234,30 +168,6 @@ func generateParenthesisDP(n int) []string { ### Java 实现(动态规划) -```java -public List generateParenthesisDP(int n) { - List> dp = new ArrayList<>(); - List dp0 = new ArrayList<>(); - dp0.add(""); - dp.add(dp0); - - for (int i = 1; i <= n; i++) { - List current = new ArrayList<>(); - for (int j = 0; j < i; j++) { - List leftList = dp.get(j); - List rightList = dp.get(i - 1 - j); - for (String left : leftList) { - for (String right : rightList) { - current.add("(" + left + ")" + right); - } - } - } - dp.add(current); - } - - return dp.get(n); -} -``` ## 复杂度分析 diff --git a/16-LeetCode Hot 100/无重复字符的最长子串.md b/16-LeetCode Hot 100/无重复字符的最长子串.md index c3dd47e..ef4b73b 100644 --- a/16-LeetCode Hot 100/无重复字符的最长子串.md +++ b/16-LeetCode Hot 100/无重复字符的最长子串.md @@ -81,40 +81,8 @@ func lengthOfLongestSubstring(s string) int { --- -## Java 解法 +## 解法 -```java -class Solution { - public int lengthOfLongestSubstring(String s) { - // 记录字符最后出现的位置 - Map charIndex = new HashMap<>(); - int maxLength = 0; - int left = 0; - - for (int right = 0; right < s.length(); right++) { - char char = s.charAt(right); - - // 如果字符已存在且在窗口内,移动左边界 - if (charIndex.containsKey(char) && charIndex.get(char) >= left) { - left = charIndex.get(char) + 1; - } - - // 更新字符位置 - charIndex.put(char, right); - - // 更新最大长度 - maxLength = Math.max(maxLength, right - left + 1); - } - - return maxLength; - } -} -``` - -### Java 代码要点 -1. `HashMap` 记录字符索引 -2. `charAt()` 遍历字符串 -3. `Math.max()` 更新最大值 --- diff --git a/16-LeetCode Hot 100/最长回文子串.md b/16-LeetCode Hot 100/最长回文子串.md index 023fc1c..91cb247 100644 --- a/16-LeetCode Hot 100/最长回文子串.md +++ b/16-LeetCode Hot 100/最长回文子串.md @@ -136,71 +136,6 @@ func main() { ### Java 实现(中心扩展法) -```java -public class LongestPalindromicSubstring { - - public String longestPalindrome(String s) { - if (s == null || s.length() < 2) { - return s; - } - - int start = 0, maxLen = 1; - - for (int i = 0; i < s.length(); i++) { - // 奇数长度:以当前字符为中心 - int len1 = expandAroundCenter(s, i, i); - // 偶数长度:以当前字符和下一个字符之间为中心 - int len2 = expandAroundCenter(s, i, i + 1); - - int currentLen = Math.max(len1, len2); - if (currentLen > maxLen) { - maxLen = currentLen; - start = i - (currentLen - 1) / 2; - } - } - - return s.substring(start, start + maxLen); - } - - private int expandAroundCenter(String s, int left, int right) { - while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) { - left--; - right++; - } - return right - left - 1; - } - - // 测试用例 - public static void main(String[] args) { - LongestPalindromicSubstring solution = new LongestPalindromicSubstring(); - - // 测试用例1 - String s1 = "babad"; - System.out.println("输入: " + s1); - System.out.println("输出: " + solution.longestPalindrome(s1)); - - // 测试用例2 - String s2 = "cbbd"; - System.out.println("\n输入: " + s2); - System.out.println("输出: " + solution.longestPalindrome(s2)); - - // 测试用例3: 单个字符 - String s3 = "a"; - System.out.println("\n输入: " + s3); - System.out.println("输出: " + solution.longestPalindrome(s3)); - - // 测试用例4: 全部相同 - String s4 = "aaaa"; - System.out.println("\n输入: " + s4); - System.out.println("输出: " + solution.longestPalindrome(s4)); - - // 测试用例5: 无回文 - String s5 = "abc"; - System.out.println("\n输入: " + s5); - System.out.println("输出: " + solution.longestPalindrome(s5)); - } -} -``` ### Go 实现(动态规划) @@ -246,41 +181,6 @@ func longestPalindromeDP(s string) string { ### Java 实现(动态规划) -```java -public String longestPalindromeDP(String s) { - if (s == null || s.length() < 2) { - return s; - } - - int n = s.length(); - boolean[][] dp = new boolean[n][n]; - int start = 0, maxLen = 1; - - // 初始化:所有单个字符都是回文串 - for (int i = 0; i < n; i++) { - dp[i][i] = true; - } - - // 按长度递增的顺序遍历 - for (int length = 2; length <= n; length++) { - for (int i = 0; i <= n - length; i++) { - int j = i + length - 1; - - if (s.charAt(i) == s.charAt(j)) { - if (length == 2 || dp[i + 1][j - 1]) { - dp[i][j] = true; - if (length > maxLen) { - maxLen = length; - start = i; - } - } - } - } - } - - return s.substring(start, start + maxLen); -} -``` ### Go 实现(Manacher 算法) diff --git a/16-LeetCode Hot 100/柱状图中最大的矩形.md b/16-LeetCode Hot 100/柱状图中最大的矩形.md index d336edd..57e5862 100644 --- a/16-LeetCode Hot 100/柱状图中最大的矩形.md +++ b/16-LeetCode Hot 100/柱状图中最大的矩形.md @@ -54,27 +54,6 @@ func largestRectangleArea(heights []int) int { ### Java 实现 -```java -public int largestRectangleArea(int[] heights) { - Stack stack = new Stack<>(); - int maxArea = 0; - int n = heights.length; - - for (int i = 0; i <= n; i++) { - int h = (i == n) ? 0 : heights[i]; - - while (!stack.isEmpty() && h < heights[stack.peek()]) { - int height = heights[stack.pop()]; - int width = stack.isEmpty() ? i : i - stack.peek() - 1; - maxArea = Math.max(maxArea, height * width); - } - - stack.push(i); - } - - return maxArea; -} -``` ## 复杂度分析 diff --git a/16-LeetCode Hot 100/电话号码的字母组合.md b/16-LeetCode Hot 100/电话号码的字母组合.md index 64043a3..032cfe9 100644 --- a/16-LeetCode Hot 100/电话号码的字母组合.md +++ b/16-LeetCode Hot 100/电话号码的字母组合.md @@ -153,84 +153,6 @@ func main() { ### Java 实现(回溯法) -```java -import java.util.ArrayList; -import java.util.List; - -public class LetterCombinations { - - public List letterCombinations(String digits) { - List result = new ArrayList<>(); - if (digits == null || digits.length() == 0) { - return result; - } - - // 数字到字母的映射 - String[] phoneMap = { - "", // 0 - "", // 1 - "abc", // 2 - "def", // 3 - "ghi", // 4 - "jkl", // 5 - "mno", // 6 - "pqrs", // 7 - "tuv", // 8 - "wxyz" // 9 - }; - - StringBuilder current = new StringBuilder(); - backtrack(digits, 0, phoneMap, current, result); - return result; - } - - private void backtrack(String digits, int index, String[] phoneMap, - StringBuilder current, List result) { - if (index == digits.length()) { - result.add(current.toString()); - return; - } - - // 获取当前数字对应的所有字母 - int digit = digits.charAt(index) - '0'; - String letters = phoneMap[digit]; - - for (int i = 0; i < letters.length(); i++) { - // 选择当前字母 - current.append(letters.charAt(i)); - // 递归处理下一个数字 - backtrack(digits, index + 1, phoneMap, current, result); - // 撤销选择(回溯) - current.deleteCharAt(current.length() - 1); - } - } - - // 测试用例 - public static void main(String[] args) { - LetterCombinations solution = new LetterCombinations(); - - // 测试用例1 - String digits1 = "23"; - System.out.println("输入: " + digits1); - System.out.println("输出: " + solution.letterCombinations(digits1)); - - // 测试用例2 - String digits2 = ""; - System.out.println("\n输入: " + digits2); - System.out.println("输出: " + solution.letterCombinations(digits2)); - - // 测试用例3 - String digits3 = "2"; - System.out.println("\n输入: " + digits3); - System.out.println("输出: " + solution.letterCombinations(digits3)); - - // 测试用例4: 最长输入 - String digits4 = "9999"; - System.out.println("\n输入: " + digits4); - System.out.println("输出长度: " + solution.letterCombinations(digits4).size()); - } -} -``` ### Go 实现(队列迭代法) @@ -275,39 +197,6 @@ func letterCombinationsIterative(digits string) []string { ### Java 实现(队列迭代法) -```java -public List letterCombinationsIterative(String digits) { - List result = new ArrayList<>(); - if (digits == null || digits.length() == 0) { - return result; - } - - String[] phoneMap = { - "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" - }; - - // 初始化队列 - List queue = new ArrayList<>(); - queue.add(""); - - for (int i = 0; i < digits.length(); i++) { - int digit = digits.charAt(i) - '0'; - String letters = phoneMap[digit]; - List newQueue = new ArrayList<>(); - - // 取出队列中所有组合,与当前字母组合 - for (String combination : queue) { - for (int j = 0; j < letters.length(); j++) { - newQueue.add(combination + letters.charAt(j)); - } - } - - queue = newQueue; - } - - return queue; -} -``` ## 复杂度分析 diff --git a/16-LeetCode Hot 100/盛最多水的容器.md b/16-LeetCode Hot 100/盛最多水的容器.md index 449cc3d..4921849 100644 --- a/16-LeetCode Hot 100/盛最多水的容器.md +++ b/16-LeetCode Hot 100/盛最多水的容器.md @@ -125,65 +125,6 @@ func main() { ### Java 实现 -```java -public class ContainerWithMostWater { - - public int maxArea(int[] height) { - int left = 0; - int right = height.length - 1; - int maxArea = 0; - - while (left < right) { - // 计算当前面积 - int width = right - left; - int h = Math.min(height[left], height[right]); - int area = width * h; - - // 更新最大面积 - maxArea = Math.max(maxArea, area); - - // 移动较短的指针 - if (height[left] < height[right]) { - left++; - } else { - right--; - } - } - - return maxArea; - } - - // 测试用例 - public static void main(String[] args) { - ContainerWithMostWater solution = new ContainerWithMostWater(); - - // 测试用例1 - int[] height1 = {1, 8, 6, 2, 5, 4, 8, 3, 7}; - System.out.println("输入: [1, 8, 6, 2, 5, 4, 8, 3, 7]"); - System.out.println("输出: " + solution.maxArea(height1)); // 期望输出: 49 - - // 测试用例2 - int[] height2 = {1, 1}; - System.out.println("\n输入: [1, 1]"); - System.out.println("输出: " + solution.maxArea(height2)); // 期望输出: 1 - - // 测试用例3: 递增序列 - int[] height3 = {1, 2, 3, 4, 5}; - System.out.println("\n输入: [1, 2, 3, 4, 5]"); - System.out.println("输出: " + solution.maxArea(height3)); // 期望输出: 6 - - // 测试用例4: 递减序列 - int[] height4 = {5, 4, 3, 2, 1}; - System.out.println("\n输入: [5, 4, 3, 2, 1]"); - System.out.println("输出: " + solution.maxArea(height4)); // 期望输出: 6 - - // 测试用例5: 包含0 - int[] height5 = {0, 2}; - System.out.println("\n输入: [0, 2]"); - System.out.println("输出: " + solution.maxArea(height5)); // 期望输出: 0 - } -} -``` ## 复杂度分析