203. 移除链表元素 简单题
方法一:在原链表上直接删除
var removeElements = function(head, val) {
// 处理头结点
while(head && head.val == val) head = head.next;
let pre = head;
let cur;
if(head) cur = head.next;
while(cur) {
if(cur.val === val) {
pre.next = cur.next;
}else pre = cur;
cur = cur.next;
}
return head;
};
方法二: 设置一个虚拟头结点
var removeElements = function(head, val) {
//构建虚拟头结点,便于处理删除节点为头结点的情况
let node = new ListNode(0,head);
let pre = node; // pre记录前一结点
while(head) { // head为当前结点
if(head.val === val) {
pre.next = head.next;
} else pre = head;
head = head.next;
}
return node.next; // node.next为链表的头结点
};
707. 设计链表 中等题
class LinkNode {
constructor(val,next) {
this.val = val;
this.next = next;
}
}
var MyLinkedList = function() {
this.head = null;
this.tail = null;
this.size = 0;
};
MyLinkedList.prototype.getNode = function(index) {
if(index < 0 || index >= this.size) return null;
// 0 --> this.head
let cur = this.head;
while(index--) {
cur = cur.next;
}
return cur;
}
/**
* @param {number} index
* @return {number}
*/
MyLinkedList.prototype.get = function(index) {
if(index < 0 || index >= this.size) return -1;
// 获取当前节点
return this.getNode(index).val;
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtHead = function(val) {
let node = new ListNode(val,this.head);
this.head = node;
this.size++;
if(!this.tail) {
this.tail = this.head;
}
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtTail = function(val) {
let node = new ListNode(val);
this.size++;
if(this.tail) {
this.tail.next = node;
this.tail = node;
}else {
this.head = node;
this.tail = node;
}
};
/**
* @param {number} index
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtIndex = function(index, val) {
if(index > this.size) return ;
if(index <= 0) {
this.addAtHead(val);
return ;
}
if(index == this.size) {
this.addAtTail(val);
return ;
}
let pre = this.getNode(index-1);
let node = new ListNode(val);
node.next = pre.next;
pre.next = node;
this.size++;
};
/**
* @param {number} index
* @return {void}
*/
MyLinkedList.prototype.deleteAtIndex = function(index) {
if(index < 0 || index >= this.size) return ;
// 分为: 删除头结点和非头结点,两种情况都要考虑尾节点
if(index == 0) {
this.head = this.head.next;
// 删除的这个节点同时是尾节点,要处理尾节点
if(index == this.size - 1) this.tail = this.head;
} else {
// 获取目标节点的上一个节点
let pre = this.getNode(index-1);
pre.next = pre.next.next;
// 处理尾节点
if(index == this.size-1) {
this.tail = pre;
}
}
this.size--;
};
206. 反转链表 简单题
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表
方法一: 递归法
function reverse(pre,head) {
if(head == null) return pre;
let next = head.next;
head.next = pre;
return reverse(head,next);
}
var reverseList = function(head) {
return reverse(null,head);
};
方法二: 非递归法
var reverseList = function(head) {
let node = null;
while(head) {
let next = head.next;
head.next = node;
node = head;
head = next;
}
return node;
};
24. 两两交换链表中的节点 中等题
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)
var swapPairs = function(head) {
let node = new ListNode(0,head);
let pre = node;
while(head && head.next) {
let next = head.next.next;
pre.next = head.next;
head.next.next = head;
head.next = next;
pre = head;
head = next;
}
return node.next;
};
方法一
先遍历链表计算链表长度,链表长度 - n - 1 为要删除节点的上一节点位置 (不推荐)
var removeNthFromEnd = function(head, n) {
// 统计整个链表长度
let len = 0;
let cur = head;
while(cur) {
cur = cur.next;
len++;
}
// 删除的是头节点
if(n == len) return head.next;
// 删除的是非头节点
len = len - n -1;
cur = head;
while(len--) {
cur = cur.next;
}
cur.next = cur.next.next;
return head;
};
方法二
如果要删除倒数第n个节点,让fast移动n+1步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的下一个节点就可以了
var removeNthFromEnd = function(head, n) {
// 设置虚拟头节点,避免头结点的处理
let node = new ListNode(0,head);
// fast,slow都从虚拟头结点开始
let slow = node,fast = node;
// 快指针先走 n 步
while(n--) {
fast = fast.next;
}
// 然后快慢指针同时走,快指针到达最后一个时,slow来到待删除节点的前一个
while(fast.next) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return node.next;
};
面试题 02.07. 链表相交 简单题
//求链表的长度
var getListLen = function(head){
let len =0;
while(head){
len++;
head = head.next;
}
return len;
}
var getIntersectionNode = function(headA, headB) {
let lenA =getListLen(headA);
let lenB =getListLen(headB);
//固定: A作为较长的链表,B为较短的链表
if(lenA < lenB){
[lenA,lenB]=[lenB,lenA];
[headA,headB] = [headB,headA];
}
//两个链表长度之差
let n = lenA -lenB;
//让长链表走(两个链表长度之差)个节点
//长,短链表此时步伐同步,则会相遇或相遇于null
while(n--)headA=headA.next;
while(headA && headA !==headB){
headA=headA.next;
headB=headB.next;
}
//A,B相交 则headA为第一个相交节点,不相交则headA为null
return headA;
};
142. 环形链表 II 中等题
方法一: 哈希表
var detectCycle = function(head) {
let map = new Map();
while(head) {
if(map.has(head)) {
return head;
}
map.set(head);
head = head.next;
}
return null;
};
方法二: 快慢指针法(双指针)
主要考虑以下两个问题:
- 判断链表是否有环
- 如果有环,如何找到这个环的入口 !
判断链表是否有环
使用快慢指针法: slow走一步,fast走两步
如果链表没有环,那么fast一定会先到链表尾节点
如果链表有环,那么fast一定先入环,slow和fast一定会在环中相遇
为什么有环的时候,slow和fast一定会在环中相遇呢???
如果有环,如何找到这个环的入口 !
slow与fast相遇在环中,此时slow回到链表的头节点
slow,fast 一次都走一步,它们第一次相遇的节点就是环的入口节点!!!
那么解释一下为什么这样它们第一次相遇的节点就是环的入口节点???
var detectCycle = function(head) {
// 链表无环
if(!head || !head.next) return null;
// 判断链表是否有环
let slow = head.next, fast = head.next.next;
// slow 初始化值一定不是null,fast可能为null
// 如果无环, fast或fast.next 一定先到达null
while(fast && fast.next && slow != fast) {
slow = slow.next;
fast = fast.next.next;
}
// 判断循环结束的情况
if(!fast || !fast.next) return null; // 无环
// 有环,如何找到入环的第一个节点呢
// slow回到链表头节点,fast,slow一起走,都是走一步,那么它们一定会在入环的第 一个节点相遇
slow = head;
while(slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
};