1.链表定义
链表是一组节点组成的集合,每个节点都使用一个对象的引用来指向它的后一个节点。指向另一节点的引用讲做链。 链表与数组都属于物理结构。
2.链表操作
1.查询
通过循环一直查找每一个元素的next,知道内容匹配。
2.插入
1.尾部插入
直接把最后一个node 的next指向 新元素。
2.头部插入 直接把新元素插的next 指向 head的元素。
3.中间插入 现在新节点的next指向插入节点,然后把插入节点的上一个指向新节点。
3.删除
与插入逻辑类似。
3.代码实现
class Node {
constructor(element) {
this.element = element
this.next = null
}
}
class LinkNodeList {
constructor() {
this.length = 0
this.head = null
}
append(element) {
let node = new Node(element)
if (this.head === null) {
this.head = node
}else {
let cur = this.head
while(cur.next) {
cur = cur.next
}
cur.next = node
}
this.length++
console.log("this.length",this.length)
}
print() {
let cur = this.head
let arr = []
while(cur) {
arr.push(cur.element)
cur = cur.next
}
console.log(arr.join("=>"))
}
getHead() {
// console.log(this.head)
return this.head
}
deleteAt(index) {
let cur = this.head
let i = 0
let prev = null
if (index == 0) {
this.head = cur.next
}else {
while(i < index) {
prev = cur
cur = cur.next
i++
}
prev.next = cur.next
cur.next = null
}
this.length--
return cur.element
}
}
let linkNodeList = new LinkNodeList()
linkNodeList.append("111")
linkNodeList.append("222")
linkNodeList.append("333")
console.log(linkNodeList.deleteAt(0))
console.log(linkNodeList.length)
linkNodeList.print()
4.leetcode刷题
203. 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。 示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
答题
/**
* 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} val
* @return {ListNode}
*/
// [1,2,6,3,4,5,6] 6
// [1,2,3,4,5]
// 使用哨兵 + while
// 时间复杂度O(N),空间复杂度O(1)
var removeElements = function(head, val) {
let top = {
val:"",
next:head
}
let cur = top
while (cur.next) {
if(cur.next.val === val) {
cur.next = cur.next.next
} else {
cur = cur.next
}
}
return top.next
};
//判断head+ while + 递归
//时间复杂度O(N),空间复杂度O(1)
var removeElements = function(head, val) {
if(!head) {
return head
}
if (head.val == val) {
head = head.next
return removeElements(head,val)
}else {
let cur = head
while(cur.next) {
if (cur.next.val === val) {
cur.next = cur.next.next
}else{
cur = cur.next
}
}
return head
}
};
206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
示例 2:
输入: head = [1,2]
输出: [2,1]
答题:
/**
* 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}
*/
//解构版本-优化
// 时间复杂度O(N),空间复杂度O(1)
var reverseList = function(head) {
let cur = head
let prev = null
while (cur) {
[cur.next,prev,cur] = [prev,cur,cur.next]
}
return prev
};
//正常while 反向prev
// 时间复杂度O(N),空间复杂度O(n)
var reverseList = function(head) {
let cur = head
let prev = null
while (cur) {
let next = cur.next
cur.next = prev
prev = cur
cur = next
}
return prev
};
141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例 1:
输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入: head = [1,2], pos = 0
输出: true
解释: 链表中有一个环,其尾部连接到第一个节点。
答题
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
//快慢指针 最优
// 时间O(N),空间 O(0)
// 注意 快慢指针如果有环一定会相遇,
// 因为慢一直往前走一格,快走两个格,两者算下来,快的每次只是递进1格。
// 要么差1格时候,slow=5,fast=4, 再循环后是 slow=6,fast=6刚好相遇
// 要么差2格时候,slow=5,fast=3, 再循环后是 slow=6,fast=5刚好相遇,变回上面一格的情况所以还是相遇。
var hasCycle = function(head) {
let slow = head
let fast = head
while (fast && fast.next) {
slow = slow.next
fast = fast.next.next
if(slow === fast) {
return true
}
}
return false
};
// while + Set 判断
//时间O(N),空间 O(N)
var hasCycle = function(head) {
const cache = new Set()
let cur = head
while (cur) {
if (cache.has(cur)) {
return true
} else {
cache.add(cur)
}
cur = cur.next
}
return false
};
// while i++ 循环
//时间O(N2),空间 O(1)
var hasCycle = function(head) {
let i = 0
let cur = head
while (cur) {
if(i > 10000) {
return true
}
cur = cur.next
i++
}
return false
};
//日期循环
//时间O(N2),空间 O(1)
var hasCycle = function(head) {
let cur = head
let now = new Date().getTime()
while (cur) {
if( (new Date().getTime()) > now + 100) {
return true
}
cur = cur.next
}
return false
};
142. 环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入: head = [3,2,0,-4], pos = 1
输出: 返回索引为 1 的链表节点
解释: 链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入: head = [1,2], pos = 0
输出: 返回索引为 0 的链表节点
解释: 链表中有一个环,其尾部连接到第一个节点。
问题分析
已知:slow和fast可以相遇,在K点相遇,求X点的位置?
fast的速度是slow的2倍,两者使用的时间相同
距离 = 速度 * 时间 s = v * t ,
得出相等公式 S(slow) * 2 = S(fast)
由于slow走到K点时,S(slow) = a + b
fast 走到K点时,S(fast) = a + b + c + b
最终的到公式 (a+b) * 2 = a + b + c + b , 得出 a = c
等价于 从0到X点 = K点到X点\
结论
相当于slow和fast相遇后,slow 继续走只要与从新开始的start相遇, 如果判断是相等,那当前的slow的下标就是X的下标。
答题
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
//使用快慢指针+ ( start 与 slow 交集)
// 时间 O(N),空间 O(1)
var detectCycle = function(head) {
let slow = head
let fast = head
let start = head
while (fast && fast.next) {
slow = slow.next
fast = fast.next.next
if(slow === fast) {//当相遇的时候 再把start 和 slow 一起转,直到相遇,此时的slow就是出现环的下标
while (slow && start) {
if(slow === start) {
return slow
}
start = start.next
slow = slow.next
}
}
}
return null
};
//使用Set
// 时间 O(N),空间 O(N)
var detectCycle = function(head) {
let cache = new Set()
while (head) {
if (cache.has(head)) {
return head
} else {
cache.add(head)
}
head = head.next
}
return null
}