一、链表的概念
链表和数组一样,可以用来存储一系列的元素,但是链表和数组的机制完全不同
数组:要存储多个元素,数组(或列表)可能是最常用的数据结构
数组的缺点:1.数组的创建通常都需要申请一段连续的内存空间(一整块的内存),并且大小是固定的(大多数编程语言都是固定的),所以当当前数组不能满足容量需求时,需要扩容(一般情况下是申请一个更大的数组,比如2倍,然后将原来的数组中的元素复制过去)
2.在数组开头或中间插入数据的成本很高,需要大量元素的位移,比如说要在1345里面插入2,就需要把345往后都移动,即使是javascript里面的array类的底层原理也是如此,但数组通过下标值修改和获取数组性能比较高。
链表:也可以存储多个元素,且不需要连续的内存,链表的每个元素都由一个存储元素本身的节点和一个指向下一个元素的引用(指针或称为连接)组成
链表的优点:1.内存空间不是必须要连续的,可以充分利用计算机内存,实现灵活的内存动态管理。
2.链表不必在创建时就确定大小,并且大小可以无限的延伸下去。
3.链表在插入删除数组时,事件复杂度可以达到o(1).相对数组高校很多
但链表也有一些缺点:1.链表访问任何位置的元素都需要从头开始访问(无法跳过第一个元素访问任何一个元素)
2.无法直接通过下标来访问元素,需要从头一个个访问,知道找到对应的元素
二、链表的基本操作:
1.append(element):向列表尾部添加一个新的项
2.insert(position, element):向列表的特定位置插入一个新的项。
3.get(position):获取对应位置的元素
4.indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
5.update(position,element):修改某个位置的元素
6.removeAt(position):从列表的特定位置移除一项。
7.remove(element):从列表中移除一项。
8.isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false.
9.size():返回链表包含的元素个数。与数组的length属性类似。
10.toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值
1.append()方法实现
var LinkedList=function(){
var length=0;
var head=null
//辅助类:节点
var Node=function (element){
this.element=element
this.next=null
}
this.append=function(element){
var node=new Node(element)
if(length==0){
head=node
}else{
var current=head
while(current.next){
current=current.next
}
current.next=node
}
length++
}
this.getHead=function(){
return head
}
}
2.insert()方法实现
this.insert = function (position, element) {
// 判断位置
var node = new Node(element)
if (position > -1 && position < length) {
if (position == 0) {
var current = head
head = node
head.next = current
} else {
var index = 0;
var current = head;
var previous = null;
while (index < position) {
previous = current;
current = current.next
index++
}
previous.next = node
node.next = current
length++
}
}
this.getHead = function () {
return head
}
}
}
3.get()方法实现
this.get=function(position){
var current=head
var index=0
if(position>-1&&position<=length){
current=current.next
index++
}
return current.element
}
4. indexOf()方法实现
this.indexOf=function(data){
var current=head
var index=0
while(current){
if(current.element==data)
{
return index
}
current=current.next
index++
}
return -1
}
}
var l=new LinkedList()
l.append(1)
l.append(2)
l.append(3)
console.log(l.indexOf(10))
console.log(l.indexOf(1))
5. update()方法的实现
this.update=function(position,data){
var current=head
var index=0
if(position>-1&&position<=length){
while(index<position){
current=current.next;
index++
}
current.element=data
return true
}
}
6.removeAt()方法实现
this.removeAt=function(position){
var index=0;
var current=head;
var previous=null;
if(position>-1&&position<length){
if(position==0){
head=current.next
}
else{
while(index<position){
previous=current
current=current.next;
index++
}
previous.next=current.next
}
length--
}
}
var l=new LinkedList()
l.append(1)
l.append(2)
l.append(3)
7.remove()方法实现(用indexOf和removeAt实现)
this.remove=function(element){
return this.removeAt(this.indexOf(element))
}
8. isEmpty()方法的实现
this.isEmpty=function(){
if(length!=0){
return true
}
return false
}
9.size()方法实现
this.size = function () {
return length
}
10.toString()方法实现
this.toString = function () {
var current = head
var stringCurrent = ""
while (current) {
stringCurrent += current.element + "";
current = current.next
}
return stringCurrent
}
三、双向链表
1.单向列表:①只能从头遍历到尾或者从尾遍历到头(一般从头到尾)②也就是链表相连的过程是单向的③实现的原理是上一个链表中有一个指向下一个的引用.
缺点:一个节点既有向前连接的引用,也有一个向后连接的引我们可以轻松的到达下一个节点,但是回到前一个节点是很难的.还需要从头开始查找到当前元素的位置
2.双向链表:①既可以从头遍历到尾,又可以从尾遍历到头②也就是链表相连的过程是双向的.它的实现原理是一个节点既有向前连接的引用,也有一个向后连接的引用③双向链表可以有效的解决单向链表中提到的问题.
缺点:每次在插入或删除某个节点时,需要处理四个引用,而不是两个.也就是实现起来要困难一些
并且相当于单向链表,必然占用内存空间更大一些.
四、双向链表增加操作
1.forwardString():返回正向遍历的节点字符串形式
2.backString():返回反向遍历的节点字符串形式
toString、isEmpty、size、get、indexOf、update,remove方法实现和上述一样
append方法实现
function DoublyLinkedList() {
var length = 0;
var tail = null;
var head = null;
var Node = function (data) {
this.data = data;
this.next = null;
this.prev = null
}
this.append = function (data) {
var node = new Node(data)
if (length == 0) {
head = node
tail = node
} else {
tail.next = node
node.prev = tail
//让最后的尾巴节点是node
tail = node
}
length++
}
this.getHead = function () {
return head
}
}
backString和forwardString
this.backString = function () {
var current = head
var DoublyString = ""
while (current.next) {
DoublyString += current.data + ""
current = current.next
}
return DoublyString
}
this.forwardString = function () {
var current = tail
var DoublyString = ""
while (current.prev) {
DoublyString += current.data + ""
current = current.prev
}
return DoublyString
}
insert方法实现
this.insert = function (position, data) {
var index = 0
var current = head
var node = new Node(data)
if (position < -1 || position >= length) return false
if (length == 0) {
head = node
tail = node
} else {
if (position == 0) {
head = node
node.next = current
current.prev = node
} else if (position == length-1) {
node.prev = tail
tail.next = node
tail = node
} else {
while (index < position) {
index++
current = current.next
}
node.next = current
node.prev = current.prev
current.prev.next = node
current.prev = node
}
}
length++
}
removeAt方法实现
this.removeAt = function (position) {
if (position < -1 || position >= length) return false
if (length == 0) {
head = null
tail = null
} else {
if (position == 0) {
head.next.prev = null
head = head.next
} else if (position == length - 1) {
tail.prev.next = null
tail = tail.prev
} else {
var index = 0
var current = head
while (index < position) {
current = current.next
index++
}
current.prev.next = current.next
current.next.prev = current.prev
}
length--
}
}
}