此数据结构算法知识点系列笔记均是看coderwhy老师视频整理得出!!!
链表和数组一样,用于**存储一系列的元素**,但是链表和数组的**实现机制完全不同**
数组存在的缺点
要存储多个元素,数组(或称为列表)可能是最常用的数据结构。
我们之前说过,几乎每一种编程语言都有默认实现数组结构.。
但数组也有很多缺点:
- 数组的创建通常需要申请一段**连续的内存空间(一整块的内存)**并且大小是固定的(大多数编程语言数组都是固定的),所以当当前数组不能满足容量需求时,需要扩容.(一般情况下是申请一个更大的数组,比如2倍.然后将原数组中的元素复制过去)
- 而且在数组开头或中间位置插入数据的成本很高,需要进行大量元素的位移.
- 尽管我们已经学过的JavaScript的Array类方法可以帮我们做这些事,但背后的原理依然是这样。
链表的特点及优势、劣势
特点
- 链表中的元素在内存中**不必是连续的空间**
- 链表的每个元素由一个存储**元素本身的节点**和一个**指向下一个元素的引用**组成
优势
- 内存空间不必须是连续的,灵活的内存动态管理
- 在创建时不必确定大小,可以**无限的延伸**
- 在**插入和删除**数据时,**时间复杂度**可以达到O(1),相对数组效率高很多。但是如果是通过下标值进行相关操作,数组的效率时非常高的。
劣势
- 链表访问任何一个位置的元素时,都需要**从头开始访问**.(无法跳过第一个元素访问任何一个元素).
- 无法通过下标直接访问元素,需要从头一个个访问,直到找到对应的元素.
链表到底是什么?
链表的常见操作
- >+ append(element):向列表尾部添加一个新的项
- >+ insert(position, element):向列表的特定位置插入一个新的项。
- >+ get(position):获取对应位置的元素
- >+ indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
- >+ update(position):修改某个位置的元素
- >+ removeAt(position):从列表的特定位置移除一项。
- >+ remove(element):从列表中移除一项。
- >+ isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
- >+ size():返回链表包含的元素个数。与数组的length属性类似。
- >+ toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。
单向链表的封装
// 单向链表
// 封装链表类
function LinkedList() {
// 内部类:节点类
function Node(data) {
this.data = data;
this.next = null;
}
// 属性
this.head = null;
this.length = 0;
// 1. append 在尾部添加一项
LinkedList.prototype.append = function (data) {
// 创建新节点
var newNode = new Node(data);
// 判断是否添加的是第一个节点
if (this.length === 0) {
this.head = newNode;
} else {
var current = this.head;
// 找到最后一个节点
while (current.next) {
current = current.next;
}
// 最后节点指向新节点
current.next = newNode;
}
this.length += 1;
};
//2. toString
LinkedList.prototype.toString = function () {
var current = this.head;
var listString = "";
while (current) {
listString += current.data + " ";
current = current.next;
}
return listString;
};
//3. insert
LinkedList.prototype.insert = function (position, data) {
//对position进行越界判断
if (position < 0 || position > this.length) return false;
var newNode = new Node(data);
// 判断插入的位置是否为第一个
if (position === 0) {
newNode.data = data;
newNode.next = this.head;
this.head = newNode;
} else {
var index = 0;
var current = this.head;
var prev = null;
while (index++ < position) {
prev = current;
current = current.next;
}
newNode.next = current.next;
prev = newNode;
}
};
// 4.get
LinkedList.prototype.get = function (position) {
if (position < 0 || position >= this.length) return false;
var current = this.head;
var index = 0;
while (index < position) {
current = current.next;
index++;
}
return current.data;
};
//5.updata
LinkedList.prototype.update = function (position, newData) {
if (position < 0 || position >= this.length) return false;
var current = this.head;
var index = 0;
while (index++ < position) {
current = current.next;
}
current.data = newData;
return true;
};
//6.indexOf
LinkedList.prototype.indexOf = function (data) {
var current = this.head;
var index = 0;
while (current) {
if (current.data == data) {
return index;
}
current = current.next;
index += 1;
}
return -1;
};
//7.removeAt
LinkedList.prototype.removeAt = function (position) {
// 越界判断
if (position < 0 || position >= this.length) return null;
var current = this.head;
if (position == 0) {
this.head = current.next;
} else {
var index = 0;
var prev = null;
while (index++ < position) {
prev = current;
current = current.next;
}
prev.next = current.next;
}
this.length -= 1;
return current.data;
};
//8.remove
LinkedList.prototype.remove = function (data) {
var position = this.indexOf(data);
return this.removeAt(position);
};
//9.isEmpty
LinkedList.prototype.isEmpty = function () {
return this.length == 0;
};
//10.size
LinkedList.prototype.size = function () {
return this.length;
};
}
双向链表
双向链表的特点
- 可以使用一个head和一个tail分别指向头部和尾部的节点
- 每个节点都由三部分组成:前一个节点的指针(prev)/保存的元素(item)/后一个节点的指针(next)
- 双向链表的第一个节点的prev是null
- 双向链表的最后的节点的next是null
双向链表的常用方法
- append(element):向列表尾部添加一个新的项
- insert(position, element):向列表的特定位置插入一个新的项。
- get(position):获取对应位置的元素
- indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
- update(position):修改某个位置的元素
- removeAt(position):从列表的特定位置移除一项。
- remove(element):从列表中移除一项。
- isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
- size():返回链表包含的元素个数。与数组的length属性类似。
- toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。
- forwardString(): 返回正向遍历节点字符串形式
- backwordString():返回反向遍历的节点字符串形式
双向链表的封装
// 双向链表类的封装、
function DoublyLinkedList() {
// 内部节点类
function Node(data) {
this.data = data;
this.prev = null;
this.next = null;
}
// 属性
this.head = null;
this.tail = null;
this.length = 0;
// 1.append
DoublyLinkedList.prototype.append = function (data) {
var newNode = new Node(data);
// 判断链表中是否有节点
if (this.length == 0) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode;
}
this.length += 1;
};
// 2.toString
DoublyLinkedList.prototype.toString = function () {
return this.backwardString();
};
// 3.forwardString 向前遍历组成字符串
DoublyLinkedList.prototype.forwardString = function () {
var current = this.tail;
var resultString = "";
while (current) {
resultString += current.data + " ";
current = current.prev;
}
return resultString;
};
// 4.backwardString 向后遍历组成字符串
DoublyLinkedList.prototype.backwardString = function () {
var current = this.head;
var resultString = "";
while (current) {
resultString += current.data + " ";
current = current.next;
}
return resultString;
};
// 5.insert
DoublyLinkedList.prototype.insert = function (position, data) {
if (position < 0 || position > this.length) return false;
var 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 {
var current = this.head;
var index = 0;
while (index++ < position) {
current = current.next;
}
newNode.prev = current.prev;
newNode.next = current;
current.prev.next = newNode;
current.prev = newNode;
}
}
this.length += 1;
return true;
};
// 6.get
DoublyLinkedList.prototype.get = function (position) {
if (position < 0 || position >= this.length) return null;
if (this.length / 2 > position) {
var index = 0;
var current = this.head;
while (index++ < position) {
current = current.next;
}
return current.data;
} else {
var index = this.length - 1;
var current = this.tail;
while (index-- > position) {
current = current.prev;
}
return current.data;
}
};
//7.indexOf
DoublyLinkedList.prototype.indexOf = function (data) {
var current = this.head;
var index = 0;
while (current) {
if (current.data == data) {
return index;
}
current = current.next;
index++;
}
return -1;
};
// 8.update
DoublyLinkedList.prototype.update = function (position, data) {
if (position < 0 || position >= this.length) return false;
var current = this.head;
var index = 0;
while (index++ < position) {
current = current.next;
}
current.data = data;
return true;
};
// 9.removeAt()
DoublyLinkedList.prototype.removeAt = function (position) {
if (position < 0 || position >= this.length) return null;
var current = this.head;
if (this.length == 1) {
this.head = 0;
this.tail = 0;
} 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 {
var index = 0;
while (index++ < position) {
current = current.next;
}
current.prev.next = current.next;
current.next.prev = current.prev;
}
}
this.length -= 1;
return current.data;
};
// 10.remove
DoublyLinkedList.prototype.remove = function (data) {
var index = this.indexOf(data);
return this.removeAt(index);
};
// 11.isEmpty
DoublyLinkedList.prototype.isEmpty = function () {
return this.length == 0;
};
// 12.size
DoublyLinkedList.prototype.size = function () {
return this.length;
};
// 13.getHead
DoublyLinkedList.prototype.getHead = function () {
return this.head.data;
};
// 14.getTail
DoublyLinkedList.prototype.getTail = function () {
return this.tail.data;
};
}
var list = new DoublyLinkedList();
list.append("1111");
list.append("222");
alert(list);
// console.log(list);
alert(list.backwardString());
alert(list.forwardString());
list.insert(0, "第一位插入");
list.insert(3, "末尾插入");
list.insert(1, "中间插入");
alert(list);
alert(list.get(0));
alert(list.indexOf("1111"));
alert(list.indexOf("aaaa"));