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