From 0a41e81e588e7d2a38552f42e1bca078a501ad2d Mon Sep 17 00:00:00 2001 From: LouisFonda Date: Sat, 7 Jun 2025 16:08:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E6=B2=BB=E7=AE=97=E6=B3=9523,?= =?UTF-8?q?10,148,427?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../divide/108将有序数组转换为二叉树搜索树.js | 38 ++++ .../divide/148排序列表.js | 166 ++++++++++++++++++ .../divide/23合并k个升序列表.js | 112 ++++++++++++ .../divide/427构建四叉树.js | 93 ++++++++++ 4 files changed, 409 insertions(+) create mode 100644 top-interview-leetcode150/divide/108将有序数组转换为二叉树搜索树.js create mode 100644 top-interview-leetcode150/divide/148排序列表.js create mode 100644 top-interview-leetcode150/divide/23合并k个升序列表.js create mode 100644 top-interview-leetcode150/divide/427构建四叉树.js diff --git a/top-interview-leetcode150/divide/108将有序数组转换为二叉树搜索树.js b/top-interview-leetcode150/divide/108将有序数组转换为二叉树搜索树.js new file mode 100644 index 0000000..c8cc494 --- /dev/null +++ b/top-interview-leetcode150/divide/108将有序数组转换为二叉树搜索树.js @@ -0,0 +1,38 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {number[]} nums + * @return {TreeNode} + */ +const sortedArrayToBST = function (nums) { + +}; + +function TreeNode(val, left, right) { + this.val = (val === undefined ? 0 : val); + this.left = (left === undefined ? null : left); + this.right = (right === undefined ? null : right); +} + +/* +用数组的中间值作为根节点,然后递归的将左半部分和右半部分处理成一颗二叉搜索树,并设置成根节点的左右子树 +*/ +function f1(left, right, nums) { + // 如果left > right, 说明这个区间没有元素,直接返回null + if (left > right) return null; + // 计算中间节点的索引 + const mid = left + Math.floor((right - left) / 2); + + // 创建根节点,或子树的根节点 + const root = new TreeNode(nums[mid]); + // 递归处理左半部分和右半部分 + root.left = f1(left, mid - 1, nums); + root.right = f1(mid + 1, right, nums); + return root; +} diff --git a/top-interview-leetcode150/divide/148排序列表.js b/top-interview-leetcode150/divide/148排序列表.js new file mode 100644 index 0000000..36d5504 --- /dev/null +++ b/top-interview-leetcode150/divide/148排序列表.js @@ -0,0 +1,166 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ + +function ListNode(val, next) { + this.val = (val === undefined ? 0 : val); + this.next = (next === undefined ? null : next); +} + +/** + * @param {ListNode} head + * @return {ListNode} + */ +const sortList = function (head) { + +}; + +/* +利用归并排序的思想,将链表分成两半,分别对两半进行排序,然后合并两个已排序的链表。 +*/ +function f1(head) { + if (!head || !head.next) return head; + let tail = head; + while (tail.next) { + tail = tail.next; + } + + return merge(head, tail); +} + +/* +将一个链表归并成一个有序列表,具体过程可以参考归并排序,不好描述,但不是很难 +*/ +function merge(head, tail) { + if (head === tail) return head; + + // 快慢指针计算中间节点 + let low = head; + let fast = head; + + while (fast.next && fast.next.next) { + low = low.next; + fast = fast.next.next; + } + + const mid = low; + // 将链表从中间截断 + const head2 = mid.next; + mid.next = null; + + // 将截断后的两个链表继续进行merge操作,之后将其合并成一个有序的链表返回 + return mergeList(merge(head, mid), merge(head2, tail)); +} + +/* + 合并两个有序链表 + */ +function mergeList(l1, l2) { + // 至少一个为空的情况 + if (!l1 || !l2) { + // 返回其中任意一个不为空的情况 + return l1 || l2; + } + + // 两个链表都不为空,将两个链表中的所有节点按大小拼接到dummy上,最后返回dummy.next + const dummy = new ListNode(0); + let cur = dummy; + while (l1 && l2) { + if (l1.val <= l2.val) { + cur.next = l1; + l1 = l1.next; + } else { + cur.next = l2; + l2 = l2.next; + } + cur = cur.next; + } + // 当一个链表处理完毕,还会右一个列表存在节点,将它拼接到cur.next上 + cur.next = l1 === null ? l2 : l1; + return dummy.next; +} + +/* +优化上面的写法,思路一致,但是只传入一个参数head,表示一个链表的头节点 +*/ +function f2(head) { + // 如果链表为空,或者只有一个节点,直接返回head + if (head === null || head.next === null) { + return head; + } + + // 通过快慢指针寻找中间节点,将链表分成左右两部分 + let slow = head; + let fast = head; + let prev = null; + while (fast && fast.next) { + prev = slow; + slow = slow.next; + fast = fast.next.next; + } + + // 把链表从中间断开 + prev.next = null; + + const left = f2(head); + const right = f2(slow); + return mergeList(left, right); +} + +/* +利用快速排序的思想,将链表分成三部分,left, pivot, right,left表示小于pivot的部分,right表示大于pivot的部分,最后将它们拼接起来。 +*/ +function quickSortList(head) { + if (!head || !head.next) return head; + + const pivot = head; + const leftDummy = new ListNode(0); + const rightDummy = new ListNode(0); + let leftCur = leftDummy; + let rightCur = rightDummy; + let cur = head.next; + + // 分组:< pivot 到 left,≥ pivot 到 right + while (cur) { + if (cur.val < pivot.val) { + leftCur.next = cur; + leftCur = leftCur.next; + } else { + rightCur.next = cur; + rightCur = rightCur.next; + } + cur = cur.next; + } + + // 截断,避免串联成环 + leftCur.next = null; + rightCur.next = null; + pivot.next = null; // 断开 pivot 和旧链表的联系 + + const leftSorted = quickSortList(leftDummy.next); + const rightSorted = quickSortList(rightDummy.next); + + // 拼接三段:leftSorted + pivot + rightSorted + return concat(leftSorted, pivot, rightSorted); +} + +// 拼接:将 left + pivot + right 接成一个链表并返回头结点 +function concat(left, pivot, right) { + let head = pivot; + + if (left) { + head = left; + let tail = left; + while (tail.next) { + tail = tail.next; + } + tail.next = pivot; + } + + pivot.next = right; + return head; +} diff --git a/top-interview-leetcode150/divide/23合并k个升序列表.js b/top-interview-leetcode150/divide/23合并k个升序列表.js new file mode 100644 index 0000000..4b4d3c3 --- /dev/null +++ b/top-interview-leetcode150/divide/23合并k个升序列表.js @@ -0,0 +1,112 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode[]} lists + * @return {ListNode} + */ +const mergeKLists = function (lists) { + +}; + +function ListNode(val, next) { + this.val = (val === undefined ? 0 : val); + this.next = (next === undefined ? null : next); +} + +/* +定义一个minIndex变量来记录当前最小头节点的索引。 +在每次循环中,遍历lists数组,找到当前最小头节点的索引minIndex。 +如果找到的minIndex为-1,说明没有更多的节点可以合并,跳出循环。 +如果找到的minIndex不为-1,将当前最小头节点添加到合并后的链表中。 +然后将lists[minIndex]指向下一个节点,继续下一轮循环。 +*/ + +/** + * 合并k个升序链表 + * @param {ListNode[]} lists + * @return {ListNode} + */ +function f1(lists) { + const dummy = new ListNode(0); + let current = dummy; + + // 遍历lists,找到最小的头节点 + while (true) { + let minIndex = -1; + + for (let i = 0; i < lists.length; i++) { + if (lists[i] && (minIndex === -1 || lists[i].val < lists[minIndex].val)) { + minIndex = i; // 更新最小头节点的索引 + } + } + + if (minIndex === -1) break; // 没有找到最小的头节点 + + current.next = lists[minIndex]; // 拼接到dummy节点上 + current = current.next; // 移动到下一个节点 + lists[minIndex] = lists[minIndex].next; // 移动到下一个头节点 + } + return dummy.next; // 返回合并后的链表 +} + +/* +定义一个ans表示要返回的链表,然后将lists中的链表挨个和它合并,最终返回ans + */ +function f2(lists) { + let ans = null; + for (let i = 0; i < lists.length; i++) { + ans = mergeLists(ans, lists[i]); + } + return ans; +} + +/* +使用分治法 + */ +function f3(lists) { + return merge(lists, 0, lists.length - 1); +} + +/** +* @param {ListNode[]} lists 链表数组 +* @param {number} l 要进行合并的左边界 +* @param {number} r 要进行合并的右边界 +* merge函数会将l到r范围的链表合并成一个新链表 + */ +function merge(lists, l, r) { + // 如果l和r相等,表明这个范围只有一个链表,直接返回 + if (l === r) return lists[l]; + if (l > r) return null; + // 将l到r的范围拆分成左右两部分,等这两部分merge之后再合并它们 + const mid = Math.floor((l + r) / 2); + + return mergeLists(merge(lists, l, mid), merge(lists, mid + 1, r)); +} + +function mergeLists(l1, l2) { + if (l1 === null || l2 === null) { + return l1 === null ? l2 : l1; + } + + const dummy = new ListNode(0); + let cur = dummy; + + while (l1 && l2) { + if (l1.val <= l2.val) { + cur.next = l1; + l1 = l1.next; + } else { + cur.next = l2; + l2 = l2.next; + } + cur = cur.next; // 移动到下一个节点 + } + + cur.next = l1 === null ? l2 : l1; // 将剩余的节点拼接到链表后面 + return dummy.next; // 返回合并后的链表 +} diff --git a/top-interview-leetcode150/divide/427构建四叉树.js b/top-interview-leetcode150/divide/427构建四叉树.js new file mode 100644 index 0000000..e4be595 --- /dev/null +++ b/top-interview-leetcode150/divide/427构建四叉树.js @@ -0,0 +1,93 @@ +/* eslint-disable max-len */ +// eslint-disable-next-line no-underscore-dangle +function _Node(val, isLeaf, topLeft, topRight, bottomLeft, bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; +} + +/** + * @param {number[][]} grid + * @return {_Node} + */ +const construct = function (grid) { + +}; + +/* +利用分治算法,我们很容易发现当啊n=1时grid一定可以构建成一个叶子节点,val=当前这个位置表示的值,当n=2时也即是由四个n=1的grid组成的, +我们把它们划分好,然后查看这左上,有伤,左下,右下的这四个四叉树节点的值是否都是叶子节点,如果都是再判断它们的值是否相等,如果是叶子节点 +并且它们的值也相等,就把他们合成一个更大的叶子节点,否则返回这个有叶子节点的四叉树 +*/ +function f1(grid) { + const n = grid.length; + return buildNode(grid, 0, n - 1, 0, n - 1); +} + +/** + * + * @param {*} grid + * @param {*} start + * @param {*} end + */ +function buildNode(grid, rStart, rEnd, cStart, cEnd) { + // 如果划分的grid只有一个值,那么这个值就是四叉树的叶子节点 + if (rStart === rEnd) { + return new _Node(grid[rStart][cStart], 1); + } + + // 划分四个区域构建子节点 + const topLeft = buildNode(grid, rStart, rStart + Math.floor((rEnd - rStart) / 2), cStart, cStart + Math.floor((cEnd - cStart) / 2)); + const topRight = buildNode(grid, rStart, rStart + Math.floor((rEnd - rStart) / 2), cStart + Math.floor((cEnd - cStart) / 2) + 1, cEnd); + const bottomLeft = buildNode(grid, rStart + Math.floor((rEnd - rStart) / 2) + 1, rEnd, cStart, cStart + Math.floor((cEnd - cStart) / 2)); + const bottomRight = buildNode(grid, rStart + Math.floor((rEnd - rStart) / 2) + 1, rEnd, cStart + Math.floor((cEnd - cStart) / 2) + 1, cEnd); + + // 如果四个节点都是叶子节点,并且值相同将它们合并成一个更大的叶子节点 + if (topLeft.isLeaf && topRight.isLeaf && bottomLeft.isLeaf && bottomRight.isLeaf && topLeft.val === topRight.val && topRight.val === bottomLeft.val && bottomLeft.val === bottomRight.val) { + return new _Node(topLeft.val, 1); + } + + // 构建一个包含四个节点的非叶子节点 + return new _Node(1, 0, topLeft, topRight, bottomLeft, bottomRight); +} + +/* +优化分治思路 +*/ +function buildNodeBest(grid) { + const n = grid.length; + + function dfs(r0, c0, size) { + // 判断是否整个区域都是同一个值 + const val = grid[r0][c0]; + let same = true; + for (let i = r0; i < r0 + size; i++) { + for (let j = c0; j < c0 + size; j++) { + if (grid[i][j] !== val) { + same = false; + break; + } + } + if (!same) break; + } + + if (same) { + return new Node(val === 1, true, null, null, null, null); + } + + const half = size / 2; + return new Node( + true, + false, + dfs(r0, c0, half), // topLeft + dfs(r0, c0 + half, half), // topRight + dfs(r0 + half, c0, half), // bottomLeft + dfs(r0 + half, c0 + half, half), // bottomRight + ); + } + + return dfs(0, 0, n); +}