一、链表概念
链表类似于火车:有一个火车头,火车头会连接一个节点,节点上有乘客(类似于数据),并且这个节点会连接下一个节点,依次类推)
二、数组与链表的区别
相同点:数组和链表都可以存储多个元素
不同点:
-
数组的创建通常需要申请
一段连续的内存空间
(一整块的内存),并且大小是固定的(大多数编程语言数组都是固定的),所以当当前数组不能满足容量需求
时,就需要扩容
(一般情况下是申请一个更大的数组比如2倍,然后将原数组的元素复制过去) -
而且在数组开头或中间插入数据的成本很高,需要进行大量元素的位移
-
链表中的元素在内存中
不必是连续的空间
-
链表的每个元素有一个存储
元素本身的节点
和一个指向下一个元素的引用
(有些语言称为指针或者连接)组成
相对于数组,链表的优点:
1) 内存空间不必是连续的,可以充分利用计算机的内存,实现灵活的内存动态管理。
2) 链表不必在创建时就确定大小,并且大小可以无限的延伸下去。
3)链表在插入和删除时,相对数组效率高很多。
相对于数组,链表的缺点:
1)链表访问任何一个位置的元素时,都需要从头开始访问(无法跳过一个元素访问任何一个元素)。
2)无法通过下标直接访问元素,需要从头一个个访问,直到找到对应的元素。
三、单链表的封装
首先,我们封装一个LinkedList类,来表示链表结构,并在LinkedList类中保存两个属性,一个是链表中的头结点,一个是链表的长度。然后在类里面再封装一个内部类Node,用于封装每一个节点上的信息,如下所示
//封装链表类
function LinkedList(){
//内部的类:节点类
function Node(data){
this.data=data
this.next=null
}
//
this.head = null
this.length = 0
}
四、单链表的主要操作
-
append(element:向列表尾部添加一个项
-
insert(position,element):向列表的特定位置插入一个项
-
get(position):获取对应位置的元素
-
indexOf(element):返回元素在列表中的索引,如果列表中没有该元素则返回-1
-
update(position,ele):修改某个位置的元素
-
removeAt(position):从列表的指定位置移除一项
-
remove(element):从列表中移除一项
-
isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0返回false
-
size():返回链表包含的元素个数,与数组的length属性相关
-
toString():由于列表项用到了Node类,需要重写继承自JavaScript对象默认的toString方法,让其输出元素的值
下面我们将逐个实现
1、append(element:向列表尾部添加一个项
向链表尾部追加节点有两种情况:
- 链表本身为空,新添加的数据是唯一的节点。
- 链表不为空,需要向其他节点后面追加节点。
如果添加的节点是第一个节点,我们只需要将头节点指向第一个节点。如果添加的节点是第二个节点,则必须找到第一个节点,并需要让第一个节点的next指向第二个节点,依次循环,直到找到尾节点。
//封装链表类
function LinkedList(){
//内部的类:节点类
function Node(data){
this.data=data
this.next=null
}
//属性
this.head = null
this.length = 0
// 1.追加方法
LinkedList.prototype.append = function(data){
//1.创建新的节点
var newNode = new Node(data)
//2.判断是否添加的是第一个节点
if(this.length==0){ //2.1 是第一个节点
this.head = newNode
}else{ //2.2不是第一个节点
//找到最后一个节点
var current = this.head
while(current.next){
current = current.next
}
//最后节点的next指向新的节点
current.next = newNode
}
//3.length+1
this.length +=1
}
//2.tostring方法
LinkedList.prototype.toString = function(){
//1.定义变量
var current = this.head
var listString=''
//2.循环获取一个个的节点
while(current){
listString += current.data + " "
current = current.next
}
return listString
}
//3.insert方法
LinkedList.prototype.insert = function(position,data){
//1.对position进行越界判断
if(position<0 || position > this.length) return false
//2.根据data创建newNode
var newNode = new Node(data)
//3.判断插入的位置是否是第一个
if(position == 0){
newNode.next = this.head
this.head = newNode
}else{
var index=0
var current = this.head
var previous = null
while(index++ <position){
previous = current
current = current.next
}
newNode.next = current
previous.next = newNode
}
//4.length+1
this.length +=1
return true
}
//4.get方法
LinkedList.prototype.get = function(position){
//1.越界判断
if(position < 0 || position >= this.length) return null
//2.获取对应的data
var current = this.head
var index = 0;
while(index++ <position){
current = current.next
}
return current.data
}
//5.indexOf()方法
LinkedList.prototype.indexOf = function(data){
//1.定义变量
var current = this.head
var index = 0
//2.开始查找
while(current){
if(current.data == data){
return index
}
current = current.next
index +=1
}
//3.找到最后没有找到则返回-1
return -1
}
//6.update方法
LinkedList.prototype.update = function(position,newData){
//1.越界判断
if(position < 0 || position >= this.length) return false
//2.查找正确的节点
var current = this.head
var index = 0;
while(index++ <position){
current = current.next
}
//3.将position位置的node 的data修改成newData
current.data = newData
return true
}
//7.removeAt方法
LinkedList.prototype.removeAt = function(position){
//1.对position进行越界判断
if(position<0 || position > this.length) return null
//3.判断是否删除的是第一个节点
var current = this.head
if(position == 0){
this.head = this.head.next
}else{
var index=0
var previous = null
while(index++ <position){
previous = current
current = current.next
}
//3.前一个节点的next指向,current的next指向
previous.next = current.next
}
//4.length+1
this.length -=1
return current.data
}
//8.remove方法
LinkedList.prototype.remove = function(data){
//1.获取data在列表中的位置
var position = this.indexOf(data)
//2.根据位置信息,删除节点
return this.remove(position)
}
//9.isEmpty方法
LinkedList.prototype.isEmpty = function(){
return this.length == 0
}
//10.size()方法
LinkedList.prototype.size = function(){
return this.length
}
}
测试代码如下:
//测试代码
//1.创建LinkedList
var list = new LinkedList()
//2.测试append 方法
list.append('aaa')
list.append('bbb')
list.append('ccc')
alert(list)
//3.测试insert 方法
list.insert(0,'111')
list.insert(3,'222')
list.insert(5,'333')
alert(list)
//4.测试get方法
alert(list.get(0))
alert(list.get(3))
alert(list.get(5))
//5.测试indexOf方法
alert(list.indexOf('aaa'))
alert(list.indexOf('ccc'))
//6.测试update 方法
list.insert(0,'444')
list.insert(3,'555')
list.insert(5,'666')
alert(list)
//7.测试removeAt 方法
list.removeAt(0)
alert(list)
list.removeAt(3)
alert(list)
//9.测试isEmpty,size方法
alert(list.isEmpty())
alert(list.size())