# 链表
# 用JS实现链表
- 先写一下链表所需要的属性和方法
function LinkedList(){
this.head = null // 链表的首位
this.length = 0 // 链表的长度
// 链表节点
function Node(value){
this.value = value
this.next = null
}
this.append = function (){} // 向尾部添加新的节点
this.insert(position,element) // 向链表特定位置插入元素;
this.remove(element) // 从链表移除一项;
this.indexOf(element) // 返回链表中某元素的索引,如果没有返回-1;
this.removeAt(position) // 从特定位置移除一项;
this.isEmpty() // 判断链表是否为空,如果为空返回true,否则返回false;
this.size() // 返回链表包含的元素个数;
this.toString() // 重写继承自Object类的toString()方法,因为我们使用了Node类;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 实现append
function LinkedList(){
this.head = null // 链表的首位
this.length = 0 // 链表的长度
// 链表节点
function Node(value){
this.value = value
this.next = null
}
// 向尾部添加新的节点
this.append = function (value){
let newNode = new Node(value)
if(this.length === 0){
this.head = newNode
}else{
let current = this.head
while(current.next){
current = current.next
}
current.next = newNode
}
this.length++
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 实现insert
// ... 省略 ...
this.insert = function (position,element){
if(position>=0 && position<=this.length){
let newNode = new Node(element)
let current = this.head
if(position === 0){
newNode.next = current
this.head = newNode
} else {
while(--position){
current = current.next
}
}
newNode.next = current.next
current.next = newNode
this.length++
return true
}else{
return false
}
}
// ... 省略 ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 实现indexOf
// ... 省略 ...
this.indexOf = function(element){
let currnet = this.head
let index = 0
while(currnet){
if(element === currnet.value){
return index
}
currnet = currnet.next
index++
}
return -1
}
// ... 省略 ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 实现removeAt
// ... 省略 ...
this.removeAt = function(position){
let current = this.head
if(position>=0 && position<=this.length){
let returnValue = null
if(position === 0){
returnValue = current
this.head = current.next
} else{
let previous = null
while(position--){
previous = current
current = current.next
}
returnValue = current
previous.next = current.next
}
this.length--
return returnValue
}else{
return false
}
}
// ... 省略 ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 实现remove
// ... 省略 ...
this.remove = function(element){
let postion = this.indexOf(element)
return this.removeAt(postion)
}
// ... 省略 ...
1
2
3
4
5
6
2
3
4
5
6
# 实现 isEmpty
- 如果是空返回
true
,否则false
// ... 省略 ...
this.isEmpty = function(){
return this.length === 0
}
// ... 省略 ...
1
2
3
4
5
2
3
4
5
# 实现 size
// ... 省略 ...
this.size = function(){
return this.length
}
// ... 省略 ...
1
2
3
4
5
2
3
4
5
# 实现 toString
// ... 省略 ...
this.toString = function() {
let current = this.head
let str = ''
while(current){
str +=current.value+(current.next?',':'')
current = current.next
}
return str
}
// ... 省略 ...
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 测试
let list = new LinkedList()
list.append(1)
list.append(3)
list.insert(0,4)
list.insert(0,4)
list.removeAt(1)
list.remove(3)
console.log(list.size()); // 2
console.log(list.head.next) // Node { value: 1, next: null }
console.log(list.toString()) // 4,1
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 双向链表
- 所谓双向链表,其实就是在数据节点上增加前一个保留前一个节点的属性
- 接下来实现一下和单链表一样的功能
# 基础结构
function LinkedList(){
this.head = null
this.length = 0
function Node(value){
this.value = value
this.next = null
this.prev = null // 和单链表区别的地方
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 双向链表 append
// 向尾部添加新的节点
this.append = function (value){
let newNode = new Node(value)
if(this.length === 0){
this.head = newNode
}else{
let current = this.head
while(current.next){
current = current.next
}
newNode.prev = current
current.next = newNode
}
this.length++
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 双向链表 insert
// 向链表特定位置插入元素;
this.insert=function(position, element) {
let newNode = new Node(element)
let current = this.head
// 当还没有数据的时候
if(position === 0 && this.length===0) {
this.head = newNode
return this.length++
}
// 当有数据插入到首个元素的时候
if(position === 0 && this.length!==0){
newNode.next = current
current.prev = newNode
this.head = newNode
return this.length++
}
// 多个元素的时候,插入到中间位置,如果索引大于当前的链表长度,插入失败返回false
if (position > 0 && position <= this.length) {
while(position>0 && current.next){
current = current.next
position--
}
if(current.next !== null){
newNode.prev = current
newNode.next = current.next
current.next.prev = newNode
current.next = newNode
} else {
newNode.prev = current
newNode.next = null
current.next = newNode
}
return this.length++
}else {
return false
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 双向链表 indexOf
- 这个方法和但链表的一致
// 返回链表中某元素的索引,如果没有返回-1;
this.indexOf=function(element) {
let current = this.head
let index = 0
while(current){
if(current.value === element){
return index
}
current = current.next
index++
}
return -1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 双向链表 removeAt
// 从特定位置移除一项;
this.removeAt=function(position) {
let current = this.head
if(position>=0&&position < this.length){
if(position === 0){
this.head = current.next
this.head.prev = null
} else {
let prev = null
while(position--){
prev = current
current = current.next
}
prev.next = current.next
if(current.next&¤t.next.prev){ // 如果current.next是null则不做处理
current.next.prev = prev
}
}
this.length--
return true
} else {
return false
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 双向链表 remove
- 该方法也和单链表一致,需要调用自身的indexOf和removeAt来实现
// 从链表移除一项;
this.remove=function(element) {
let index = this.indexOf(element)
if(index !== -1){
this.removeAt(index)
return true
}
return false
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# isEmpty 和 size 和 toString 和单链表一样
# 环形链表 即 最后一个元素的 next 指向 head
# 链表常见算法题
# 反转链表
/**
* @param {ListNode} head
* @return {ListNode}
*/
function reverse(head){
let newHead = null
while(head){
let temp = head
head = head.next
temp.next = newHead
newHead = temp
}
return newHead
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 俩俩交换链表数据
TIP
1->2->3->4 swapPairs 2->1->4->3
- 解决方案一 循环迭代的方式
function swapPairs(head){
let dummy = head.next
let prev = null
while(head && head.next){
let firstNode = head
let secondNode = head.next
// 和上一轮的最后一位关联
prev?prev.next = secondNode:''
// swap
firstNode.next = secondNode.next
secondNode.next = firstNode
// 准备下一轮赋值
head = firstNode.next
prev = firstNode
}
return dummy
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 解决方案二 递归的方式
function swapPairs(head) {
if(!head || !head.next) return head
let firstNode = head
let secondNode = head.next
firstNode.next = swapPairs(secondNode.next)
secondNode.next = firstNode
return secondNode
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 检测链表中是否有环
- 如果有环输出该环的head,如果没有则返回null
- 主要利用了快满指针的思想
var detectCycle = function(head) {
let fast = head,slow = head;
// 边界值判断
if(!head||!head.next){
return null
}
// 快慢指针循环,如果有相等的情况则代表有环
while(fast&&fast.next){
slow = slow.next
fast = fast.next.next
if(slow == fast){
break
}
}
// 上面的循环结束后,如果fast和slow不想等则代表没有环
if(fast!= slow){
return null
}
// 将slow指向链表最开始的位置
slow = head
// 接下来fast和slow每次循环走一步,直到相等后,就找到了有环链表的head
while(fast!=slow){
fast= fast.next
slow = slow.next
}
return slow
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
← js 实现的一些算法 栈 →