浅析JavaScript中的链表

链表结构的定义

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和指向下一个元素的引用(也称为指针或链接)组成。下面是单向链表元素节点的定义:

class Node {
	constructor (element) {
		this.element = element
		this.next = null
	}
}

以下是双向链表元素节点定义:

class DoublyNode {
	constructor (element) {
		this.element = element
		this.next = null
		this.prev = null
	}
}

单链表

1.首先,我们来创建一个单链表类:

class LinkedList {
	constructor (equalsFn = defaultEquals) { // 这里需要设置一个比较函数,用于查找元素时的判定
		this.count = 0 // 链表元素总数
		this.head = null // 头结点
	}
}

2.单链表的基本操作如下:

  • push(element)-向链表尾部添加一个新元素(也可以成为add之类的名称)
  • insert(element, position)-向链表的特定位置插入一个新元素。
  • getElementAt(index)-返回链表中特定位置的元素。如果查找不到,返回undefined。
  • remove(element)-从链表中移除一个元素。
  • indexOf(element)-返回元素在链表中的索引。如果链表中没有该元素返回-1。
  • removeAt(position)-从链表的特定位置移除一个元素。
  • isEmpty()-如果链表中不包含任何元素,返回true,否则返回false。
  • size()-返回链表中包含元素的总数。
  • toString()-返回表示整个链表的字符串。

下面我们就一个个实现:

 class LinkedList {
 	constructor (equalsFn = defaultEquals) {
 		this.count = 0
 		this.head = null
 	}
 	push (element) { // 向链表尾部添加元素
 		const node = new Node(element)
 		let current = this.head
 		if (current == null) { // 空链表
 			this.head = node // 这里不要写成current = node,因为current是this.head的副本
 		} else {
 			while (current.next != null) {
 				current = current.next
 			}
 			current.next = node
 		}
 		this.count++
 	}
 	removeAt(index) { // 从链表中移除指定下标的元素
 		if (index >= 0 && index <= this.count) { // 越界检测
 			let current = this.head
 			if (index == 0) {
				this.head = current.next
			} else {
				let previous = null
				for (let i = 0;i < index; i++) { // 迭代找到index的前一个节点和当前节点
					previous = current
					current = current.next
				}
				previous.next = current.next // 当index的前一个节点指向当前节点的后面节点,可能为null
			}
			this.count--
			return current.element
 		}
 		return undefined
 	}
 	getElementAt (index) {
		if (index >= 0 && index <= this.count) {
			let node = this.head
			for (let i = 0; i < index && node != null; i++) { // 迭代找到元素
				node = node.next
			}
			return node
		}
		return undefined
	}
	insert (element, index) {
		if (index >= 0 && index <= this.count) {
			const node = new Node(element)
			if (index == 0) {
				const current = this.head
				node.next = current
				this.head = node
			} else {
				const previous = this.getElementAt(index - 1)
				const current = previous.next
				node.next = current
				previous.next = node
			}
			this.count++
			return true
		}
		return false
	}
	indexOf (element) {
		let current = this.head
		let i = 0
		while(current != null) {
			if (this.equalsFn(element, current.element) { // 这里的比较函数是可以自定义
				return i
			}
			current = current.next
			i++
		}
		return -1
	}
	remove (element) {
		cosnt index = this.indexOf(element)
		return this.removeAt(index)
	}
	isEmpty () {
		return this.count == 0
	}
	size () {
		return this.count
	}
	toString() {
		if (this.head == null) {
			return ''
		}
		/* 这一段也可以改造-下面一段代码就是改造的
		let objString = `${this.head.element}`
		let current = this.head.next
		while (current != null) {
			objString = `${objString}, ${current.element}`
			current = current.next	
		}
		*/
		let objString = ''
		let current = this.head
		while (current != null ) {
			objString += `${current.element}`
			current = current.next
			if (current != null) {
				objString += ','
			}
		}
		return objString
	}
 }

双向链表

下面简单说下双向链表中的一些方法:

// 1.插入:
insert(element, index) {
	if (index >= 0 && index <= this.count) {
		const node = new DoublyNode (element)
		let current = this.head
		if (index == 0) { // 如果是首元素
			if (this.head == null) { // 空表,直接设置head、tail
				this.head = node
				this.tail = node
			} else { // 非空表
				node.next = this.head
				current.prev = node
				this.head = node
			}
		} else if (index == this.count) { // 如果是尾元素
			current = this.tail
			current.next = node
			node.prev = current
			this.tail = node
		} else { // 其他位置的元素
			const previous = this.getElement(index - 1)
			current = previous.next
			node.next = current
			previous.next = node
			current.prev = node
			node.prev = previous
		}
		this.count++
		return true
	}
	return false
}
// 2.删除
removeAt(index) {
	if (index >= 0 && index <= this.count) { // 越界检测
		let current = this.head // 设置为首元素
		if (index == 0) { // 首元素删除
			this.head = current.next
			if (this.count == 1) { // 如果是单元素,就设置尾指针为null
				this.tail = null
			} else { // 非单元素链表,设置删除后的元素的前节点(已经设置为首元素)为null
				this.head.prev = null
			}
		} else if (index == this.count - 1) { // 如果是尾元素
			current = this.tail // 获取尾元素
			this.tail = current.prev //将尾结点设置为尾元素的前一个节点
			this.tail.next = null // 尾元素置空
		} else { // 其他位置元素
			current = this.getElementAt(index)
			const previous = current.prev
			previous.next = current.next
			current.next.prev = previous
		}
		this.count--
		return current.element
	}
	return undefined
}

循环链表

这里我们介绍下循环单链表的两个方法:

// 1.插入元素
insert(element, index) {
	if (index >= 0 && index <= this.count) {
		const node = new Node(element)
		let current = this.head
		if (index == 0) {
			if (this.head == null) {
				this.head = node
				node.next = this.head // 这里表示元素的next指针指向了头元素,形成了闭环
			} else {
				node.next = current
				current = this.getElementAt(this.size() - 1) // 获取尾元素
				this.head = node
				current.next = this.head // 更新尾元素的指针,形成闭环
			}
		} else {
			const previous = this.getElementAt(index - 1)
			node.next = previous.next
			previous.next = node
		}
		this.count++
		return true
	}
	return false
}

// 2.删除元素
removeAt(index) {
	if (index >= 0 && index <= this.count) {
		let current = this.head
		if (index == 0) {
			if (this.size() == 1) {
				this.head = null
			} else {
				const removed = this.head
				current = this.getElementAt(this.size())
				this.head = this.head.next
				current.next = this.head
				current = removed
			}
		} else {
			const previous = this.getElementAt(index - 1)
			current = previous.next
			previous.next = current.next
		}
		this.count--
		return current.element
	}
	return undefined
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值