From 5efaae9af5d81f6d650689e53716ac53d2abb3da Mon Sep 17 00:00:00 2001 From: brightzoe Date: Sun, 24 Mar 2024 22:58:34 +0800 Subject: [PATCH] docs: update docs: update docs: update --- docs/algorithm/list-problem.md | 75 ++++++++++++++++ docs/algorithm/stack-problem.md | 146 ++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 docs/algorithm/stack-problem.md diff --git a/docs/algorithm/list-problem.md b/docs/algorithm/list-problem.md index 9cf40f85..14ecb50f 100644 --- a/docs/algorithm/list-problem.md +++ b/docs/algorithm/list-problem.md @@ -188,3 +188,78 @@ const three = new ListNode(3); const two = new ListNode(2, three); const one = new ListNode(1, two); ``` + +[92. 反转链表 II - 力扣(LeetCode)](https://leetcode.cn/problems/reverse-linked-list-ii/) + +- 局部反转链表。是上一题的加强版,利用多指针。第一种思路,找到开始节点,和preNode,找到结束节点和nextNode。将链表切成三部分,将中间切开的部分进行反转,然后preNode.next = newStart;start.next = nextNode 进行拼接。思路顺畅但编码量稍多。 +- 第二种思路,同样是遍历链表,找到preNode,原地将left-node段进行反转。使用pre,curr的快慢指针遍历反转。然后特殊处理两个边界, + +> 注意添加dummy节点简化思路。 + +```ts +//拆分为三段 +function reverseBetween( + head: ListNode | null, + left: number, + right: number, +): ListNode | null { + let dummy = new ListNode(0, head); + let pre = dummy; + for (let i = 0; i < left - 1; i++) { + pre = pre.next; + } + let start = pre.next; + let end = pre; + for (let i = 0; i < right - left + 1; i++) { + end = end.next; + } + let next = end.next; + end.next = null; + pre.next = null; + reverse(start); + + pre.next = end; + + start.next = next; + + return dummy.next; +} + +function reverse(head: ListNode) { + let pre = null; + let curr = head; + while (curr) { + const next = curr.next; + curr.next = pre; + pre = curr; + curr = next; + } +} +``` + +```ts +// 不拆分直接反转 +function reverseBetween( + head: ListNode | null, + left: number, + right: number, +): ListNode | null { + let dummy = new ListNode(0, head); + let leftPre = dummy; + for (let i = 0; i < left - 1; i++) { + leftPre = leftPre.next; + } + let start = leftPre.next; + let pre = start; + let curr = start.next; + for (let i = left; i < right; i++) { + const next = curr.next; + curr.next = pre; + pre = curr; + curr = next; + } + leftPre.next = pre; + start.next = curr; + return dummy.next; +} +``` diff --git a/docs/algorithm/stack-problem.md b/docs/algorithm/stack-problem.md new file mode 100644 index 00000000..d1584436 --- /dev/null +++ b/docs/algorithm/stack-problem.md @@ -0,0 +1,146 @@ +# 栈和队列 + +## 栈 + +[20. 有效的括号 - 力扣(LeetCode)](https://leetcode.cn/problems/valid-parentheses/) + +- 对称性。利用栈,先入栈的后出栈。栈的后进先出原则,一组数据的入栈和出栈顺序刚好是对称的。 + +```ts +function isValid(s: string): boolean { + // 边界 + if (!s.length) { + return true; + } + // Map或者obj都是可以的 + const map = new Map([ + ['(', ')'], + ['[', ']'], + ['{', '}'], + ]); + let stack = []; + for (let i = 0; i < s.length; i++) { + // 是左边的括号直接进入 + if (map.has(s[i])) { + stack.push(s[i]); + } else { + // 是右边的括号但无法出栈直接返回 + if (s[i] !== map.get(stack.pop())) { + return false; + } + } + } + // 是不是空栈 + return !stack.length; +} +``` + +## 单调栈 + +[739. 每日温度 - 力扣(LeetCode)](https://leetcode.cn/problems/daily-temperatures/submissions/516316386/) + +- 暴力解法,循环套循环,超时。 +- 维护一个递减的栈。循环一次,每次将当前元素与栈顶元素比较,若栈顶元素小于当前元素,则将栈顶元素出栈,并计算差值。直到栈顶元素大于等于当前元素,将当前元素入栈。 + +```ts +// 维护递减的栈 +function dailyTemperatures(temperatures: number[]): number[] { + const res = new Array(temperatures.length).fill(0); + // 递减栈,维护的是每天的索引而不是温度,比较好计算 + const stack: number[] = []; + + for (let i = 0; i < temperatures.length; i++) { + // 当前温度大于栈顶的温度 + while ( + stack.length && + temperatures[i] > temperatures[stack[stack.length - 1]] + ) { + const top = stack.pop()!; + // 栈顶的索引放入结果中,值为i-top + res[top] = i - top; + } + stack.push(i); + } + return res; +} +``` + +[155. 最小栈 - 力扣(LeetCode)](https://leetcode.cn/problems/min-stack/description/) + +- 设计一个数据结构,支持push/pop等,并能够检索到最小值。getMin可以通过一遍循环实现,时间复杂度O(n),空间复杂度O(1)。利用空间换时间,多维护一个递减栈,将最小值放在栈顶。getMin直接从栈顶拿。 + +```ts +class MinStack { + stack: number[]; + minStack: number[]; + constructor() { + this.stack = []; + this.minStack = []; + } + push(val: number) { + this.stack.push(val); + const min = this.minStack[this.minStack.length - 1]; + // 当前值更小,推入递减栈 注意边界条件 + if (this.minStack.length === 0 || val <= min) { + this.minStack.push(val); + } + } + pop() { + const top = this.stack.pop(); + const min = this.minStack[this.minStack.length - 1]; + // 小数走了,把对应的递减值推出去 注意边界条件 + if (this.minStack.length > 0 && top === min) { + this.minStack.pop(); + } + } + top() { + return this.stack[this.stack.length - 1]; + } + getMin() { + console.log(this.minStack); + return this.minStack[this.minStack.length - 1]; + } +} +``` + +## 用栈实现队列 + +[232. 用栈实现队列 - 力扣(LeetCode)](https://leetcode.cn/problems/implement-queue-using-stacks/submissions/516876214/) + +- 用两个栈倒过来倒过去。 + +```ts +class MyQueue { + private stack1: number[]; + private stack2: number[]; + constructor() { + this.stack1 = []; + this.stack2 = []; + } + push(val: number) { + this.stack1.push(val); + } + + peek() { + if (this.stack2.length === 0) { + while (this.stack1.length) { + const last = this.stack1.pop()!; + this.stack2.push(last); + } + } + return this.stack2[this.stack2.length - 1]; + } + pop() { + if (this.stack2.length === 0) { + while (this.stack1.length) { + const last = this.stack1.pop()!; + this.stack2.push(last); + } + } + return this.stack2.pop(); + } + empty() { + return this.stack1.length === 0 && this.stack2.length === 0; + } +} +```