feat: 分治算法23,10,148,427
This commit is contained in:
parent
7eb11b0136
commit
0a41e81e58
38
top-interview-leetcode150/divide/108将有序数组转换为二叉树搜索树.js
Normal file
38
top-interview-leetcode150/divide/108将有序数组转换为二叉树搜索树.js
Normal file
@ -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;
|
||||||
|
}
|
166
top-interview-leetcode150/divide/148排序列表.js
Normal file
166
top-interview-leetcode150/divide/148排序列表.js
Normal file
@ -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;
|
||||||
|
}
|
112
top-interview-leetcode150/divide/23合并k个升序列表.js
Normal file
112
top-interview-leetcode150/divide/23合并k个升序列表.js
Normal file
@ -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; // 返回合并后的链表
|
||||||
|
}
|
93
top-interview-leetcode150/divide/427构建四叉树.js
Normal file
93
top-interview-leetcode150/divide/427构建四叉树.js
Normal file
@ -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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user