链表
基本概念
- 链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
- 链接的入口节点称为链表的头结点也就是head。
链表类型
- 单链表(上述就是单链表):每个节点只有一个指针域,节点只能指向节点的下一个节点
- 双链表(每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点),既可以向前查询也可以向后查询
- 循环链表(链表首尾相连):用来解决约瑟夫环问题
约瑟夫环问题:
主要方法
链表的定义:
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
链表操作的两种方式:
- 直接使用原来的链表来进行删除操作。
- 设置一个虚拟头结点再进行删除操作。
js中可以使用:const ret = new ListNode(0, head);
示例
移除链表元素
题目链接:[203.移除链表元素](203. 移除链表元素 - 力扣(LeetCode) (leetcode-cn.com))
解答:
// 设置一个虚拟头结点来解决
var removeElements = function(head, val) {
const dummyNode = new ListNode(0, head);
let cur = dummyNode;
while(cur.next){
// if(cur.next.val === val){
// cur.next = cur.next.next;
// } else {
// cur = cur.next;
// }
if(cur.next.val === val){
cur.next = cur.next.next;
continue;// 以防cur.next.next.val的值仍然为val的情况,所以还需要检查一遍
}
cur = cur.next;
}
return dummyNode.next;
};
// 直接在原来链表上进行修改
var removeElements = function(head, val) {
// 删除头部节点
while(head !== null && head.val === val) {
head = head.next;
}
if(head === null) return head;
// 删除非头部节点
let pre = head, cur = head.next;
while(cur){
if(cur.val === val){
pre.next = cur.next;
} else {
pre = pre.next;
}
cur = cur.next;
}
return head;
}
设计链表
题目链接:[707.设计链表](707. 设计链表 - 力扣(LeetCode) (leetcode-cn.com))
class LinkNode {
constructor(val, next){
this.val = (val === undefined ? 0 : val);
this.next = (next === undefined ? null : next);
}
}
var MyLinkedList = function() {
this._size = 0;
this._tail = null;
this._head = null;
};
MyLinkedList.prototype.getNode = function(index) {
if(index < 0 || index >= this._size) return null;
let cur = new LinkNode(0, this._head);
while(index-- >= 0){
cur = cur.next;
}
return cur;
};
MyLinkedList.prototype.get = function(index) {
if(index < 0 || index >= this._size) return -1;
return this.getNode(index).val;
};
MyLinkedList.prototype.addAtHead = function(val) {
const node = new LinkNode(val, this._head);
this._head = node;
this._size++;
if(!this._tail){
this._tail = node;
}
};
MyLinkedList.prototype.addAtTail = function(val) {
const node = new LinkNode(val, null);
this._size++;
if(this._tail){
this._tail.next = node;
this._tail = node;
return;
}
this._tail = node;
this._head = node;
};
MyLinkedList.prototype.addAtIndex = function(index, val) {
if(index === this._size) {
this.addAtTail(val);
return;
}
if(index > this._size) {
return;
}
if(index <= 0) {
this.addAtHead(val);
return;
}
const node = this.getNode(index - 1);
node.next = new LinkNode(val, node.next);
this._size++;
};
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;
}
this._size--;
return;
}
const node = this.getNode(index - 1);
node.next = node.next.next;
if(index === this._size - 1){
this._tail = node;
}
this._size--;
};
反转链表
题目链接:[206.反转链表](206. 反转链表 - 力扣(LeetCode) (leetcode-cn.com))
双指针解法
var reverseList = function(head) {
if(!head || !head.next) return head;
let pre = null, cur = head;
while(cur){
let temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
};
递归解法
var reverseList = function(head) {
const reverse = function(pre, cur) {
if(!cur) return pre;
let temp = cur.next;
cur.next = pre;
return reverse(cur, temp);
}
return reverse(null, head);
};
两两交换链表中的节点
题目链接:[24.两两交换链表中的节点](Loading Question... - 力扣(LeetCode) (leetcode-cn.com))
解法:虚拟头结点进行模拟
var swapPairs = function(head) {
let dummyNode = new ListNode(0, head);
let temp = dummyNode;
while(temp.next && temp.next.next){
let pre = temp.next, cur = temp.next.next;
pre.next = cur.next;
cur.next = pre;
temp.next = cur;
temp = pre;
}
return dummyNode.next;
};
删除链表的倒数第N个节点
题目链接:[19.删除链表的倒数第N个节点](19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) (leetcode-cn.com))
解法:快慢指针
var removeNthFromEnd = function(head, n) {
let ret = new ListNode(0, head);
let slow = fast = ret;
while(n--){
fast = fast.next;
}
while(fast.next){
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return ret.next;
};
链表相交
题目链接:[面试题 02.07. 链表相交](面试题 02.07. 链表相交 - 力扣(LeetCode) (leetcode-cn.com))
解法
var getIntersectionNode = function(headA, headB) {
let ptrA = headA, ptrB = headB;
const getLength = (head) => {
let len = 0, node = head;
while(node){
len++;
node = node.next;
}
return len;
}
let lenA = getLength(headA), lenB = getLength(headB);
if(lenA < lenB){
[ptrA, ptrB] = [ptrB, ptrA];
[lenA, lenB] = [lenB, lenA];
}
let i = lenA - lenB;
while(i--){
ptrA = ptrA.next;
}
while(ptrA && ptrA !== ptrB){
ptrA = ptrA.next;
ptrB = ptrB.next;
}
return ptrA;
};
环形链表II
题目链接:[142.环形链表II](142. 环形链表 II - 力扣(LeetCode) (leetcode-cn.com))
解法 从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
var detectCycle = function(head) {
if(!head || !head.next) return null;
let slow = head.next, fast = head.next.next;
while(fast && fast.next && fast !== slow){
slow = slow.next;
fast = fast.next.next;
}
if(!fast || !fast.next) return null;
slow = head;
while(fast !== slow){
slow = slow.next;
fast = fast.next;
}
return slow;
};