From 67697ee1a83addf794742be7c2c277263b62a65c Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Tue, 13 May 2025 17:16:41 +0800 Subject: [PATCH] =?UTF-8?q?5.13=20=E4=BA=8C=E8=BD=AE=20=E9=93=BE=E8=A1=A8+?= =?UTF-8?q?=E6=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/linkedlist/DeleteDuplicates.java | 36 +++++++ src/main/java/linkedlist/ReverseBetween.java | 95 +++++++++++++++++ .../{greedy => range}/FindMinArrowShots.java | 2 +- src/main/java/stack/SimplifyPath.java | 100 ++++++++++++++++++ .../java/greedy/FindMinArrowShotsTest.java | 3 +- src/test/java/stack/SimplifyPathTest.java | 16 +++ 6 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 src/main/java/linkedlist/DeleteDuplicates.java create mode 100644 src/main/java/linkedlist/ReverseBetween.java rename src/main/java/{greedy => range}/FindMinArrowShots.java (98%) create mode 100644 src/main/java/stack/SimplifyPath.java create mode 100644 src/test/java/stack/SimplifyPathTest.java diff --git a/src/main/java/linkedlist/DeleteDuplicates.java b/src/main/java/linkedlist/DeleteDuplicates.java new file mode 100644 index 0000000..1b22602 --- /dev/null +++ b/src/main/java/linkedlist/DeleteDuplicates.java @@ -0,0 +1,36 @@ +package linkedlist; +/** + * 题目: 82. 删除排序链表中的重复元素 II (deleteDuplicates) + * 描述:给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 + + 示例 2: + 输入:head = [1,2,3,3,4,4,5] + 输出:[1,2,5] + + * 链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/ + */ +public class DeleteDuplicates { + public ListNode deleteDuplicates(ListNode head) { + if (head == null) { + return head; + } + + ListNode dummy = new ListNode(0, head); + + ListNode cur = dummy; + while (cur.next != null && cur.next.next != null) { + //cur及之前的元素一定是已经处理好的,如果它的下一个和下下个元素相同,那就删 + if (cur.next.val == cur.next.next.val) { + int x = cur.next.val; + //while循环删到没有这个值的元素为止 + while (cur.next != null && cur.next.val == x) { + cur.next = cur.next.next; + } + } else { + cur = cur.next; + } + } + + return dummy.next; + } +} diff --git a/src/main/java/linkedlist/ReverseBetween.java b/src/main/java/linkedlist/ReverseBetween.java new file mode 100644 index 0000000..6e67f24 --- /dev/null +++ b/src/main/java/linkedlist/ReverseBetween.java @@ -0,0 +1,95 @@ +package linkedlist; +/** + * 题目: 92. 反转链表 II (reverseBetween) + * 描述:给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。 + + 示例 2: + 输入:head = [1,2,3,4,5], left = 2, right = 4 + 输出:[1,4,3,2,5] + + * 链接:https://leetcode.cn/problems/reverse-linked-list-ii/ + */ +//不会一次遍历来翻转 +public class ReverseBetween { + private void reverseLinkedList(ListNode head) { + // 也可以使用递归反转一个链表 + ListNode pre = null; + ListNode cur = head; + + while (cur != null) { + ListNode next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + } + //遍历两次,先找两个切断的点left和right,再对内部翻转,再拼接 + public ListNode reverseBetween2(ListNode head, int left, int right) { + // 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论 + ListNode dummyNode = new ListNode(-1); + dummyNode.next = head; + + ListNode pre = dummyNode; + // 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点 + // 建议写在 for 循环里,语义清晰 + for (int i = 0; i < left - 1; i++) { + pre = pre.next; + } + + // 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点 + ListNode rightNode = pre; + for (int i = 0; i < right - left + 1; i++) { + rightNode = rightNode.next; + } + + // 第 3 步:切断出一个子链表(截取链表) + ListNode leftNode = pre.next; + ListNode curr = rightNode.next; + + // 注意:切断链接 + pre.next = null; + rightNode.next = null; + + // 第 4 步:同第 206 题,反转链表的子区间 + reverseLinkedList(leftNode); + + // 第 5 步:接回到原来的链表中 + pre.next = rightNode; + leftNode.next = curr; + return dummyNode.next; + } + //头插法反转 + public ListNode reverseBetween(ListNode head, int left, int right) { + // 1. 创建哨兵节点,简化边界处理(当 left = 1 时,也能统一处理) + ListNode dummyNode = new ListNode(-1); + dummyNode.next = head; + + // 2. pre 最终要指向待反转区间的前一个节点:第 left−1 个节点 + ListNode pre = dummyNode; + // 走 left−1 步,停在第 left−1 个节点上 + for (int i = 0; i < left - 1; i++) { + pre = pre.next; + } + + // 3. cur 指向第 left 个节点,也就是待反转区间的第一个节点 + ListNode cur = pre.next; + // next 用于临时保存 cur.next 节点 + ListNode next; + + // 4. 头插法:将 [left+1, right] 之间的节点依次“摘下”并插入到 pre 之后 + // 需要执行 right−left 次操作,才能把区间内所有节点都移动到前面 + for (int i = 0; i < right - left; i++) { + // 4.1 取出要移动的节点 + next = cur.next; + // 4.2 将 next 从原位置摘除:让 cur 跳过 next + cur.next = next.next; + // 4.3 将 next 插到 pre 之后(即已反转部分的头部) + next.next = pre.next; + pre.next = next; + //(cur 不动,next 每次都变成下一个要插入的节点) + } + + // 5. 返回新的链表头 + return dummyNode.next; + } +} diff --git a/src/main/java/greedy/FindMinArrowShots.java b/src/main/java/range/FindMinArrowShots.java similarity index 98% rename from src/main/java/greedy/FindMinArrowShots.java rename to src/main/java/range/FindMinArrowShots.java index 749d575..c48f66f 100644 --- a/src/main/java/greedy/FindMinArrowShots.java +++ b/src/main/java/range/FindMinArrowShots.java @@ -1,4 +1,4 @@ -package greedy; +package range; import java.util.Arrays; import java.util.HashMap; diff --git a/src/main/java/stack/SimplifyPath.java b/src/main/java/stack/SimplifyPath.java new file mode 100644 index 0000000..fab821a --- /dev/null +++ b/src/main/java/stack/SimplifyPath.java @@ -0,0 +1,100 @@ +package stack; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +/** + * 题目: 71. 简化路径 (simplifyPath) + * 描述:给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为 更加简洁的规范路径。 + * + * 在 Unix 风格的文件系统中规则如下: + * + * 一个点 '.' 表示当前目录本身。 + * 此外,两个点 '..' 表示将目录切换到上一级(指向父目录)。 + * 任意多个连续的斜杠(即,'//' 或 '///')都被视为单个斜杠 '/'。 + * 任何其他格式的点(例如,'...' 或 '....')均被视为有效的文件/目录名称。 + * 返回的 简化路径 必须遵循下述格式: + * + * 始终以斜杠 '/' 开头。 + * 两个目录名之间必须只有一个斜杠 '/' 。 + * 最后一个目录名(如果存在)不能 以 '/' 结尾。 + * 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.' 或 '..')。 + * 返回简化后得到的 规范路径 。 + + 示例 1: + 输入:path = "/home/" + 输出:"/home" + 解释: + 应删除尾随斜杠。 + + * 链接:https://leetcode.cn/problems/simplify-path/ + */ +public class SimplifyPath { + public String simplifyPath(String path) { + Deque stack = new ArrayDeque<>(); + // 按 '/' 分割,连续斜杠会产生空字符串 + for (String dir : path.split("/")) { + if (dir.isEmpty() || dir.equals(".")) { + // 当前目录或多余的斜杠,跳过 + continue; + } + if (dir.equals("..")) { + // 返回上一级目录 + if (!stack.isEmpty()) { + stack.pop(); + } + } else { + // 有效目录名,入栈 + stack.push(dir); + } + } + + // 构建结果路径 + if (stack.isEmpty()) { + return "/"; + } + StringBuilder result = new StringBuilder(); + while (!stack.isEmpty()) { + result.insert(0, stack.pop()); + result.insert(0, "/"); + } + return result.toString(); + } + public String simplifyPath2(String path) { + Deque stack = new ArrayDeque<>(); + StringBuilder segment = new StringBuilder(); + // 在末尾加一个 '/',方便最后一段也能被处理 + String p = path.endsWith("/") ? path : path + "/"; + + for (int i = 0; i < p.length(); i++) { + char c = p.charAt(i); + if (c == '/') { + if (segment.length() > 0) { + String dir = segment.toString(); + if ("..".equals(dir)) { + if (!stack.isEmpty()) { + stack.pop(); + } + } else if (!".".equals(dir)) { + stack.push(dir); + } + segment.setLength(0); + } + } else { + segment.append(c); + } + } + + // 构建规范路径 + if (stack.isEmpty()) { + return "/"; + } + StringBuilder res = new StringBuilder(); + while (!stack.isEmpty()) { + res.insert(0, "/" + stack.pop()); + } + return res.toString(); + } +} diff --git a/src/test/java/greedy/FindMinArrowShotsTest.java b/src/test/java/greedy/FindMinArrowShotsTest.java index 60b1120..a87a9b7 100644 --- a/src/test/java/greedy/FindMinArrowShotsTest.java +++ b/src/test/java/greedy/FindMinArrowShotsTest.java @@ -1,8 +1,7 @@ package greedy; import org.junit.Test; - -import static org.junit.Assert.*; +import range.FindMinArrowShots; public class FindMinArrowShotsTest { diff --git a/src/test/java/stack/SimplifyPathTest.java b/src/test/java/stack/SimplifyPathTest.java new file mode 100644 index 0000000..2bf136e --- /dev/null +++ b/src/test/java/stack/SimplifyPathTest.java @@ -0,0 +1,16 @@ +package stack; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SimplifyPathTest { + + @Test + public void simplifyPath() { + String path = "/home/"; + SimplifyPath solution = new SimplifyPath(); + String res=solution.simplifyPath(path); + System.out.println(res); + } +} \ No newline at end of file