From 6976c55204e5234d518fd5149561739169eb5fd6 Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Mon, 30 Jun 2025 16:55:09 +0800 Subject: [PATCH] =?UTF-8?q?6.30=20=E4=BA=8C=E5=88=B7=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/linkedlist/LRUCache.java | 105 ------------ src/main/java/linkedlist/MergeKLists.java | 67 ++++++++ src/main/java/tree/DiameterOfBinaryTree.java | 14 +- src/main/java/tree/InorderTraversal.java | 16 ++ src/main/java/tree/InvertTree.java | 18 ++ src/main/java/tree/IsSymmetric.java | 3 + src/main/java/tree/IsValidBST.java | 3 + src/main/java/tree/KthSmallest.java | 26 +-- src/main/java/tree/LevelOrder.java | 1 + src/main/java/tree/MaxDepth.java | 2 + src/main/java/tree/SortedArrayToBST.java | 1 + src/main/java/实现类功能/LRUCache.java | 164 +++++++++++++++++++ 12 files changed, 303 insertions(+), 117 deletions(-) delete mode 100644 src/main/java/linkedlist/LRUCache.java create mode 100644 src/main/java/linkedlist/MergeKLists.java create mode 100644 src/main/java/实现类功能/LRUCache.java diff --git a/src/main/java/linkedlist/LRUCache.java b/src/main/java/linkedlist/LRUCache.java deleted file mode 100644 index a616d45..0000000 --- a/src/main/java/linkedlist/LRUCache.java +++ /dev/null @@ -1,105 +0,0 @@ -package linkedlist; -/** - * 题目: 146. LRU 缓存 (LRUCache) - * 描述:请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 - * 实现 LRUCache 类: - * LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 - * int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 - * void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。 - * 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。 - - 示例 1: - 输入 - ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] - [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] - 输出 - [null, null, null, 1, null, -1, null, -1, 3, 4] - - * 链接:https://leetcode.cn/problems/lru-cache/ - */ -import java.util.HashMap; -import java.util.Map; - -public class LRUCache { - class DLinkedNode { - int key; - int value; - DLinkedNode prev; - DLinkedNode next; - public DLinkedNode() {} - public DLinkedNode(int _key, int _value) {key = _key; value = _value;} - } - - private Map cache = new HashMap<>(); - private int size; - private int capacity; - private DLinkedNode head, tail; - - public LRUCache(int capacity) { - this.size = 0; - this.capacity = capacity; - // 使用伪头部和伪尾部节点 - head = new DLinkedNode(); - tail = new DLinkedNode(); - head.next = tail; - tail.prev = head; - } - - public int get(int key) { - DLinkedNode node = cache.get(key); - if (node == null) { - return -1; - } - // 如果 key 存在,先通过哈希表定位,再移到头部 - moveToHead(node); - return node.value; - } - - public void put(int key, int value) { - DLinkedNode node = cache.get(key); - if (node == null) { - // 如果 key 不存在,创建一个新的节点 - DLinkedNode newNode = new DLinkedNode(key, value); - // 添加进哈希表 - cache.put(key, newNode); - // 添加至双向链表的头部 - addToHead(newNode); - ++size; - if (size > capacity) { - // 如果超出容量,删除双向链表的尾部节点 - DLinkedNode tail = removeTail(); - // 删除哈希表中对应的项 - cache.remove(tail.key); - --size; - } - } - else { - // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部 - node.value = value; - moveToHead(node); - } - } - - private void addToHead(DLinkedNode node) { - node.prev = head; - node.next = head.next; - head.next.prev = node; - head.next = node; - } - - private void removeNode(DLinkedNode node) { - node.prev.next = node.next; - node.next.prev = node.prev; - } - - private void moveToHead(DLinkedNode node) { - removeNode(node); - addToHead(node); - } - - private DLinkedNode removeTail() { - DLinkedNode res = tail.prev; - removeNode(res); - return res; - } -} diff --git a/src/main/java/linkedlist/MergeKLists.java b/src/main/java/linkedlist/MergeKLists.java new file mode 100644 index 0000000..fc49f18 --- /dev/null +++ b/src/main/java/linkedlist/MergeKLists.java @@ -0,0 +1,67 @@ +package linkedlist; + +/** + * 题目: 23. 合并 K 个升序链表 (mergeKLists) + * 描述:给你一个链表数组,每个链表都已经按升序排列。 + * 请你将所有链表合并到一个升序链表中,返回合并后的链表。 + * 链接:https://leetcode.cn/problems/merge-k-sorted-lists + *

+ * 输入:lists = [[1,4,5],[1,3,4],[2,6]] + * 输出:[1,1,2,3,4,4,5,6] + * 解释:链表数组如下: + * [ + * 1->4->5, + * 1->3->4, + * 2->6 + * ] + * 将它们合并到一个有序链表中得到。 + * 1->1->2->3->4->4->5->6 + */ +//二刷不会 +public class MergeKLists { + //两两合并 + public ListNode mergeKLists2(ListNode[] lists) { + ListNode ans = null; + for (int i = 0; i < lists.length; ++i) { + ans = mergeTwoLists(ans, lists[i]); + } + return ans; + } + + public ListNode mergeTwoLists(ListNode a, ListNode b) { + if (a == null || b == null) { + return a != null ? a : b; + } + ListNode head = new ListNode(0); + ListNode tail = head, aPtr = a, bPtr = b; + while (aPtr != null && bPtr != null) { + if (aPtr.val < bPtr.val) { + tail.next = aPtr; + aPtr = aPtr.next; + } else { + tail.next = bPtr; + bPtr = bPtr.next; + } + tail = tail.next; + } + tail.next = (aPtr != null ? aPtr : bPtr); + return head.next; + } + + + //分治合并 + public ListNode mergeKLists(ListNode[] lists) { + return merge(lists, 0, lists.length - 1); + } + + public ListNode merge(ListNode[] lists, int l, int r) { + if (l == r) { + return lists[l]; + } + if (l > r) { + return null; + } + int mid = (l + r) >> 1; + return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r)); + } +} diff --git a/src/main/java/tree/DiameterOfBinaryTree.java b/src/main/java/tree/DiameterOfBinaryTree.java index 564dc71..4048e11 100644 --- a/src/main/java/tree/DiameterOfBinaryTree.java +++ b/src/main/java/tree/DiameterOfBinaryTree.java @@ -6,8 +6,20 @@ package tree; * 两节点之间路径的 长度 由它们之间边数表示。 * 链接:https://leetcode.cn/problems/diameter-of-binary-tree/ - + 1 + / \ + 2 3 + / \ + 4 5 + / \ + 6 7 + / \ + 8 9 + / \ + 10 11 +不一定经过root! */ +//二刷会做 public class DiameterOfBinaryTree { private int maxDiameter = 0; diff --git a/src/main/java/tree/InorderTraversal.java b/src/main/java/tree/InorderTraversal.java index 42f2b62..5417254 100644 --- a/src/main/java/tree/InorderTraversal.java +++ b/src/main/java/tree/InorderTraversal.java @@ -11,6 +11,7 @@ import java.util.List; * 链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/ */ +//二刷会做 //递归会 迭代需记一下 public class InorderTraversal { //递归 @@ -29,6 +30,21 @@ public class InorderTraversal { inorder(root.right, res); } + public List inorderTraversal2(TreeNode root) { + List res=new ArrayList<>(); + Dequestack=new ArrayDeque<>(); + while (root!=null || !stack.isEmpty()){ + while (root!=null){ + stack.push(root); + root=root.left; + } + root=stack.pop(); + res.add(root.val); + root=root.right; + } + return res; + } + //迭代+栈 public List inorderTraversal1(TreeNode root) { List res = new ArrayList(); diff --git a/src/main/java/tree/InvertTree.java b/src/main/java/tree/InvertTree.java index 56e598f..d947203 100644 --- a/src/main/java/tree/InvertTree.java +++ b/src/main/java/tree/InvertTree.java @@ -6,7 +6,24 @@ package tree; * 链接:https://leetcode.cn/problems/invert-binary-tree/ */ +//二刷会做 public class InvertTree { + void dfs(TreeNode root){ + if(root!=null) { + TreeNode tp; + tp = root.left; + root.left = root.right; + root.right = tp; + dfs(root.left); + dfs(root.right); + } + } + public TreeNode invertTree2(TreeNode root) { + dfs(root); + return root; + } + + //先调整父节点,再调子节点 public void invert(TreeNode root){ if(root!=null){ TreeNode temp; @@ -21,6 +38,7 @@ public class InvertTree { invert(root); return root; } + //先调子 再调父节点 public TreeNode invertTree(TreeNode root) { if (root == null) { return null; diff --git a/src/main/java/tree/IsSymmetric.java b/src/main/java/tree/IsSymmetric.java index e710793..53fddbb 100644 --- a/src/main/java/tree/IsSymmetric.java +++ b/src/main/java/tree/IsSymmetric.java @@ -11,8 +11,10 @@ import java.util.Queue; */ //不会做,递归和迭代都写一下 +//二刷不会 public class IsSymmetric { + //递归 public boolean isSymmetric(TreeNode root) { // 如果根节点为空,则认为是对称的 if (root == null) { @@ -40,6 +42,7 @@ public class IsSymmetric { && isMirror(left.right, right.left); } + //层序遍历 public boolean isSymmetric1(TreeNode root) { if (root == null) { return true; diff --git a/src/main/java/tree/IsValidBST.java b/src/main/java/tree/IsValidBST.java index eee5b4a..e1398c4 100644 --- a/src/main/java/tree/IsValidBST.java +++ b/src/main/java/tree/IsValidBST.java @@ -15,7 +15,9 @@ import java.util.List; * 链接:https://leetcode.cn/problems/validate-binary-search-tree/ */ +//二刷会做 public class IsValidBST { + //递归 public boolean helper(TreeNode node, long lower, long upper) { if (node == null) { @@ -30,6 +32,7 @@ public class IsValidBST { return helper(root, Long.MIN_VALUE, Long.MAX_VALUE); } + //中序遍历 public void dfs(TreeNode root,Listlist){ if(root!=null){ diff --git a/src/main/java/tree/KthSmallest.java b/src/main/java/tree/KthSmallest.java index 6216a4d..bb5ec63 100644 --- a/src/main/java/tree/KthSmallest.java +++ b/src/main/java/tree/KthSmallest.java @@ -8,20 +8,24 @@ import java.util.List; * 描述:给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 * 链接:https://leetcode.cn/problems/kth-smallest-element-in-a-bst/ - + 进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法? */ +//二刷会做 public class KthSmallest { //中序遍历 - public void dfs(TreeNode root, List list){ - if(root!=null){ - dfs(root.left,list); - list.add(root.val); - dfs(root.right,list); - } - } + private int k, ans; public int kthSmallest(TreeNode root, int k) { - Listlist=new ArrayList<>(); - dfs(root,list); - return list.get(k-1); + this.k = k; + dfs(root); + return ans; + } + private void dfs(TreeNode node) { + if (node == null) return; + dfs(node.left); + if (--k == 0) { // 访问到第 k 个 + ans = node.val; + return; // 提前结束递归 + } + dfs(node.right); } } diff --git a/src/main/java/tree/LevelOrder.java b/src/main/java/tree/LevelOrder.java index 77a2feb..bfd00c0 100644 --- a/src/main/java/tree/LevelOrder.java +++ b/src/main/java/tree/LevelOrder.java @@ -12,6 +12,7 @@ import java.util.Queue; * 链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/ */ +//二刷会做 public class LevelOrder { public List> levelOrder(TreeNode root) { List>res=new ArrayList<>(); diff --git a/src/main/java/tree/MaxDepth.java b/src/main/java/tree/MaxDepth.java index 7f69a62..e55e71f 100644 --- a/src/main/java/tree/MaxDepth.java +++ b/src/main/java/tree/MaxDepth.java @@ -13,6 +13,7 @@ import java.util.Queue; * 链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/ */ +//二刷会做 public class MaxDepth { //递归 int dfs(TreeNode root){ @@ -23,6 +24,7 @@ public class MaxDepth { public int maxDepth1(TreeNode root) { return dfs(root); } + //bfs public int maxDepth(TreeNode root) { if (root == null) { diff --git a/src/main/java/tree/SortedArrayToBST.java b/src/main/java/tree/SortedArrayToBST.java index 5821e40..2ddb9c9 100644 --- a/src/main/java/tree/SortedArrayToBST.java +++ b/src/main/java/tree/SortedArrayToBST.java @@ -6,6 +6,7 @@ package tree; * 链接:https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/ */ +//二刷不会 public class SortedArrayToBST { //分治 TreeNode func(int left,int right ,int[] nums){ diff --git a/src/main/java/实现类功能/LRUCache.java b/src/main/java/实现类功能/LRUCache.java new file mode 100644 index 0000000..69c6800 --- /dev/null +++ b/src/main/java/实现类功能/LRUCache.java @@ -0,0 +1,164 @@ +package 实现类功能; +/** + * 题目: 146. LRU 缓存 (LRUCache) + * 描述:请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 + * 实现 LRUCache 类: + * LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 + * int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 + * void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。 + * 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。 + + 示例 1: + 输入 + ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] + [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] + 输出 + [null, null, null, 1, null, -1, null, -1, 3, 4] + + * 链接:https://leetcode.cn/problems/lru-cache/ + */ +import java.util.HashMap; +import java.util.Map; +//二刷不会 学习 + +/** + * 对于 get 操作,首先判断 key 是否存在: + * 如果 key 不存在,则返回 −1; + * 如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。 + * + * 对于 put 操作,首先判断 key 是否存在: + * 如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项; + * 如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。 + * + * + * 注意,key和value不必相等 + */ +public class LRUCache { + + /* ----------------------------- 链表节点定义 ----------------------------- */ + + /** + * 双向链表节点 + */ + class DLinkedNode { + int key; // 保存 key:便于淘汰节点时同步从 cache 中删除 + int value; // 保存 value + DLinkedNode prev; // 前驱 + DLinkedNode next; // 后继 + + public DLinkedNode() {} + + public DLinkedNode(int _key, int _value) { + key = _key; + value = _value; + } + } + + /* ----------------------------- 成员变量 ----------------------------- */ + + private final Map cache = new HashMap<>(); // 哈希表 + private int size; // 当前键值对数量 + private final int capacity; // 最大容量 + + // 伪头、伪尾(固定不动) + private final DLinkedNode head; + private final DLinkedNode tail; + + /* ----------------------------- 构造函数 ----------------------------- */ + + /** + * @param capacity 缓存最大容量 (正整数) + */ + public LRUCache(int capacity) { + this.size = 0; + this.capacity = capacity; + + // 创建伪头、伪尾并相连 + head = new DLinkedNode(); + tail = new DLinkedNode(); + head.next = tail; + tail.prev = head; + } + + /* ----------------------------- 公共接口 ----------------------------- */ + + /** + * @param key 查询的键 + * @return 若存在则返回对应 value,否则返回 -1 + */ + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) { // 不存在 + return -1; + } + // 存在:移动到链表头部(标记为最近使用) + moveToHead(node); + return node.value; + } + + /** + * 写入或更新键值对 + * @param key 键 + * @param value 值 + */ + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + + if (node == null) { + /* ---------- 新键 ---------- */ + DLinkedNode newNode = new DLinkedNode(key, value); + cache.put(key, newNode); // 哈希表记录 + addToHead(newNode); // 链表头插 + ++size; + + // 超容量:弹出尾节点(最久未使用) + if (size > capacity) { + DLinkedNode tail = removeTail(); // 真尾节点 + cache.remove(tail.key); // 同步删除哈希表项 + --size; + } + } else { + /* ---------- 已存在键:更新值并提到头部 ---------- */ + node.value = value; + moveToHead(node); + } + } + + /* ----------------------------- 链表操作辅助 ----------------------------- */ + + /** + * 将节点插到伪头之后(链表头部) + */ + private void addToHead(DLinkedNode node) { + node.prev = head; + node.next = head.next; + head.next.prev = node; + head.next = node; + } + + /** + * 从当前链表中删除节点(不删除哈希表项) + */ + private void removeNode(DLinkedNode node) { + node.prev.next = node.next; + node.next.prev = node.prev; + } + + /** + * 将节点移动到头部:先删再头插 + */ + private void moveToHead(DLinkedNode node) { + removeNode(node); + addToHead(node); + } + + /** + * 弹出尾部节点(真尾 = 伪尾的前驱) + * @return 被移除的节点 + */ + private DLinkedNode removeTail() { + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } +}