复习 链表
链表被称为线性表的链式存储结构
定义
链表:可以存储多个元素,由n个结点和指针链组成一个链表,称为线性表的链式存储结构;每个节点由数据域和指针域两部分组成
单链表
单链表:由n个节点和指针链组成,每一个结点由数据域(存放元素本身的数据)和指针域(指向下一个元素的引用(或称为指针))组成
单链表的代码实现
function LinkedList() {
/**
* 头指针,用于指向链表的第一个节点的引用,
* 当链表为空的时候,head指向 null
* 最后一个节点指向下一个元素的引用为null
* */
this.head = null
/**
* Node:节点,每个节点包含自己的数据和指向下一个节点的引用
*/
function Node(ele, next = null) {
this.ele = ele
this.next = next
}
/**
* length: 链表的长度
*/
this.length = 0
//append 向列表末尾插入节点
LinkedList.prototype.append = ele => {
const newNode = new Node(ele)
if (this.length === 0) {
// 链表为空时直接插入
this.head = newNode
// newNode.next = null
} else {
// 链表不为空的时候插入到最后一个节点后面
let currentNode = this.head
// 遍历链表,找到最后一个节点
while (currentNode.next) {
currentNode = currentNode.next
}
currentNode.next = newNode
newNode.next = null
}
// 让长度加一
this.length += 1
}
//insert 在position位置插入节点
LinkedList.prototype.insert = (position, ele) => {
if (position < 0 || position > this.length) return
const newNode = new Node(ele)
let currentNode = this.head
if (position === 0) {
this.head = newNode
newNode.next = currentNode
} else {
let index = 1 // index 对应 每个节点
while (index < this.length) {
// 当前节点的前驱
const previous = currentNode
// 当前节点
currentNode = currentNode.next
// 新节点插入两者中间中间
if (position === index) {
previous.next = newNode
newNode.next = currentNode
}
index++
}
}
this.length += 1
}
// get 获取对应位置的元素
LinkedList.prototype.getNode = position => {
if (position < 0 || position >= this.length) return null
let index = 0
let currentNode = this.head
while (index < this.length) {
if (index >= 1) currentNode = currentNode.next
if (position === index) return currentNode
index++
}
}
// indexOf 返回元素在列表中的索引
LinkedList.prototype.indexOf = data => {
let index = 0
let currentNode = this.head
while (index < this.length) {
if (index >= 1) currentNode = currentNode.next
if (data === currentNode.ele) return index
index++
}
return -1
}
// update 用于修改节点数据
LinkedList.prototype.update = (position, data) => {
if (position < 0 || position >= this.length) return false
let index = 0
let currentNode = this.head
while (index < this.length) {
if (position === index) {
return currentNode.ele = data
}
index++
currentNode = currentNode.next
}
}
// removeAt 删除特定位置的节点
LinkedList.prototype.removeAt = position => {
if (position < 0 || position >= this.length) return false
let node = this.head
// 处理 删除的是第一个节点的情况
if (position === 0) {
this.head = node.next
this.length -= 1
// 返回被删除的节点
return node
}
let index = 1
while (index < this.length) {
if (position === index) {
// 当前节点(index对应位置的节点)的前一个节点
const previousNode = node
// 当前节点
const currentNode = node.next
// 下一个节点
// const nextNode = currentNode.next
// 前一个节点的指针域 next 指向 下一个节点
previousNode.next = currentNode.next
this.length -= 1
return currentNode
}
index++
node = node.next
}
}
// remove 删除特定元素节点
LinkedList.prototype.remove = data => {
let node = this.head
if (data === node.ele) {
this.head = node.next
return (this.length -= 1)
}
let index = 1
while (index < this.length) {
const previous = node
const currentNode = node.next
const nextNode = currentNode.next
if (data === currentNode.ele) {
previous.next = nextNode
return (this.length -= 1)
}
index++
}
return console.log('链表中的节点没有此数据')
}
//toString 将链表的所有节点的值拼接成一个字符串
LinkedList.prototype.toString = () => {
if (this.length === 0) return ''
let currentNode = this.head
let resultStr = ''
while (currentNode) {
resultStr += currentNode.ele + ' '
currentNode = currentNode.next
}
return resultStr
}
// size
LinkedList.prototype.size = () => {
return this.length
}
// isEmpty
LinkedList.prototype.isEmpty = () => {
return this.length === 0
}
}
双向链表
双向链表在单链表的基础上每一个结点指针域增加了一个指向前一个结点的引用
双向链表的代码实现
function BidirectionalLinkedList() {
/**
* 头指针
*/
this.head = null
/**
* 尾指针
*/
this.tail = null
function Node(data, pre = null, next = null) {
this.data = data
this.pre = pre
this.next = next
}
/**
* 链表长度
*/
this.length = 0
//append 向列表末尾插入节点
BidirectionalLinkedList.prototype.append = ele => {
const newNode = new Node(ele)
// 链表为空时直接插入
if (this.length === 0) {
this.head = newNode
this.tail = newNode
} else {
// 链表不为空时
const lastNode = this.tail
lastNode.next = newNode
newNode.pre = lastNode
this.tail = newNode
}
this.length += 1
}
// 在 position 位置插入节点
BidirectionalLinkedList.prototype.insert = (position, ele) => {
if (position < 0 || position > this.length) return
const newNode = new Node(ele)
let currentNode = this.head
if (position === 0) {
this.head = newNode
currentNode.pre = newNode
newNode.next = currentNode
return this.length += 1
}
let index = 1
while (index < this.length) {
const preNode = currentNode
currentNode = currentNode.next
if (position === index) {
preNode.next = newNode
currentNode.pre = newNode
newNode.pre = preNode
newNode.next = currentNode
return this.length += 1
}
index++
}
}
// get 获取对应位置的结点
BidirectionalLinkedList.prototype.get = position => {
if (position < 0 || position >= this.length) return null
let index = 0
let currentNode = this.head
while (index < this.length) {
if (index >= 1) currentNode = currentNode.next
if (position === index) return currentNode
index++
}
}
// indexOf 返回元素在列表中的索引
BidirectionalLinkedList.prototype.indexOf = ele => {
let index = 0
let currentNode = this.head
while (index < this.length) {
if (index >= 1) currentNode = currentNode.next
if (currentNode.data === ele) return index
index++
}
return -1
}
// update 修改某个位置的元素
BidirectionalLinkedList.prototype.update = (position, ele) => {
if (position < 0 || position >= this.length) return false
let index = 0
let currentNode = this.head
while (index < this.length) {
if (index >= 1) currentNode = currentNode.next
if (position === index) {
currentNode.data = ele
return true
}
index++
}
}
// removeAt 删除某个位置的结点
BidirectionalLinkedList.prototype.removeAt = position => {
if (position < 0 || position >= this.length) return false
let node = this.head
if (position === 0) {
if (this.length > 1) {
node.next.pre = null
this.head = node.next
} else {
this.head = this.tail = null
}
this.length -= 1
return true
}
let index = 1
while (index < this.length) {
if (position === index) {
// 当前index索引位置结点的前驱
const preNode = node
// const currentNode = node.next
// 当前index索引位置结点的后继
const nextNode = node.next.next
// 如果是最后一个结点,尾结点前移
if (position === this.length - 1) {
preNode.next = nextNode
this.tail = preNode
} else {
nextNode.pre = preNode
preNode.next = nextNode
}
this.length -= 1
return true
}
index++
node = node.next
}
}
// remove 删除特定元素节点
BidirectionalLinkedList.prototype.remove = data => {
let node = this.head
if (node.data === data) {
if (this.length > 1) {
const newHeadNode = node.next
newHeadNode.pre = null
this.head = newHeadNode
} else {
this.head = this.tail = null
}
this.length -= 1
return true
}
let index = 1
while (index < this.length) {
const preNode = node
const currentNode = node.next
const nextNode = currentNode.next
if (currentNode.data === data) {
if (index === this.length - 1) {
preNode.next = nextNode
this.tail = preNode
} else {
nextNode.pre = preNode
preNode.next = nextNode
}
this.length -= 1
return true
}
node = node.next
index++
}
return false
}
//toString 将链表的所有节点的值拼接成一个字符串
BidirectionalLinkedList.prototype.toString = () => {
if (this.length === 0) return ''
let currentNode = this.head
let resultStr = ''
while (currentNode) {
resultStr += currentNode.data + ' '
currentNode = currentNode.next
}
return resultStr
}
// forwardString 返回正向遍历的结点字符串形式
BidirectionalLinkedList.prototype.forwardString = () => this.toString()
// backwordString 返回反向遍历的结点字符串形式
BidirectionalLinkedList.prototype.backwordString = () => {
let lastNode = this.tail
let resultStr = ''
while (lastNode) {
resultStr += lastNode.data + ' '
lastNode = lastNode.pre
}
return resultStr
}
BidirectionalLinkedList.prototype.size = () => this.length
BidirectionalLinkedList.prototype.isEmpty = () => this.length === 0
}
与数组的区别
- 数组是线性表的顺序存储结构,链表是数组的链式存储结构:数组的内存空间必须是连续的,知道上一个元素的内存地址就可以推导出其他元素的内存地址,而链表的内存空间不是必须的连续,逻辑上连续但物理上不一定连续,可以实现灵活的内存动态管理
- 数组可以通过索引号直接拿到元素,而链表找一个元素必须从头开始找,无法通过下标值直接访问某个元素
- 链表在插入和删除时效率非常高O(1),而数组在插入和删除时效率比较低,因为需要移动数组里面元素的位置
栈、队列、链表、数组区别
待补充