二、链表(Linked List)
2.1单向链表简介
链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同。链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有的语言称为指针或连接)组成。类似于火车头,一节车厢载着乘客(数据),通过节点连接另一节车厢。
head属性指向链表的第一个节点;
链表中的最后一个节点指向null;
当链表中一个节点也没有的时候,head直接指向null;
数组存在的缺点:
数组的创建通常需要申请一段连续的内存空间(一整块内存),并且大小是固定的。所以当原数组不能满足容量需求时,需要扩容(一般情况下是申请一个更大的数组,比如2倍,然后将原数组中的元素复制过去)。
在数组的开头或中间位置插入数据的成本很高,需要进行大量元素的位移。
链表的优势:
- 链表中的元素在内存中不必是连续的空间,可以充分利用计算机的内存,实现灵活的内存动态管理。
- 链表不必在创建时就确定大小,并且大小可以无限地延伸下去。
- 链表在插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高很多。
链表的缺点:
- 链表访问任何一个位置的元素时,都需要从头开始访问(无法跳过第一个元素访问任何一个元素)。
- 无法通过下标值直接访问元素,需要从头开始一个个访问,直到找到对应的元素。
- 虽然可以轻松地到达下一个节点,但是回到前一个节点是很难的。
链表中的常见操作:
- append(element):向链表尾部添加一个新的项;
- insert(position,element):向链表的特定位置插入一个新的项;
- get(position):获取对应位置的元素;
- indexOf(element):返回元素在链表中的索引。如果链表中没有该元素就返回-1;
- update(position,element):修改某个位置的元素;
- removeAt(position):从链表的特定位置移除一项;
- remove(element):从链表中移除一项;
- isEmpty():如果链表中不包含任何元素,返回trun,如果链表长度大于0则返回false;
- size():返回链表包含的元素个数,与数组的length属性类似;
- toString():由于链表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值;
实现
/**
*
* 单向链表封装
*
* */
function LinkList(){
function Node(data){
this.data = data;
this.next = null;
}
this.head = null;
this.length = 0;
//添加数据
LinkList.prototype.append = function (data){
//创建新节点
let newNode = new Node(data);
if (this.head === null){
this.head = newNode;
}else{
let current = this.head;
while (current.next){
current = current.next;
}
current.next = newNode;
}
this.length += 1;
}
//想指定位置插入数据
LinkList.prototype.insert = function (data,index){
let newNode = new Node(data);
let current = this.head;
if (index === 0){
//插入首位时
this.head = newNode;
newNode.next = current;
}else if (index <= this.length && index >= 0 ){
for (let i = 1; i < index; i++) {
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
}else {
console.log("该节点不存在")
return -1;
}
this.length += 1;
}
//根据下标获得数据
LinkList.prototype.get = function (index){
if (index < 0 && index >= this.length){
return -1;
}
let current = this.head;
for (let i = 0; i < index; i++) {
current = current.next;
}
return current.data;
}
//获得元素下标
LinkList.prototype.indexOf = function (data){
let current = this.head;
let index = 0;
let is = true;
while (current.data !== data){
index++;
current = current.next;
if (current === null){
is = false;
break;
}
}
if (!is){
return false;
}
return index;
}
//更新数据(根据下标)
LinkList.prototype.update = function (newData,index){
let current = this.head;
if (index <= this.length && index >= 0 ){
for (let i = 0; i < index; i++) {
current = current.next;
}
current.data = newData;
}else {
console.log("该节点不存在")
return -1;
}
}
//移除数据(根据指定下标)
LinkList.prototype.removeAt = function (index){
if (this.isEmpty()){
console.log("无数据可删")
return -1;
}
let current = this.head;
let last = null;
let res = null;
if (index === 0){
res = current.data;
index === this.length -1 ? this.head = null : this.head = current.next;
this.length--;
return res;
}else if (index > 0 && index <= this.length-1){
for (let i = 0; i < index; i++) {
if (i === index -1){
last = current;
}
current = current.next;
}
res = current.data;
index === this.length -1 ? last.next = null : last.next = current.next;
this.length--;
return res;
}else {
console.log("该下标不存在")
return -1;
}
}
//移除数据(根据指定元素)
LinkList.prototype.remove = function (data){
return this.removeAt(this.indexOf(data));
}
//isEmpty
LinkList.prototype.isEmpty = function (){
return this.length === 0;
}
//toString
LinkList.prototype.toString = function (){
let current = this.head;
let str = '';
while (current){
str += current.data+" ";
current = current.next;//指针下移
}
return str;
}
}
// Module.exports = LinkList
exports.linklist = LinkList;
//
// let link = new LinkList();
//
// link.append('yxh');
// link.append('cjt');
// link.append('wcc');
// link.append('wwj');
//
//
// console.log(link.toString())
//
//
// link.insert("666",0);
// link.insert("888",0);
//
// console.log(link.get(0))
// console.log(link.toString())
//
//
// console.log(link.indexOf("yxh"));
// //
// console.log(link.toString())
//
//
//
// link.update("888",-1);
// link.removeAt(4);
//
// link.remove("666");
// console.log(link.toString())
//
// console.log(link.remove("yxh"));
//
// console.log(link.toString())
//
// console.log(link.removeAt(1));
//
// console.log(link.toString())
//
//
2.2双向链表简介
双向链表:既可以从头遍历到尾,又可以从尾遍历到头。也就是说链表连接的过程是双向的,它的实现原理是:一个节点既有向前连接的引用,也有一个向后连接的引用。
双向链表的缺点:
- 每次在插入或删除某个节点时,都需要处理四个引用,而不是两个,实现起来会困难些;
- 相对于单向链表,所占内存空间更大一些;
- 但是,相对于双向链表的便利性而言,这些缺点微不足道。
双向链表的结构:
- 双向链表不仅有head指针指向第一个节点,而且有tail指针指向最后一个节点;
- 每一个节点由三部分组成:item储存数据、prev指向前一个节点、next指向后一个节点;
- 双向链表的第一个节点的prev指向null;
- 双向链表的最后一个节点的next指向null;
双向链表常见的操作(方法):
- append(element):向链表尾部添加一个新的项;
- inset(position,element):向链表的特定位置插入一个新的项;
- get(element):获取对应位置的元素;
- indexOf(element):返回元素在链表中的索引,如果链表中没有元素就返回-1;
- update(position,element):修改某个位置的元素;
- removeAt(position):从链表的特定位置移除一项;
- isEmpty():如果链表中不包含任何元素,返回trun,如果链表长度大于0则返回false;
- size():返回链表包含的元素个数,与数组的length属性类似;
- toString():由于链表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值;
- forwardString():返回正向遍历节点字符串形式;
- backwordString():返回反向遍历的节点的字符串形式;
实现
注意个别方法过于简单,未实现
/**
*
* 双向链表
* */
function dbLinkList(){
function Node(data){
this.prev = null;
this.data = data;
this.next = null;
}
this.head = null;
this.tail = null;
this.length = 0;
dbLinkList.prototype.append = 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;
}
newNode.prev = current;
current.next = newNode;
this.tail = newNode;
}
this.length += 1;
return true;
}
dbLinkList.prototype.insert = function (index,data){
if (index < 0 || index > this.length) return false;
if (index === this.length) return !this.append(data);
let newNode = new Node(data);
if (index === 0){
this.head.prev = newNode;
newNode.next = this.head;
this.head = newNode;
this.length += 1;
return true;
}
let curr = this.head;
let n = 0;
while (n++ < index){
curr = curr.next;
}
newNode.next = curr;
newNode.prev = curr.prev;
newNode.prev.next = newNode;
newNode.next.prev = newNode;
this.length += 1;
return true;
}
dbLinkList.prototype.toString = function (){
let curr = this.head;
let str = '';
while (curr){
str += curr.data+" ";
curr = curr.next;
}
return str;
}
}
let dbLinkList1 = new dbLinkList();
dbLinkList1.append("yxh");
dbLinkList1.append(66);
dbLinkList1.append("true");
dbLinkList1.append(false);
console.log(dbLinkList1.toString(),"长度:"+dbLinkList1.length);
console.log("插入999",dbLinkList1.insert(3,999));
console.log("插入666",dbLinkList1.insert(4,666));
// console.log(dbLinkList1.head);
console.log(dbLinkList1.toString(),"长度:"+dbLinkList1.length);