1.概述
链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同。
先来说说数组:
要存储多个元素,数组可能是常用的数据结构,但它也有许多缺点,如:
- 需要申请一段连续存储的内存空间,在大多数语言中还需要预先限定大小,所以当数组不能满足容量需求时还需要扩容。
- 在数组开头或中间位置插入数据成本很高,需要进行大量元素的位移。
所以要存储多个元素,还有一个选择就是链表
相对于数组,它有以下优点:
- 内存空间不必是连续的,可以充分利用计算机的内存,实现灵活的内存动态管理。
- 不需要创建时就给定大小,可以动态添加删除,甚至无限延申下去
- 链表在插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高很多
当然相对数组,它也有缺点:
- 每次访问,它只能从头节点开始访问
- 不能通过下标直接访问,需要从头一个个访问
2.单向链表
在链表中,常见的有单向链表,顾名思义,它是单向的,如下:
可见,头节点指向第一个节点,每个节点含有data、next属性,data内保存了节点的数据,next保存了指向的下一个节点的地址。
封装单链表
<script>
function linkedList() {
function Node(data) {
this.data = data;
this.next = null;
}
this.head = null;
this.length = 0;
// 1.在链表末尾添加节点
linkedList.prototype.appendlist = function (data) {
let newNode = new Node(data);
if (this.length === 0) {
this.head = newNode
console.log(this.head);
}
else {
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
this.length += 1;
}
// 2.toString
linkedList.prototype.toSting = function () {
let str = '';
let current = this.head;
while (current) {
str = str + current.data + ' ';
current = current.next;
}
return str;
}
// 3.在某个位置插入节点
linkedList.prototype.insert = function (position, data) {
if (position < 0 || position > this.length + 1) return false;
else {
let newNode = new Node(data);
let num = 1;
let current = this.head;
while (current) {
if (position === 1) {
newNode.next = this.head;
this.head = newNode;
}
else if (position - 1 === num) {
let temp = current.next;
current.next = newNode;
newNode.next = temp;
}
num += 1;
current = current.next;
}
this.length += 1;
}
}
// 4.更换某个位置的节点
linkedList.prototype.update = function (position, data) {
let newNode = new Node(data)
let num = 1;
let current = this.head;
if (position < 0 || position > this.length + 1) return false;
while (current) {
if (num === position - 1) {
let temp = current.next;
let tempnext = temp.next;
current.next = newNode;
newNode.next = tempnext;
}
current = current.next;
num += 1;
}
}
// 5.查看某个位置的节点
linkedList.prototype.get = function (position) {
if (position < 0 || position > this.length + 1) return false;
let num = 1;
let current = this.head;
while (current) {
if (num === position)
return current
current = current.next;
num += 1;
}
}
// 6.是否包含某个节点,若包含返回是第几个,不包含返回-1
linkedList.prototype.indexOf = function (data) {
let num = 1;
let current = this.head;
let ishave = false;
while (current) {
if (current.data === data) {
ishave = true;
return num;
}
current = current.next;
num += 1;
}
if (!ishave) return -1;
}
// 7.根据位置删除
linkedList.prototype.removeAt = function (position) {
if (position < 0 || position > this.length + 1) return false;
let num = 1;
let current = this.head;
while (current) {
if (num === position - 1) {
let temp = current.next.next;
current.next = temp;
}
current = current.next;
num += 1;
}
this.length -= 1;
}
// 8.根据数据删除
linkedList.prototype.remove = function (data) {
let num = 1;
let current = this.head;
let before = null;
while (current) {
if (current.data === data) {
let temp = current.next;
if (num === 1) {
this.head = temp;
}
else {
before.next = temp
}
}
if (current === this.head) {
before = this.head
}
else {
before = before.next;
}
current = current.next;
num = num + 1;
}
this.length -= 1;
}
// 9.是否为空
linkedList.prototype.isEmpty = function () {
return this.length === 0;
}
// 10.返回长度
linkedList.prototype.size = function () {
return this.length;
}
}
</script>
3.双向链表
除了单向链表,常用的还有双向链表,顾名思义,它是双向的,如下:
可见,头节点指向第一个节点,每个节点含有data、prev、next属性,data内保存了节点的数据,pre 保存了指向的上一个节点的地址,next保存了指向的下一个节点的地址。
封装双向链表
<script>
function DoublyLinkedList() {
// 内部类
function Node(data) {
this.data = data;
this.prev = null;
this.next = null;
}
this.head = null;
this.tail = null;
this.length = 0;
// 添加节点
DoublyLinkedList.prototype.appendList = function (data) {
let NewNode = new Node(data);
if (this.length === 0) {
this.head = NewNode;
this.tail = NewNode;
}
else {
let current = this.head;
while (current.next) {
current = current.next
}
current.next = NewNode
current.next.prev = current
this.tail = NewNode
}
this.length += 1;
}
// 1.toString
DoublyLinkedList.prototype.toString = function () {
return this.forwarString();
}
// 2.forwardString 正向遍历
DoublyLinkedList.prototype.forwarString = function () {
let current = this.head;
let str = '';
while (current) {
str = str + current.data + ' ';
current = current.next
}
return str;
}
// 3.backwordString 反向遍历
DoublyLinkedList.prototype.backwordString = function () {
let current = this.tail;
let str = '';
while (current) {
str = str + current.data + ' ';
current = current.prev
}
return str;
}
// 4.insert插入
DoublyLinkedList.prototype.insert = function (position, data) {
let newNode = new Node(data);
// 1.范围限制
if (position < 0 || position > this.length) return false;
let current = this.index;
if (position === 0) {
this.head.prev = newNode;
this.head = newNode
this.tail = newNode
}
else if (position === this.length) {
this.tail.next = newNode;
let temp = this.tail;
this.tail = newNode
this.tail.prev = temp;
}
else {
let index = 0;
let current = this.head;
while (index++ < position) {
current = current.next;
}
current.prev.next = newNode
newNode.prev = current.prev
current.prev = newNode;
newNode.next = current
}
this.length += 1;
return true;
}
// 5.get根据位置查数据
DoublyLinkedList.prototype.getNode = function (position) {
if (position < 0 || position >= this.length) return false;
if (position <= (this.lenth / 2 - 1)) {
let current = this.head;
let index = 0;
while (index++ < position) {
current = current.next;
}
return current
}
else {
let current = this.tail;
let index = this.length - 1;
while (index-- > position) {
current = current.prev
}
return current
}
}
// 6.indexof查找元素
DoublyLinkedList.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;
}
// 7.update更新数据
DoublyLinkedList.prototype.update = function (position, data) {
if (position < 0 || position >= this.length) return false;
let index = 0;
let current = this.head;
while (index++ < position) {
current = current.next;
}
current.data = data;
return true;
}
// 8.removeAt根据位置删除
DoublyLinkedList.prototype.removeAt = function (position) {
if (position < 0 || position >= this.length) return false;
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) {
this.tail.prev.next = null;
this.tail = this.tail.prev;
}
else {
current = this.getNode(position);
current.prev.next = current.next;
current.next.prev = current.prev;
current.prev = null;
current.next = null;
}
}
this.length -= 1;
return current.data
}
// 9.remove根据元素名删除
DoublyLinkedList.prototype.remove = function (data) {
let position = this.indexOf(data)
console.log(position);
this.removeAt(position)
}
}
</script>