链表原理:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
单链表的特点:1、只能单方向的遍历(一般从头到尾)2、上一个节点只有一个指向下一个节点的指针,如图:
p,q是指向A节点和节点的两个指针。
单项链表的缺点:从上一个节点到下一个节点很容易,但回去很难
代码实现:
//封装链表类
function LinkedList() {
//内部类: 节点类
function Node(data) {
this.data = data
this.next = null
}
this.head = null //头指针 指向链表的第一个元素
this.length = 0 //初始链表的长度
//1、追加方法
LinkedList.prototype.append = function (data) {
let newNode = new Node(data)
//判断添加的是否为第一个节点
if (this.length === 0) {
this.head = newNode
} else {
let current = this.head
while (current.next) { //寻找最后的节点并添加新节点
current = current.next
}
current.next = newNode
}
this.length += 1 //追加后 长度加1
}
//2、toString 方法
LinkedList.prototype.toString = function () {
let current = this.head
let resultString = ''
while(current) {
resultString += current.data + ' '
current = current.next
}
return resultString
}
//3、insert方法 根据确切的位置插入节点
LinkedList.prototype.insert = function (data,position) {
//对position进行越界判断
if (position < 0 || position > this.length) return false
let newNode = new Node(data)
// 判断插入的是否是第一个
if (position === 0) {
newNode.next = this.head
this.head = newNode
} else {
let index = 0
let current = this.head //当前节点
let previous = null //当前节点的上一个节点
while (index < position) { //找到插入的位置
previous = current
current = current.next
index += 1
}
newNode.next = current
previous.next = newNode
}
this.length += 1
return true
}
//4、get方法 获取确切位置的数据
LinkedList.prototype.get = function (position) {
//越界判断
if (position < 0 || position >= this.length) return false
let current = this.head
let index = 0
while (index < position) {
current = current.next
index += 1
}
return current.data
}
//5、indexOf方法 返回查找元素的索引
LinkedList.prototype.indexOf = function (data) {
let current = this.head
let index = 0
while (current) {
if (current.data === data) {
return index
}
current = current.next
index += 1
}
return -1 //没找到
}
//6、update方法 修改确定位置的数据
LinkedList.prototype.update = function (position,data) {
if (position < 0 || position >= this.length) return false
let current = this.head
let index = 0
while (index < position) {
current = current.next
index += 1
}
current.data = data
return true
}
//7、removeNode方法 删除确切位置的节点
LinkedList.prototype.removeNode = function (position) {
if (position < 0 || position >= this.length) return null
let current = this.head
//判断是否删除第一个节点
if (position === 0) {
this.head = this.head.next
} else {
let index = 0
let previous = null
while (index < position) {
previous = current
current = current.next
index += 1
}
previous.next = current.next
}
this.length -= 1
return current.data
}
//8、remove方法 根据数据删除节点
LinkedList.prototype.remove = function (data) {
let index = this.indexOf(data)
return this.removeNode(index)
}
//9、是否为空
LinkedList.prototype.isEmpty = function () {
return this.length === 0
}
//10 size()
LinkedList.prototype.size = function () {
return this.length
}
}
//以下是测试链表功能的代码
const ll = new LinkedList()
ll.append('a')
ll.append('b')
ll.append('c')
ll.append('d')
ll.append('r')
ll.append('f')
console.log(ll.toString())
ll.insert('w',2)
console.log(ll.toString())
console.log(ll.get(2))
console.log(ll.indexOf('b'))
console.log(ll.update(1,'m'))
console.log(ll.toString())
console.log(ll.removeNode(1))
console.log(ll.toString())
console.log(ll.remove('w'))
console.log(ll.toString())
console.log(ll.isEmpty())
console.log(ll.size())
测试的结果
a b c d r f
a b w c d r f
w
1
true
a m w c d r f
m
a w c d r f
w
a c d r f
false
5
双向链表特点:1、每个数据结点中都有两个指针,分别指向直接后继和直接前驱 2、有头指针和尾指针分别指向第一个节点和最后一个节点 3,可以从前往后遍历也可以从后往前遍历
代码实现:
//封装双向链表类
function DoubleLinkedList() {
this.head = null //指向第一个节点的指针
this.tail = null //指向最后一个节点的指针
this.length = 0 //长度
//内部类: 节点类
function Node(data) {
this.prev = null //指向上一个节点的指针
this.data = data
this.next = null //指向下一个节点的指针
}
//1、append()方法 追加
DoubleLinkedList.prototype.append = function (data) {
let newNode = new Node(data) //创建新节点
//判断添加的是否为第一个节点
if (this.length === 0) { //为第一个节点
this.head = newNode //第一个节点 头指针和尾指针指向同一个节点
this.tail = newNode
} else { //不是第一个
newNode.prev = this.tail //新节点的prev指向tail
this.tail.next = newNode //双向连接
this.tail = newNode //尾指针更新为新加入的节点 始终指向最后一个节点
}
this.length += 1
}
//2、toString() 方法
DoubleLinkedList.prototype.toString = function () {
return this.backwardString()
}
//3、forwardString() 从后往前遍历
DoubleLinkedList.prototype.forwardString = function () {
let current = this.tail
let resultString = ''
while(current) {
resultString += current.data + ' '
current = current.prev
}
return resultString
}
//4、backwardString() //从前往后遍历
DoubleLinkedList.prototype.backwardString = function () {
let current = this.head
let resultString = ''
while(current) {
resultString += current.data + ' '
current = current.next
}
return resultString
}
//5、insert() 在确切位置插入节点
DoubleLinkedList.prototype.insert = function (position,data) {
//越界判断
if (position < 0 || position > this.length) {
return false
}
//创建新节点
let newNode = new Node(data)
//判断列表是否为空
if (this.length === 0) {
this.head = newNode
this.tail = newNode
} else {
//判断插入的是否为第一个节点
if (position === 0) {
this.head.prev = newNode
newNode.next = this.head
this.head = newNode
} else if (position === this.length) { //判断插入的是否为最后一个节点
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
} else {
let current = this.head
let index = 0
while (index < position) {
current = current.next
index += 1
}
//修改指针
newNode.prev = current.prev
newNode.next = current
current.prev.next = newNode
current.prev = newNode
}
}
this.length += 1
return true
}
//6、get()方法 获取确切位置的数据
DoubleLinkedList.prototype.get = function (position) {
//越界判断
if (position < 0 || position >= this.length) {
return false
}
//this.length / 2 > position 从后遍历
//this.length / 2 < position 从前遍历
if ( this.length / 2 <= position ) {
let current = this.head
let index = 0
while (index < position) {
current = current.next
index +=1
}
return current.data
} else {
let current = this.tail
let index = this.length - 1
while (index > position) {
current = current.prev
index -= 1
}
return current.data
}
}
//7、indexOf()方法 获取元素的索引
DoubleLinkedList.prototype.indexOf = function (data) {
let current = this.head
let index = 0
while (current) {
if (current.data === data) {
return index
}
current = current.next
index += 1
}
return -1
}
//8、update()方法 跟新数据
DoubleLinkedList.prototype.update = function (position,data) {
//越界判断
if ( position < 0 || position >= this.length ) {
return false
}
//this.length / 2 > position 从后遍历
//this.length / 2 < position 从前遍历
if ( this.length / 2 <= position ) {
let current = this.head
let index = 0
while (index < position) {
current = current.next
index +=1
}
current.data = data
} else {
let current = this.tail
let index = this.length - 1
while (index > position) {
current = current.prev
index -= 1
}
current.data = data
}
return true
}
//9、removeNode() 删除确切位置的节点
DoubleLinkedList.prototype.removeNode = function (position) {
//越界判断
if ( position < 0 || position >= this.length ) {
return null
}
let current = this.head
//判断链表是否只有一个节点
if (this.length === 1) {
this.head = null
this.tail = null
} else {
//删除的是否为第一个节点
if (position === 0) {
this.head.next.prev = null
this.head = this.head.next
} else if (position === this.length -1) { //删除是否为最后一个节点
current = this.tail
this.tail.prev.next = null
this.tail = this.tail.prev
} else {
//this.length / 2 > position 从后遍历
//this.length / 2 < position 从前遍历
if ( this.length / 2 <= position ) {
let index = 0
while (index < position) {
current = current.next
index +=1
}
current.next.prev = current.prev
current.prev.next = current.next
} else {
current = this.tail
let index = this.length - 1
while (index > position) {
current = current.prev
index -= 1
}
current.next.prev = current.prev
current.prev.next = current.next
}
}
}
this.length -= 1
return current.data
}
//10、remove()方法 根据元素删除节点
DoubleLinkedList.prototype.remove = function (data) {
let index = this.indexOf(data)
return this.removeNode(index)
}
//isEmpty()
DoubleLinkedList.prototype.isEmpty = function () {
return this.length === 0
}
//size()
DoubleLinkedList.prototype.size = function () {
return this.length
}
//获取第一个元素
DoubleLinkedList.prototype.getFirst = function () {
return this.head.data
}
//获取随后一个元素
DoubleLinkedList.prototype.getLast = function () {
return this.tail.data
}
}
测试以上功能的代码:
const dl = new DoubleLinkedList()
dl.append('a')
dl.append('b')
dl.append('c')
dl.append('d')
dl.append('e')
dl.append('f')
dl.append('g')
console.log(dl.toString())
console.log(dl.backwardString())
console.log(dl.forwardString())
dl.insert(0,'x')
dl.insert(2,'Y')
dl.insert('4','z')
console.log(dl.backwardString())
console.log(dl.get(8))
console.log(dl.indexOf('d'))
console.log(dl.update(9,'o'))
console.log(dl.backwardString())
console.log(dl.removeNode(6))
console.log(dl.backwardString())
console.log(dl.remove('z'))
console.log(dl.backwardString())
console.log(dl.isEmpty())
console.log(dl.size())
console.log(dl.getFirst())
console.log(dl.getLast())
测试结果:
a b c d e f g
a b c d e f g
g f e d c b a
x a Y b z c d e f g
f
6
true
x a Y b z c d e f o
d
x a Y b z c e f o
z
x a Y b c e f o
false
8
x
o