feat: 分治算法23,10,148,427

This commit is contained in:
LouisFonda 2025-06-07 16:08:53 +08:00
parent 7eb11b0136
commit 0a41e81e58
Signed by: yigencong
GPG Key ID: 29CE877CED00E966
4 changed files with 409 additions and 0 deletions

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

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

View 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; // 返回合并后的链表
}

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