feat: 链表操作 2,19,21,25,61,82,86,92,138,141

This commit is contained in:
LouisFonda 2025-04-19 18:10:05 +08:00
parent bd018e32ea
commit c2c64470b0
Signed by: yigencong
GPG Key ID: 29CE877CED00E966
11 changed files with 915 additions and 0 deletions

View File

@ -0,0 +1,148 @@
/**
* 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);
}

View File

@ -0,0 +1,49 @@
/**
* 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; // 如果快指针走到了链表末尾,表示没有环
}

View File

@ -0,0 +1,118 @@
/**
* 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;
}

View File

@ -0,0 +1,97 @@
/**
* 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 的节点被连接到了合并后的链表中
}

View File

@ -0,0 +1,85 @@
/**
* 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; // 返回链表的长度
};

View File

@ -0,0 +1,92 @@
/**
* 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; // 返回结果链表的头节点
}

View File

@ -0,0 +1,48 @@
/**
* 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;
}

View File

@ -0,0 +1,48 @@
/**
* 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;
}

View File

@ -0,0 +1,46 @@
/**
* 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;
}

View File

@ -0,0 +1,157 @@
/**
* 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; // 返回反转后的链表
}

View File

@ -0,0 +1,27 @@
/**
* 获取链表的节点个数
* @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);
}