Compare commits
No commits in common. "c2c64470b09c7b8d886d86ee1cf07cb981aa8d38" and "72ac2258de9b78a75d42eb3742972711db78e9a9" have entirely different histories.
c2c64470b0
...
72ac2258de
@ -1,148 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/copy-list-with-random-pointer/?envType=study-plan-v2&envId=top-interview-150
|
||||
* // Definition for a _Node.
|
||||
* function _Node(val, next, random) {
|
||||
* this.val = val;
|
||||
* this.next = next;
|
||||
* this.random = random;
|
||||
* };
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {_Node} head
|
||||
* @return {_Node}
|
||||
*/
|
||||
const copyRandomList = function (head) {
|
||||
|
||||
};
|
||||
|
||||
// Definition for a _Node.
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
function _Node(val, next, random) {
|
||||
this.val = val;
|
||||
this.next = next;
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
/*
|
||||
遍历整个链表,创建每一个节点的克隆节点,并且使用map将它们直接联系起来,原节点作为key,新节点作为value,
|
||||
遍历完毕之后,再遍历一一遍原链表,根据random_index和map来为可能列表添加random_index
|
||||
*/
|
||||
function f1(head) {
|
||||
const map = new Map(); // 储存原链表和克隆链表直接的关系
|
||||
const dummyHead = new _Node(); // 哑节点,用于构建克隆链表
|
||||
let curClone = dummyHead;
|
||||
let cur = head;
|
||||
|
||||
while (cur) {
|
||||
// 创建克隆节点
|
||||
curClone.next = new _Node(cur.val);
|
||||
curClone = curClone.next;
|
||||
// 将原节点和克隆节点映射
|
||||
map.set(cur, curClone);
|
||||
cur = cur.next;
|
||||
}
|
||||
|
||||
// 遍历原节点,根据原节点的信息为克隆节点添加random指针
|
||||
cur = head;
|
||||
curClone = dummyHead.next;
|
||||
while (cur) {
|
||||
if (cur.random) {
|
||||
curClone.random = map.get(cur.random);
|
||||
}
|
||||
cur = cur.next;
|
||||
curClone = curClone.next;
|
||||
}
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/*
|
||||
f1 的优化版本,首先创建克隆节点和映射,之后再拼接克隆链表
|
||||
*/
|
||||
function f2(head) {
|
||||
if (!head) return null;
|
||||
|
||||
const map = new Map(); // 储存原节点 => 克隆节点的映射
|
||||
let cur = head;
|
||||
|
||||
// 第一次遍历:创建所有新节点,并放入 map 中
|
||||
while (cur) {
|
||||
map.set(cur, new _Node(cur.val));
|
||||
cur = cur.next;
|
||||
}
|
||||
|
||||
cur = head;
|
||||
|
||||
// 第二次遍历:设置新节点的 next 和 random 指针
|
||||
while (cur) {
|
||||
const cloneNode = map.get(cur);
|
||||
cloneNode.next = map.get(cur.next) || null;
|
||||
cloneNode.random = map.get(cur.random) || null;
|
||||
cur = cur.next;
|
||||
}
|
||||
|
||||
return map.get(head);
|
||||
}
|
||||
|
||||
/*
|
||||
直接再原节点中插入克隆节点,那么原节点和克隆节点就存在了引用关系,之后根据引用关系来设置random,最后
|
||||
分离原链表和克隆链表即可
|
||||
*/
|
||||
|
||||
function f3(head) {
|
||||
if (!head) return null;
|
||||
|
||||
// 创建克隆节点,并且把克隆节点放在原节点后面
|
||||
let cur = head;
|
||||
while (cur) {
|
||||
// 将原节点之后的所有节点移动到克隆节点的后面,再将克隆节点作为原节点的next
|
||||
const clone = new _Node(cur.val);
|
||||
clone.next = cur.next;
|
||||
cur.next = clone;
|
||||
cur = clone.next; // 遍历下一个原节点
|
||||
}
|
||||
|
||||
// 更具原节点的random来设置克隆节点的random
|
||||
|
||||
cur = head;
|
||||
while (cur) {
|
||||
if (cur.random) {
|
||||
cur.next.random = cur.random.next;
|
||||
}
|
||||
cur = cur.next.next; // 下一个原节点
|
||||
}
|
||||
|
||||
// 分离节点
|
||||
cur = head;
|
||||
const cloneHead = head.next;
|
||||
while (cur) {
|
||||
const clone = cur.next;
|
||||
cur.next = clone.next; // 原节点指向原本下一个节点
|
||||
if (clone.next) {
|
||||
clone.next = clone.next.next; // 克隆节点指向克隆节点的下一个节点
|
||||
}
|
||||
cur = cur.next;
|
||||
}
|
||||
return cloneHead;
|
||||
}
|
||||
|
||||
/*
|
||||
回溯加哈希表
|
||||
*/
|
||||
|
||||
function f4(head, cacheMap = new Map()) {
|
||||
if (!head) return null;
|
||||
|
||||
// 如果没有这个节点的克隆映射
|
||||
if (!cacheMap.has(head)) {
|
||||
const clone = new _Node(head.val);
|
||||
cacheMap.set(head, clone);
|
||||
// 克隆节点的下一个节点为当前节点的下一个节点的克隆节点,递归调用即可
|
||||
clone.next = f4(head.next, cacheMap);
|
||||
// random同理
|
||||
clone.random = f4(head.random, cacheMap);
|
||||
}
|
||||
|
||||
// 如果有克隆节点,直接返回
|
||||
return cacheMap.get(head);
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/linked-list-cycle/?envType=study-plan-v2&envId=top-interview-150
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val) {
|
||||
* this.val = val;
|
||||
* this.next = null;
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {ListNode} head
|
||||
* @return {boolean}
|
||||
*/
|
||||
const hasCycle = function (head) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
利用set来检测在遍历的过程中是否发生了重复,如果发生了重复说明有环,如果没有表示无环
|
||||
*/
|
||||
|
||||
function f1(head) {
|
||||
const seenNodes = new Set();
|
||||
|
||||
let curNote = head;
|
||||
|
||||
while (curNote !== null) {
|
||||
if (seenNodes.has(curNote)) return true; // 之前遍历出现过,直接判定有环
|
||||
seenNodes.add(curNote);
|
||||
curNote = curNote.next;
|
||||
}
|
||||
return false; // 遍历了整个链表也没有发生重复,表示无环
|
||||
}
|
||||
|
||||
/*
|
||||
通过快慢指针来判断是否有环
|
||||
*/
|
||||
|
||||
function f2(head) {
|
||||
let slow = head;
|
||||
let fast = head;
|
||||
|
||||
while (fast !== null && fast.next !== null) {
|
||||
slow = slow.next;
|
||||
fast = fast.next.next;
|
||||
if (slow === fast) return true; // 如果快慢指针相遇,表示有环
|
||||
}
|
||||
return false; // 如果快指针走到了链表末尾,表示没有环
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @param {ListNode} head
|
||||
* @param {number} n
|
||||
* @return {ListNode}
|
||||
*/
|
||||
const removeNthFromEnd = function (head, n) {
|
||||
|
||||
};
|
||||
|
||||
function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
||||
|
||||
/*
|
||||
思路:首先统计列表的长度,根据长度信息找到倒数第n个节点的前一个节点,把它删掉即可
|
||||
*/
|
||||
function f1(head, n) {
|
||||
// 1.获取列表的长度
|
||||
const len = getListLength(head);
|
||||
// 2.创建哑节点,减少处理逻辑
|
||||
const dummyHead = new ListNode(-1);
|
||||
dummyHead.next = head;
|
||||
// 3.找到要删除节点的前一个节点
|
||||
let pre = dummyHead;
|
||||
for (let i = 0; i < len - n; i++) {
|
||||
pre = pre.next;
|
||||
}
|
||||
// 4. 删除这个节点
|
||||
pre.next = pre.next.next;
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取链表的节点个数
|
||||
* @param {ListNode} head - 链表的头节点
|
||||
* @return {number} - 链表的节点个数
|
||||
*/
|
||||
const getListLength = function (head) {
|
||||
let count = 0; // 初始化计数器
|
||||
let current = head; // 初始化当前节点为链表头
|
||||
|
||||
// 遍历整个链表,直到链表末尾
|
||||
while (current !== null) {
|
||||
count++; // 每遍历一个节点,计数器加一
|
||||
current = current.next; // 移动到下一个节点
|
||||
}
|
||||
|
||||
return count; // 返回链表的长度
|
||||
};
|
||||
|
||||
/*
|
||||
思路:只需要找到要去除节点的前一个节点就能去掉这个节点,所以核心问题就变成了
|
||||
如何寻找这个节点,直接利用快慢指针,首先初始化两个指针在同一位置,然后让快指针
|
||||
往后走n步,之后一同走,直到快指针到达最后一个节点,此时的慢指针指向的就是要删除
|
||||
节点的前一个节点。
|
||||
*/
|
||||
|
||||
function f2(head, n) {
|
||||
const dummyHead = new ListNode(-1); // 建立哑节点,便于处理head
|
||||
dummyHead.next = head;
|
||||
let slow = dummyHead;
|
||||
let fast = dummyHead;
|
||||
|
||||
// 1.快指针先走n+1步(夺走一部是因为从哑节点开始走)
|
||||
for (let i = 0; i <= n; i++) {
|
||||
fast = fast.next;
|
||||
}
|
||||
|
||||
// 2.两个指针同时往后走,如果fast已经是最后一个节点,
|
||||
while (fast) {
|
||||
slow = slow.next;
|
||||
fast = fast.next;
|
||||
}
|
||||
|
||||
// 3.删除slow后面的那个节点
|
||||
slow.next = slow.next.next;
|
||||
|
||||
// 4.返回处理后的链表
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/*
|
||||
利用栈,f1中我们统计了链表的长度,之后根据长度和n来找到要删除节点的前一个节点,这样实际上遍历
|
||||
了两遍,可以直接将所有节点按照顺序压入栈中,之后弹出栈顶的n个元素,剩下的栈顶元素就是我们要操作
|
||||
的pre节点
|
||||
*/
|
||||
|
||||
function f3(head, n) {
|
||||
const dummyHead = new ListNode(-1); // 建立哑节点,便于处理head
|
||||
dummyHead.next = head;
|
||||
|
||||
// 1.将所有节点压入栈中
|
||||
const stack = [];
|
||||
let cur = dummyHead;
|
||||
while (cur) {
|
||||
stack.push(cur);
|
||||
cur = cur.next;
|
||||
}
|
||||
|
||||
// 2.弹出栈顶的n个元素
|
||||
for (let i = 0; i < n; i++) {
|
||||
stack.pop();
|
||||
}
|
||||
// 3.操作栈顶元素,这个元素就是要删除元素的前一个节点
|
||||
const prev = stack[stack.length - 1];
|
||||
prev.next = prev.next.next;
|
||||
|
||||
// 4.返回修改后的链表
|
||||
return dummyHead.next;
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @param {ListNode} list1
|
||||
* @param {ListNode} list2
|
||||
* @return {ListNode}
|
||||
*/
|
||||
const mergeTwoLists = function (list1, list2) {
|
||||
|
||||
};
|
||||
|
||||
function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
||||
|
||||
/*
|
||||
遍历两个链表,如果l1的值小于l2的值,就创建一个新的节点,把l1的值放入这个新节点中,之后把这个新节点
|
||||
拼接到l3中,之后返回代表l3的头节点即可
|
||||
*/
|
||||
function f1(list1, list2) {
|
||||
const dummyHead = new ListNode(0); // 哑节点,用于拼接链表
|
||||
let current = dummyHead;
|
||||
|
||||
while (list1 !== null || list2 !== null) {
|
||||
if (list1 && list2) {
|
||||
if (list1.val <= list2.val) {
|
||||
current.next = new ListNode(list1.val);
|
||||
list1 = list1.next;
|
||||
} else {
|
||||
current.next = new ListNode(list2.val);
|
||||
list2 = list2.next;
|
||||
}
|
||||
current = current.next;
|
||||
} else if (list1 === null) { // 当list1为空时直接将list2剩余部分拼接即可
|
||||
current.next = list2;
|
||||
break;
|
||||
} else if (list2 === null) { // 当list2为空时直接将list1剩余部分拼接即可
|
||||
current.next = list1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/*
|
||||
思路和上面的一致,上面的效率之所以慢是因为花费了大量的时间在创建新节点上,这个操作虽然直观,但是
|
||||
显得多余。
|
||||
*/
|
||||
function f2(list1, list2) {
|
||||
const dummyHead = new ListNode(0); // 哑节点,用于拼接链表
|
||||
let current = dummyHead;
|
||||
|
||||
while (list1 !== null && list2 !== null) {
|
||||
if (list1.val <= list2.val) {
|
||||
current.next = list1;
|
||||
list1 = list1.next;
|
||||
} else {
|
||||
current.next = list2;
|
||||
list2 = list2.next;
|
||||
}
|
||||
current = current.next;
|
||||
}
|
||||
|
||||
// 如果 list1 或者 list2 还有剩余节点,直接拼接到结果链表
|
||||
if (list1 !== null) {
|
||||
current.next = list1;
|
||||
} else if (list2 !== null) {
|
||||
current.next = list2;
|
||||
}
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/*
|
||||
利用递归,思考递归过程,首先思考f3的作用,f3的作用非常单一,就是合并两个有序链表,合并完成之后,使其
|
||||
仍然有序,假设list1[0]比list2[0]小,那么list1[0]一定是合并后链表的头节点,我们只需把list1[1:]和list2
|
||||
继续做交给f3,把合并后的头节点作为list1[0]的next,不就完成整个递归过程了嘛!
|
||||
*/
|
||||
function f3(l1, l2) {
|
||||
if (l1 === null) {
|
||||
return l2; // 如果 l1 空了,返回 l2
|
||||
} if (l2 === null) {
|
||||
return l1; // 如果 l2 空了,返回 l1
|
||||
} if (l1.val < l2.val) {
|
||||
// 如果 l1 的值小于 l2,则 l1 当前节点连接到后面的结果
|
||||
l1.next = mergeTwoLists(l1.next, l2);
|
||||
return l1; // 返回 l1,表示 l1 的节点被连接到了合并后的链表中
|
||||
}
|
||||
// 否则,l2 当前节点连接到后面的结果
|
||||
l2.next = mergeTwoLists(l1, l2.next);
|
||||
return l2; // 返回 l2,表示 l2 的节点被连接到了合并后的链表中
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @param {ListNode} head
|
||||
* @param {number} k
|
||||
* @return {ListNode}
|
||||
*/
|
||||
const reverseKGroup = function (head, k) {
|
||||
|
||||
};
|
||||
|
||||
function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
||||
|
||||
/*
|
||||
这个问题可以拆解成n个leetcode的子问题,直接利用k把区间划分出来,调用reverseBetween即可
|
||||
*/
|
||||
function f1(head, k) {
|
||||
// 利用哑节点来处理头节点问题
|
||||
const dummyHead = new ListNode(-1);
|
||||
dummyHead.next = head;
|
||||
let left = 2;
|
||||
let right = left + k - 1;
|
||||
const n = getListLength(dummyHead);
|
||||
while (right <= n) {
|
||||
reverseBetween(, left, right);
|
||||
left = right + 1;
|
||||
right = left + k - 1;
|
||||
}
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} head 头节点
|
||||
* @param {*} left 要反转的区域的第一个节点
|
||||
* @param {*} right 要反转的区域的最后一个节点
|
||||
* @returns 返回区域反转后的节点
|
||||
*/
|
||||
const reverseBetween = function (head, left, right) {
|
||||
const dummyHead = new ListNode(-1); // 创建一个哑节点,简化边界情况
|
||||
dummyHead.next = head; // 哑节点的next指向链表头
|
||||
|
||||
let pre = dummyHead; // `pre` 用于指向 `left` 前一个节点
|
||||
// 寻找 `left` 的前一个节点
|
||||
for (let i = 0; i < left - 1; i++) {
|
||||
pre = pre.next;
|
||||
}
|
||||
|
||||
const cur = pre.next; // `cur` 指向要反转部分的第一个节点
|
||||
// 从 `left` 到 `right` 反转链表部分
|
||||
for (let i = 0; i < right - left; i++) {
|
||||
const next = cur.next; // `next` 指向当前节点的下一个节点
|
||||
cur.next = next.next; // 跳过 `next` 节点
|
||||
next.next = pre.next; // 将 `next` 插入到 `cur` 前面
|
||||
pre.next = next; // 更新 `pre.next` 为 `next`
|
||||
}
|
||||
|
||||
return dummyHead.next; // 返回反转后的链表
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取链表的节点个数
|
||||
* @param {ListNode} head - 链表的头节点
|
||||
* @return {number} - 链表的节点个数
|
||||
*/
|
||||
let getListLength = function (head) {
|
||||
let count = 0; // 初始化计数器
|
||||
let current = head; // 初始化当前节点为链表头
|
||||
|
||||
// 遍历整个链表,直到链表末尾
|
||||
while (current !== null) {
|
||||
count++; // 每遍历一个节点,计数器加一
|
||||
current = current.next; // 移动到下一个节点
|
||||
}
|
||||
|
||||
return count; // 返回链表的长度
|
||||
};
|
@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @param {ListNode} l1
|
||||
* @param {ListNode} l2
|
||||
* @return {ListNode}
|
||||
*/
|
||||
const addTwoNumbers = function (l1, l2) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
直接遍历两个链表,列表的头节点代表的是各位,后一个节点代表的是十位,以此类推,所以只需定义一个
|
||||
变量表示进位即可,把计算好的结果拼接成l3即可
|
||||
*/
|
||||
|
||||
function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
||||
|
||||
function f1(l1, l2) {
|
||||
let carry = 0; // 进位
|
||||
const dummyHead = new ListNode(0); // 哑节点,用于返回结果
|
||||
let current = dummyHead; // 当前节点,用于构建求和之和的链表
|
||||
|
||||
// 遍历两个链表,直到,链表都为空,并且没有进位(只要有节点或者进位不等于0就需要创建新的求和节点)
|
||||
|
||||
while (l1 !== null || l2 !== null || carry !== 0) {
|
||||
let sum = carry;
|
||||
|
||||
if (l1) {
|
||||
sum += l1.val;
|
||||
l1 = l1.next;
|
||||
}
|
||||
|
||||
if (l2) {
|
||||
sum += l2.val;
|
||||
l2 = l2.next;
|
||||
}
|
||||
|
||||
// 根据sum,计算出current节点的值和进位值
|
||||
current.next = new ListNode(sum % 10);
|
||||
current = current.next;
|
||||
carry = Math.floor(sum / 10);
|
||||
}
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/*
|
||||
将链表转换成字符串数字,之后把字符串数字相加,最后处理成一个新的列表
|
||||
*/
|
||||
function f2(l1, l2) {
|
||||
let numStr1 = '';
|
||||
let numStr2 = '';
|
||||
|
||||
// 将第一个链表反转并拼接为字符串
|
||||
let cur1 = l1;
|
||||
while (cur1 !== null) {
|
||||
numStr1 = cur1.val + numStr1;
|
||||
cur1 = cur1.next;
|
||||
}
|
||||
|
||||
// 将第二个链表反转并拼接为字符串
|
||||
let cur2 = l2;
|
||||
while (cur2 !== null) {
|
||||
numStr2 = cur2.val + numStr2;
|
||||
cur2 = cur2.next;
|
||||
}
|
||||
|
||||
// 将拼接的字符串转为数字并进行加法运算
|
||||
const sum = BigInt(numStr1) + BigInt(numStr2); // 使用 BigInt 处理大数字
|
||||
|
||||
// 将求和结果转换为字符串
|
||||
const sumStr = sum.toString();
|
||||
|
||||
// 将求和结果转换为链表
|
||||
const dummyHead = new ListNode(0);
|
||||
let current = dummyHead;
|
||||
|
||||
for (let i = sumStr.length - 1; i >= 0; i--) {
|
||||
current.next = new ListNode(Number(sumStr[i]));
|
||||
current = current.next;
|
||||
}
|
||||
|
||||
return dummyHead.next; // 返回结果链表的头节点
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* https://leetcode.cn/problems/rotate-list/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {ListNode} head
|
||||
* @param {number} k
|
||||
* @return {ListNode}
|
||||
*/
|
||||
/*
|
||||
思路:观察发现,旋转1,就等于将链表首位相连,从原头节点出发,走 n-2 步,之后断开这个节点后后面
|
||||
的节点连接即可,例如1->2->3,roate1就是3->1->2,来一个长一点的例子1->2->3->4->5->6,我们假设
|
||||
要roate4,想象6->1是首位相连的,从原头节点1开始往后走n-4-1也就是1步,到达2的位置,这个时候记录
|
||||
2.next作为返回的头节点,并且把它断开,就变成了3->4->5->5->1->2,这就是我们要的结果
|
||||
*/
|
||||
|
||||
const rotateRight = function (head, k) {
|
||||
|
||||
};
|
||||
|
||||
function f1(head, k) {
|
||||
if (head === null || head.next === null) return head; // 没有节点,或者只有一个节点,直接返回
|
||||
// 1.遍历链表获取尾节点,和节点个数
|
||||
let tail = head;
|
||||
let len = 1;
|
||||
while (tail.next) {
|
||||
len++;
|
||||
tail = tail.next;
|
||||
}
|
||||
|
||||
// 2.把链表首尾相连
|
||||
tail.next = head;
|
||||
|
||||
// 3.从head往后走n-k-1步,找到要断开的位置
|
||||
tail = head;
|
||||
k %= len; // 当k比链表长度还长时需取模操作,比如len=3,要旋转4,那么效果和旋转1是一样的
|
||||
for (let i = 0, setp = len - k - 1; i < setp; i++) {
|
||||
tail = tail.next;
|
||||
}
|
||||
// 4.保留tail的next作为新节点的头节点,然后断开
|
||||
const rHead = tail.next;
|
||||
tail.next = null;
|
||||
return rHead;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @param {ListNode} head
|
||||
* @return {ListNode}
|
||||
*/
|
||||
const deleteDuplicates = function (head) {
|
||||
|
||||
};
|
||||
|
||||
function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
||||
|
||||
/*
|
||||
思路:先建立哑节点,用于处理头部重复的情况,用cur来遍历整个链表,如果cur.next和cur.next.next 的值
|
||||
相等,那么表示从cur.next开始发生了重复,我们记录这个val,从cur.next开始删除所有值等于val的节点;如果
|
||||
cur.next和cur.next.next不相等,那么就没有重复,将cur移动到下一个节点cur = cur.next
|
||||
*/
|
||||
|
||||
function f1(head) {
|
||||
if (!head) {
|
||||
return head;
|
||||
}
|
||||
const dummyHead = new ListNode(-1);
|
||||
dummyHead.next = head;
|
||||
let cur = dummyHead;
|
||||
let curVal; // 保存重复节点的val
|
||||
// 从哑节点开始,查看next和next.next是否相等
|
||||
while (cur.next && cur.next.next) {
|
||||
if (cur.next.val === cur.next.next.val) {
|
||||
curVal = cur.next.val;
|
||||
// 检测cur.next的值是否等于curVal,如果等于就删除这个节点
|
||||
while (cur.next && cur.next.val === curVal) {
|
||||
cur.next = cur.next.next;
|
||||
}
|
||||
} else {
|
||||
cur = cur.next;
|
||||
}
|
||||
}
|
||||
return dummyHead.next;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* @param {ListNode} head
|
||||
* @param {number} x
|
||||
* @return {ListNode}
|
||||
*/
|
||||
import { ListNode } from './linked-list-tools';
|
||||
|
||||
const partition = function (head, x) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
思路:遍历整个链表,把小于x的值连接到一个叫smallHead的哑节点上,把大于等于x的值连接到一个叫largeHead的哑节点上
|
||||
之后把smallHead整个链表,和largeHead整个链表连接起来就行了
|
||||
*/
|
||||
function f1(head, x) {
|
||||
let small = new ListNode(0);
|
||||
let large = new ListNode(0);
|
||||
const smallHead = small;
|
||||
const largeHead = large;
|
||||
|
||||
// 遍历整个head表示的列表
|
||||
while (head) {
|
||||
if (head.val < x) {
|
||||
small.next = head;
|
||||
small = small.next;
|
||||
} else {
|
||||
large.next = head;
|
||||
large = large.next;
|
||||
}
|
||||
head = head.next;
|
||||
}
|
||||
|
||||
// 连接两个链表
|
||||
small.next = largeHead.next;
|
||||
large.next = null; // 思考1->2->3->1, x=2 这种情况 samllHead:1->1, largeHead:2->3->1,这个时候3->1是不应该存在的
|
||||
|
||||
return smallHead.next;
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* function ListNode(val, next) {
|
||||
* this.val = (val===undefined ? 0 : val)
|
||||
* this.next = (next===undefined ? null : next)
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* https://leetcode.cn/problems/reverse-linked-list-ii/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {ListNode} head
|
||||
* @param {number} left
|
||||
* @param {number} right
|
||||
* @return {ListNode}
|
||||
*/
|
||||
const reverseBetween = function (head, left, right) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
思路: 首先找到left指向的节点和right指向的节点,之后把left的前一个节点和right的后一个节点保存起来
|
||||
,之后反转left到right的所有节点,再把原先得前后两部分拼接上即可。
|
||||
*/
|
||||
|
||||
function f1(head, left, right) {
|
||||
if (left === right) return head; // 无需反转
|
||||
let cur = head;
|
||||
let i = 1;
|
||||
let pre = null;
|
||||
let leftNode = null;
|
||||
let rightNode = null;
|
||||
let next = null;
|
||||
while (cur) {
|
||||
if (i === left - 1) { // 寻找left的前一个节点
|
||||
pre = cur;
|
||||
} else if (i === left) { // 寻找left节点
|
||||
leftNode = cur;
|
||||
} else if (i === right) { // 寻找right 节点
|
||||
rightNode = cur;
|
||||
next = cur.next;
|
||||
rightNode.next = null; // 将链表截断
|
||||
break; // 退出循环
|
||||
}
|
||||
cur = cur.next;
|
||||
i++;
|
||||
}
|
||||
|
||||
// 从left到right开始反转
|
||||
const rHead = reverseLink(leftNode); // rHead和RightNode指向了同一个节点
|
||||
if (pre) {
|
||||
pre.next = rHead;
|
||||
} else {
|
||||
head = rightNode;
|
||||
}
|
||||
leftNode.next = next;
|
||||
return head;
|
||||
}
|
||||
|
||||
/*
|
||||
反转一个链表,使用递归
|
||||
*/
|
||||
function reverseLink(head) {
|
||||
// 基本情况:如果链表为空或只有一个节点,直接返回
|
||||
if (!head || !head.next) return head;
|
||||
|
||||
// 递归反转后续链表
|
||||
const rHead = reverseLink(head.next);
|
||||
|
||||
// 当前节点的下一个节点的 next 指向当前节点,反转链表
|
||||
head.next.next = head;
|
||||
|
||||
// 断开当前节点与下一个节点的连接,防止形成环
|
||||
head.next = null;
|
||||
|
||||
return rHead;
|
||||
}
|
||||
|
||||
/*
|
||||
反转一个链表使用迭代
|
||||
*/
|
||||
const reverseLinkedList = (head) => {
|
||||
let pre = null;
|
||||
let cur = head;
|
||||
|
||||
while (cur) {
|
||||
const next = cur.next;
|
||||
cur.next = pre;
|
||||
pre = cur;
|
||||
cur = next;
|
||||
}
|
||||
};
|
||||
|
||||
function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
||||
|
||||
/*
|
||||
优化:思路和上面是一样的,利用哑节点来优化,减少边界判断情况
|
||||
*/
|
||||
function f2(head, left, right) {
|
||||
// left在等于1时和大于1时处理方式不同,利用哑节点来减少判断情况
|
||||
const dummyHead = new ListNode(-1);
|
||||
dummyHead.next = head;
|
||||
|
||||
// 1.寻找left节点的前一个节点pre
|
||||
// 这里直接使用for循环,从dummyHead的位置走left-1步即可
|
||||
let pre = dummyHead;
|
||||
for (let i = 0; i < left - 1; i++) {
|
||||
pre = pre.next;
|
||||
}
|
||||
|
||||
// 2.从pre的位置往后走 right - Left + 1步,即可找到right指向的节点
|
||||
let rightNode = pre;
|
||||
for (let i = 0; i < right - left + 1; i++) {
|
||||
rightNode = rightNode.next;
|
||||
}
|
||||
|
||||
// 3.将链表从left到right的位置切断
|
||||
const leftNode = pre.next;
|
||||
const surr = rightNode.next;
|
||||
pre.next = null;
|
||||
rightNode.next = null;
|
||||
|
||||
// 4.反转切断的这一部分
|
||||
reverseLinkedList(leftNode);
|
||||
|
||||
// 5.接回原来的链表中
|
||||
pre.next = rightNode;
|
||||
leftNode.next = surr;
|
||||
return dummyHead.next;
|
||||
}
|
||||
|
||||
/*
|
||||
直接在链表上面操作,把要反转的区域
|
||||
*/
|
||||
|
||||
function f3(head, left, right) {
|
||||
const dummyHead = new ListNode(-1); // 创建一个哑节点,简化边界情况
|
||||
dummyHead.next = head; // 哑节点的next指向链表头
|
||||
|
||||
let pre = dummyHead; // `pre` 用于指向 `left` 前一个节点
|
||||
// 寻找 `left` 的前一个节点
|
||||
for (let i = 0; i < left - 1; i++) {
|
||||
pre = pre.next;
|
||||
}
|
||||
|
||||
const cur = pre.next; // `cur` 指向要反转部分的第一个节点
|
||||
// 从 `left` 到 `right` 反转链表部分
|
||||
for (let i = 0; i < right - left; i++) {
|
||||
const next = cur.next; // `next` 指向当前节点的下一个节点
|
||||
cur.next = next.next; // 跳过 `next` 节点
|
||||
next.next = pre.next; // 将 `next` 插入到 `cur` 前面
|
||||
pre.next = next; // 更新 `pre.next` 为 `next`
|
||||
}
|
||||
|
||||
return dummyHead.next; // 返回反转后的链表
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* 获取链表的节点个数
|
||||
* @param {ListNode} head - 链表的头节点
|
||||
* @return {number} - 链表的节点个数
|
||||
*/
|
||||
export const getListLength = function (head) {
|
||||
let count = 0; // 初始化计数器
|
||||
let current = head; // 初始化当前节点为链表头
|
||||
|
||||
// 遍历整个链表,直到链表末尾
|
||||
while (current !== null) {
|
||||
count++; // 每遍历一个节点,计数器加一
|
||||
current = current.next; // 移动到下一个节点
|
||||
}
|
||||
|
||||
return count; // 返回链表的长度
|
||||
};
|
||||
|
||||
/**
|
||||
* 链表节点
|
||||
* @param {*} val
|
||||
* @param {*} next
|
||||
*/
|
||||
export function ListNode(val, next) {
|
||||
this.val = (val === undefined ? 0 : val);
|
||||
this.next = (next === undefined ? null : next);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/summary-ranges/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {number[]} nums
|
||||
* @return {string[]}
|
||||
*/
|
||||
const summaryRanges = function (nums) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
思路:定义两个指针,一个指针表示区间的开头,一个表示区间的结尾,用left和right表示,right扫描连续区间,如果发现不
|
||||
再连续就把left和right表示的连续区间加入到结果集
|
||||
*/
|
||||
function f1(nums) {
|
||||
const result = [];
|
||||
let left = 0;
|
||||
|
||||
while (left < nums.length) {
|
||||
let right = left;
|
||||
// 扫描连续区间
|
||||
while (right + 1 < nums.length && nums[right + 1] === nums[right] + 1) {
|
||||
right++;
|
||||
}
|
||||
|
||||
// 查看left和right是否相等,如果相等则这个区间只有一个数,否则是一个完整的区间,需要用left->right表示
|
||||
|
||||
if (left === right) {
|
||||
result.push(`${nums[left]}`);
|
||||
} else {
|
||||
result.push(`${nums[left]}->${nums[right]}`);
|
||||
}
|
||||
|
||||
// 区间统计完毕,统计下一个区间
|
||||
|
||||
left = right + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {number[][]} points
|
||||
* @return {number}
|
||||
*/
|
||||
const findMinArrowShots = function (points) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
将points按照start排序,从第一个point开始遍历,所射箭的位置取第一个point的end,之后查看第二个point,
|
||||
如果第二个point的end比第一个point的end小就把箭的x坐标移动到第二个point的end,这样就能保证之前气球
|
||||
引爆的情况下也能引爆当前气球,可以做到箭的作用的最大化,如果之前最小的end比当前point还小,则表明需要,
|
||||
新增一直箭
|
||||
*/
|
||||
|
||||
function f1(points) {
|
||||
if (points.length === 0) return 0;
|
||||
|
||||
// 按照气球的左边界进行排序
|
||||
points.sort((a, b) => a[0] - b[0]);
|
||||
|
||||
let arrows = 1; // 初始化箭的数量
|
||||
let arrowX = points[0][1]; // 第一个箭的位置是第一个气球的右边界
|
||||
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
// 如果当前气球的左边界大于箭的位置,说明需要新的箭
|
||||
if (arrowX < points[i][0]) {
|
||||
arrows++; // 增加箭
|
||||
arrowX = points[i][1]; // 更新箭的位置为当前气球的右边界
|
||||
} else {
|
||||
// 否则,箭可以继续覆盖当前气球,更新箭的位置为当前气球的右边界(确保尽量覆盖更多气球)
|
||||
arrowX = Math.min(arrowX, points[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
return arrows;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/merge-intervals/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {number[][]} intervals
|
||||
* @return {number[][]}
|
||||
*/
|
||||
const merge = function (intervals) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
思路,先对intervals按照左区间进行排序,之后遍历所有区间,如果当前区间在result中不存在就把它加入到
|
||||
reslut中,如果result中存在区间并且result的最后一个区间的右区间小于当前区间的左区间,直接把当前区间加入
|
||||
到reslut,否则合并区间,右边界取两个区间中较大的值
|
||||
*/
|
||||
function f1(intervals) {
|
||||
intervals.sort((a, b) => a[0] - b[0]); // 按照左区间从小到大排序
|
||||
const result = [];
|
||||
for (let i = 0; i < intervals.length; i++) {
|
||||
if (result.length === 0 || result[result.length - 1][1] < intervals[i][0]) {
|
||||
result.push(intervals[i]);
|
||||
} else {
|
||||
result[result.length - 1][1] = Math.max(result[result.length - 1][1], intervals[i][1]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* http://leetcode.cn/problems/insert-interval/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {number[][]} intervals
|
||||
* @param {number[]} newInterval
|
||||
* @return {number[][]}
|
||||
*/
|
||||
const insert = function (intervals, newInterval) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
假设我们插入的区间是[left, right],[start, end]表示当前元素的左右区间,由于intervals的所有区间都是
|
||||
按照左区间排序的,当end小于left时表明新插入的区间和当前区间没有重合,没必要合并,就把当前区间存入result
|
||||
如果,当start大于right时表明这两个区间没有重合也不需要合并,其他情况需要合并区间,合并的区间为[min(start,left), max(end, right)]
|
||||
*/
|
||||
|
||||
function f1(intervals, newInterval) {
|
||||
const result = [];
|
||||
let i = 0;
|
||||
const n = intervals.length;
|
||||
|
||||
// 1. 插入所有与 newInterval 不重叠的区间
|
||||
while (i < n && intervals[i][1] < newInterval[0]) {
|
||||
result.push(intervals[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
// 2. 合并重叠的区间
|
||||
while (i < n && intervals[i][0] <= newInterval[1]) {
|
||||
newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
|
||||
newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
// 3. 将合并后的 newInterval 插入到结果中
|
||||
result.push(newInterval);
|
||||
|
||||
// 4. 插入剩余的所有区间
|
||||
while (i < n) {
|
||||
result.push(intervals[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/evaluate-reverse-polish-notation/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {string[]} tokens
|
||||
* @return {number}
|
||||
*/
|
||||
const evalRPN = function (tokens) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
根据逆波兰表达式的规则,我们只需要使用一个栈来存操作数即可,遍历tokens,如果遇到的是 '+' '-' '*' '/'
|
||||
这些操作符,直接弹出两个元素right和left,之和拿left opt right,之后把计算的结果放入栈中,继续此操作
|
||||
在最后栈中只会右一个元素,这个元素就是整个波兰表达式的结果
|
||||
*/
|
||||
function f1(tokens) {
|
||||
const stack = [];
|
||||
let left; let
|
||||
right;
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i] === '+') {
|
||||
right = stack.pop();
|
||||
left = stack.pop();
|
||||
stack.push(left + right);
|
||||
} else if (tokens[i] === '-') {
|
||||
right = stack.pop();
|
||||
left = stack.pop();
|
||||
stack.push(left - right);
|
||||
} else if (tokens[i] === '*') {
|
||||
right = stack.pop();
|
||||
left = stack.pop();
|
||||
stack.push(left * right);
|
||||
} else if (tokens[i] === '/') {
|
||||
right = stack.pop();
|
||||
left = stack.pop();
|
||||
let result = left / right;
|
||||
|
||||
if (result >= 0) {
|
||||
result = Math.floor(result);
|
||||
} else {
|
||||
result = Math.ceil(result);
|
||||
}
|
||||
stack.push(result);
|
||||
} else {
|
||||
stack.push(+tokens[i]); // 转换成number
|
||||
}
|
||||
}
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
/*
|
||||
优化上面的分支,right = stack.pop();left = stack.pop(); 明显冗余,所以,先判断这个token是否是操作符
|
||||
如果不是操作符那么它一定是数字字符,就把它转换成数组压入栈中,如果是操作符,就先弹出两个操作数,计数之后把它
|
||||
压入栈中,在js中触发要特殊处理,因为js不像其他语言会把小数部分去掉
|
||||
*/
|
||||
function f2(tokens) {
|
||||
const stack = [];
|
||||
const n = tokens.length;
|
||||
for (let i = 0; i < n; i++) {
|
||||
const token = tokens[i];
|
||||
if (token === '+' || token === '-' || token === '*' || token === '/') {
|
||||
// 先弹出两个操作数
|
||||
const right = stack.pop();
|
||||
const left = stack.pop();
|
||||
// 根据具体操作符进行具体操作
|
||||
if (token === '+') {
|
||||
stack.push(left + right);
|
||||
} else if (token === '-') {
|
||||
stack.push(left - right);
|
||||
} else if (token === '*') {
|
||||
stack.push(left * right);
|
||||
} else {
|
||||
// 除法操作要特殊处理
|
||||
stack.push(left / right > 0 ? Math.floor(left / right) : Math.ceil(left / right));
|
||||
}
|
||||
} else {
|
||||
stack.push(parseInt(token, 10));
|
||||
}
|
||||
}
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
/*
|
||||
思路和上面一致,但是不使用栈,而是使用一个数组来储存要操作的数,用一个下标指向数组的最后一个元素,
|
||||
可以理解成栈顶的元素,波兰表达式的长度n一定为奇数,所以除去操作数,应该有n+1/2个数,所以数组的长度
|
||||
设置为n+1/2
|
||||
*/
|
||||
function f3(tokens) {
|
||||
const n = tokens.length;
|
||||
const optNums = new Array(Math.floor((n + 1) / 2)).fill(0);
|
||||
let index = -1; // 指向optNums中最后一个元素
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const token = tokens[i];
|
||||
if (token === '+') {
|
||||
index--;
|
||||
optNums[index] += optNums[index + 1];
|
||||
} else if (token === '-') {
|
||||
index--;
|
||||
optNums[index] -= optNums[index + 1];
|
||||
} else if (token === '*') {
|
||||
index--;
|
||||
optNums[index] *= optNums[index + 1];
|
||||
} else if (token === '/') {
|
||||
index--;
|
||||
const result = optNums[index] / optNums[index + 1];
|
||||
optNums[index] = result > 0 ? Math.floor(result) : Math.ceil(result);
|
||||
} else {
|
||||
// token 为数字,将它存入optNums中
|
||||
index++;
|
||||
optNums[index] = parseInt(token, 10);
|
||||
}
|
||||
}
|
||||
return optNums[index];
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
const MinStack = function () {
|
||||
this.data = []; // 内部使用数组维护栈元素
|
||||
this.minstack = [Infinity]; // 利用栈结构维护最小元素
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} val
|
||||
* @return {void}
|
||||
*/
|
||||
MinStack.prototype.push = function (val) {
|
||||
// 判断当前插入的元素是否比栈的最小元素还小
|
||||
if (val < this.minstack[this.minstack.length - 1]) this.minstack.push(val);
|
||||
this.data.push(val);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
MinStack.prototype.pop = function () {
|
||||
// 判断栈顶的元素是否是最小元素,如果是,最小元素栈顶的元素也要删除
|
||||
if (this.data[this.data.length - 1] === this.minstack[this.minstack.length - 1]) {
|
||||
this.minstack.pop();
|
||||
}
|
||||
if (this.data.length) this.data.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
MinStack.prototype.top = function () {
|
||||
if (this.data.length) return this.data[this.data.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
MinStack.prototype.getMin = function () {
|
||||
return this.minstack[this.minstack.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Your MinStack object will be instantiated and called as such:
|
||||
* var obj = new MinStack()
|
||||
* obj.push(val)
|
||||
* obj.pop()
|
||||
* var param_3 = obj.top()
|
||||
* var param_4 = obj.getMin()
|
||||
*/
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @param {string} s
|
||||
* @return {boolean}
|
||||
*/
|
||||
const isValid = function (s) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
题目所给的测试用例只包含'(',')','{','}','[',']'这些字符,如果是一个有效字符则说明它一定是一对一对的出现
|
||||
也就是说s的长度是偶数,如果是奇数就一定存在一个括号没有闭合,倘若是偶数个,我们需要在遇到右括号时检查它的前一个
|
||||
括号是否是对应的左括号,如果是的话,则表明这是一对合理的括号,之后检查后一个括号,以次类推。
|
||||
*/
|
||||
function f1(s) {
|
||||
// 如果长度不是偶数,一定不能正常闭合
|
||||
if (s.length % 2 !== 0) return false;
|
||||
// 括号的映射表
|
||||
const map = new Map([
|
||||
[')', '('],
|
||||
[']', '['],
|
||||
['}', '{'],
|
||||
]);
|
||||
const stack = []; // 利用栈结构来检查括号是否整除闭合
|
||||
for (const brack of s) {
|
||||
if (map.has(brack)) {
|
||||
// 如果栈为空,或者栈顶括号不是对应的括号,表明不能正常闭合,返回false
|
||||
if (!stack.length || stack[stack.length - 1] !== map.get(brack)) return false;
|
||||
stack.pop();// 如果正常闭合,从栈中去掉这个左括号
|
||||
} else {
|
||||
stack.push(brack);
|
||||
}
|
||||
}
|
||||
return !stack.length;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/basic-calculator/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {string} s
|
||||
* @return {number}
|
||||
*/
|
||||
const calculate = function (s) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
定义一个sign变量,表示数前面的符号,当数字收集完毕就使用这个符号操作,这个变量的默认值为+1,原因很简单,
|
||||
表达式为一个整体,可以理解为一个括号,这个括号之前隐式的包含了一个+号,比如表达式 -1 + 2 -(1-2),可以
|
||||
把它看成+(-1 + 2 -(1-2)),我们还需要使用一个栈结构来保存每一层括号表示的符号,以上面的表达式为例,第一个
|
||||
括号显然为+,所以栈结构为[+1],之后这个括号下一级的数都会受它影响,比如括号里面的-1会变为1*-1,1*(+2)都
|
||||
保持不变,当遇到"("时需要把这一层的符号压入栈中,栈结构为[+1,-1],第二级的括号里面的数都会受他影响,当遇到
|
||||
)表示,这一层级的数都操作完必了,栈中无需保存这一层级的符号,直接弹出即可,这样一次遍历即可得到结果。
|
||||
*/
|
||||
|
||||
function f1(s) {
|
||||
const opt = [1];// 保存每一层括号的符号,初始表达式可以看成一个整体,所以默认值为[1]
|
||||
let sign = 1; // 收集数字之后,数字前面的符号
|
||||
let ret = 0; // 计数求和的结果
|
||||
|
||||
const n = s.length; // 字符串的长度,用于控制遍历
|
||||
let i = 0; // 遍历下标
|
||||
|
||||
while (i < n) {
|
||||
if (s[i] === ' ') { // 遇到空格直接跳过
|
||||
i++;
|
||||
} else if (s[i] === '+') { // 如果遇到加号,根据当前层级的符号更新sign
|
||||
sign = opt[opt.length - 1];
|
||||
i++;
|
||||
} else if (s[i] === '-') { // 如果遇到减号,和上面同理
|
||||
sign = -opt[opt.length - 1];
|
||||
i++;
|
||||
} else if (s[i] === '(') { // 遇到左括号,表明层级加深,把sign压入栈中即可
|
||||
opt.push(sign);
|
||||
i++;
|
||||
} else if (s[i] === ')') { // 遇到右括号,表明当前层级处理完毕,把当前层级的符号弹出即可
|
||||
opt.pop();
|
||||
i++;
|
||||
} else {
|
||||
// 上面的情况处理完毕,在这里只有一种情况,那么就是数字本身,数字可能是多位数,所以
|
||||
// 需要一次遍历来收集
|
||||
let num = 0; // 用于收集数字
|
||||
while (i < n && s[i] >= '0' && s[i] <= '9') {
|
||||
num = num * 10 + (s[i] - '0');
|
||||
i++;
|
||||
}
|
||||
// 数字收集完毕,通过它前面的符号sign和结果求和
|
||||
ret += sign * num;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* https://leetcode.cn/problems/simplify-path/?envType=study-plan-v2&envId=top-interview-150
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
*/
|
||||
const simplifyPath = function (path) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
首先通过'/'来分割path,我们会得到如下的字符串,目录名,"."当前目录,".."上级目录,如果遇到的是文件名,
|
||||
就把文件名压入栈stack中,如果遇到的是.或者空字符无需做任何操作,如果遇到的是".."则需要把当前stack栈顶
|
||||
的目录弹出表示跳转到上级目录
|
||||
*/
|
||||
function f1(path) {
|
||||
const paths = path.split('/'); // 使用/来分割路径
|
||||
const stack = []; // 利用栈来处理路径
|
||||
for (const p of paths) {
|
||||
// if (p === '.' || p === '') continue;
|
||||
// if (p === '..') {
|
||||
// // 返回到上级目录
|
||||
// if (stack.length) stack.pop();
|
||||
// continue;
|
||||
// }
|
||||
// stack.push(p);
|
||||
// 优化上面分支,实际上我们要做的就两件事情,第一:如果是".."表明要跳转到上级目录,如果一个合规
|
||||
// 的目录名,就应该压入栈中
|
||||
if (p === '..') {
|
||||
if (stack.length) stack.pop();
|
||||
} else if (p !== '' && p !== '.') {
|
||||
stack.push(p);
|
||||
}
|
||||
}
|
||||
return `/${stack.join('/')}`;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user