链表反转
输入一个链表,反转链表后,输出新链表的表头。
思路:
遍历链表,分别用currentNode、nextNode、preNode记录当前节点、上一个节点、下一个节点
function reverseList(head) {
let currentNode = head;
let nextNode = head.next;
let preNode = null;
while (currentNode) {
currentNode.next = preNode;
preNode = currentNode;
currentNode = nextNode;
nextNode = currentNode && nextNode.next;
}
return preNode;
}
从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
思路:先将链表每个结点的值存入数组中,然后通过数组的reverse方法,即可从尾到头打印。
function ListNode(x){
this.val = x;
this.next = null;
}
function printListFromTailToHead(head){
if(!head) {
return 0;
}
else {
let arr = new Array();
let cur = head;
while(cur) {
arr.push(cur.val);
cur = cur.next;
}
return arr.reverse();
}
}
let node1 = new ListNode(1);
let node2 = new ListNode(2);
let node3 = new ListNode(3);
node1.next = node2;
node2.next = node3;
console.log(printListFromTailToHead(node1));
这里需要反向打印链表,于是很自然的可以想到用递归来实现。要实现反过来输出链表,我们每次访问到一个节点的时候,先递归输出它后面的节点,再输出该节点自身,这样链表的输出结果就反过来了。
function printListFromTailToHead(head) {
if(head !== null) {
printListFromTailToHead(head.next);
console.log(head.val);
}
}
删除链表中的节点
给定单链表的头指针和要删除的指针节点,在O(1)时间内删除该节点。
思路:
1.删除的节点不是尾部节点 - 将next节点覆盖当前节点
2.删除的节点是尾部节点且等于头节点,只剩一个节点 - 将头节点置为null
3.删除的节点是尾节点且前面还有节点 - 遍历到末尾的前一个节点删除
只有第三种情况时间复杂度是O(n),且这种情况只会出现1/n次,所以算法时间复杂度是O(1)
function deleteNode(head, node) {
if (node.next) {
node.val = node.next.val;
node.next = node.next.next;
} else if (node === head) {
node = null;
head = null;
} else {
node = head;
while (node.next.next) {
node = node.next;
}
node.next = null;
node = null;
}
return head;
}
复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。
思路:
拆分成三步
1.复制一份链表放在前一个节点后面,即根据原始链表的每个节点Node创建Node',把Node'直接放在Node的next位置,Node'的next指向Node的next,让复制后的链表和原始链表组成新的链表,相当于把克隆的节点插入在被克隆节点的后面。形成链表比如:Node0->Node0'->Node1->Node1'->Node2->Node2'->Node3->Node3'
2.给复制的链表random赋值,即Node'.random=Node.random.next。
3.拆分链表,将Node'和Node进行拆分,保证原始链表不受影响。
function Clone(pHead) {
if (pHead === null) {
return null;
}
cloneNodes(pHead);
cloneRandom(pHead);
return reconnetNodes(pHead);
}
// 第一步:克隆节点
function cloneNodes(pHead) {
let current = pHead;
while(current) {
let cloneNode = {
val : '',
next : null,
random : null
}
cloneNode.val = current.val;
cloneNode.next = current.next;
current.next = cloneNode;
current = cloneNode.next;
}
return pHead;
}
// 第二步:给克隆的节点random赋值
function cloneRandom(pHead) {
let current = pHead;
let cloneNode;
while(current) {
cloneNode = current.next;
cloneNode.random = current.random && current.random.next;
current = cloneNode.next;
}
return pHead;
}
// 第三步:拆分链表
function reconnetNodes(pHead) {
let current = pHead;
let cloneNode;
let cloneHead = current.next;
while(current) {
cloneNode = current.next;
current.next = cloneNode.next;
cloneNode.next = current.next && current.next.next;
current = current.next;
}
return cloneHead;
}
合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
循环版本:
function merge(list1, list2) {
let head = {};
head.next=null;
let root = head;
while(list1!=null&&list2!=null) {
if(list1.val<list2.val){
head.next=list1;
head=list1;
list1=list1.next;
}
else {
head.next=list2;
head=list2;
list2=list2.next;
}
}
//如果有一个链表为空,另一个链表非空,则应该把非空链表合并到链表尾部。
if(list1!=null) {
head.next=list1;
}
if(list2!=null) {
head.next=list2;
}
return root.next;
}
递归版本:
function merge(list1, list2) {
if (!list1) {
return list2;
}
if (!list2) {
return list1;
}
let head;
if (list1.val < list2.val) {
head = list1;
head.next = merge(list1.next, list2);
} else {
head = list2;
head.next = merge(list1, list2.next);
}
return head;
}
链表倒数第k个节点
输入一个链表,输出该链表中倒数第k个结点。
思路:
简单思路: 循环到链表末尾找到 length 在找到length-k节点 需要循环两次。
优化:
设定两个节点,间距相差k个节点,当前面的节点到达终点,取后面的节点。
前面的节点到达k后,后面的节点才出发。
代码鲁棒性: 需要考虑head为null,k为0,k大于链表长度的情况。
function FindKthToTail(head, k) {
if (!head || !k) return null;
let front = head;
let behind = head;
let index = 0;
while (front.next) {
index++;
if (index >= k) {
behind = behind.next;
}
front = front.next;
}
return (k <= index) && behind;
}
链表中环的入口节点
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路:
声明两个指针 P1 P2
1.判断链表是否有环: P1 P2 从头部出发,P1走两步,P2走一步,如果可以相遇,则环存在
2.从环内某个节点开始计数,再回到此节点时得到链表环的长度 length
3.P1、P2 回到head节点,让 P1 先走 length 步 ,当P2和P1相遇时即为链表环的起点
function EntryNodeOfLoop(pHead) {
if (!pHead || !pHead.next) {
return null;
}
let P1 = pHead.next;
let P2 = pHead.next.next;
// 1.判断是否有环
while (P1 != P2) {
if (P2 === null || P2.next === null) {
return null;
}
P1 = P1.next;
P2 = P2.next.next;
}
// 2.获取环的长度
let temp = P1;
let length = 1;
P1 = P1.next;
while (temp != P1) {
P1 = P1.next;
length++;
}
// 3.找公共节点
P1 = P2 = pHead;
while (length-- > 0) {
P2 = P2.next;
}
while (P1 != P2) {
P1 = P1.next;
P2 = P2.next;
}
return P1;
}
两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
思路:
1.先找到两个链表的长度length1、length2
2.让长一点的链表先走length2-length1步,让长链表和短链表起点相同
3.两个链表一起前进,比较获得第一个相等的节点
时间复杂度O(length1+length2) 空间复杂度O(0)
function FindFirstCommonNode(pHead1, pHead2) {
if (!pHead1 || !pHead2) { return null; }
// 获取链表长度
let length1 = getLength(pHead1);
let length2 = getLength(pHead2);
// 长链表先行
let lang, short, interval;
if (length1 > length2) {
lang = pHead1;
short = pHead2;
interval = length1 - length2;
} else {
lang = pHead2;
short = pHead1;
interval = length2 - length1;
}
while (interval--) {
lang = lang.next;
}
// 找相同节点
while (lang) {
if (lang === short) {
return lang;
}
lang = lang.next;
short = short.next;
}
return null;
}
function getLength(head) {
let current = head;
let result = 0;
while (current) {
result++;
current = current.next;
}
return result;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。