5.13 二轮 链表+栈

This commit is contained in:
zhangsan 2025-05-13 17:16:41 +08:00
parent 60f7c9d91f
commit 67697ee1a8
6 changed files with 249 additions and 3 deletions

View File

@ -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;
}
}

View File

@ -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 最终要指向待反转区间的前一个节点 left1 个节点
ListNode pre = dummyNode;
// left1 停在第 left1 个节点上
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 之后
// 需要执行 rightleft 次操作才能把区间内所有节点都移动到前面
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;
}
}

View File

@ -1,4 +1,4 @@
package greedy;
package range;
import java.util.Arrays;
import java.util.HashMap;

View File

@ -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<String> 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<String> 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();
}
}

View File

@ -1,8 +1,7 @@
package greedy;
import org.junit.Test;
import static org.junit.Assert.*;
import range.FindMinArrowShots;
public class FindMinArrowShotsTest {

View File

@ -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);
}
}