三、链表
3.1、链表的特点
- 存储多个元素,另外一个选择就是使用链表。
- 不同于数组,链表中的元素在内存中不必是连续的空间。
- 链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有些语言称为指针)组成。
- 链表优点:
- 内存空间不必是连续的,可以充分利用计算机的内存,实现灵活的内存动态管理。
- 链表不必在创建时就确定大小,并且大小可以无限延伸下去。
- 链表在插入和删除数据时,时间复杂度可以达到 O(1),相对数组效率高很多。
- 链表在插入和删除数据时,时间复杂度可以达到 O(1),相对数组效率高很多。
- 链表缺点:
- 访问任何一个位置的元素时,需要从头开始访问。(无法跳过第一个元素访问任何一个元素)
- 无法通过下标值直接访问元素,需要从头开始一个个访问,直到找到对应的元素。
- 虽然可以轻松地到达下一个节点,但是回到前一个节点是很难的。
3.2、链表的常见操作
3.2.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 方法,让其只输出元素的值。
<script>
// 封装链表类
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){
this.head = newNode
}else{
// 找到最后一个节点
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){
if(position < 0 || position >= this.length) return null
var current = this.head
var index = 0
while(index < position){
current = current.next
index++
}
return current.data
}
// 5.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
}
// 6.updata方法
LinkedList.prototype.updata = function(position,newdata){
if(position < 0 || position >= this.length) return false
var current = this.head
var index = 0
while(index < position){
current = current.next
index++
}
current.data = newdata
return true
}
// 7.removeAt方法
LinkedList.prototype.removeAt = function(position){
if(position < 0 || position >= this.length) return false
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
}
previous.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
}
}
</script>
3.2.2、双向链表
单向链表
- 只能从头遍历到尾或者从尾遍历到头(一般从头到尾)。
- 链表相连的过程是单向的,实现原理是上一个节点中有指向下一个节点的引用。
- 单向链表有一个比较明显的缺点:可以轻松到达下一个节点,但回到前一个节点很难,在实际开发中, 经常会遇到需要回到上一个节点的情况。
双向链表
- 既可以从头遍历到尾,也可以从尾遍历到头。
- 链表相连的过程是双向的。实现原理是一个节点既有向前连接的引用,也有一个向后连接的引用。
- 双向链表可以有效的解决单向链表存在的问题。
- 双向链表缺点:
- 每次在插入或删除某个节点时,都需要处理四个引用,而不是两个,实现起来会困难些。
- 相对于单向链表,所占内存空间更大一些。
- 但是,相对于双向链表的便利性而言,这些缺点微不足道。
3.2.2.1、双向链表结构
- 双向链表不仅有 head 指针指向第一个节点,而且有 tail 指针指向最后一个节点。
- 每一个节点由三部分组成:item 储存数据、prev 指向前一个节点、next 指向后一个节点。
- 双向链表的第一个节点的 prev 指向 null。
- 双向链表的最后一个节点的 next 指向 null。
3.2.2.2、双向链表常见的操作
append(element)
向链表尾部追加一个新元素。insert(position, element)
向链表的指定位置插入一个新元素。getElement(position)
获取指定位置的元素。indexOf(element)
返回元素在链表中的索引。如果链表中没有该元素就返回 -1。update(position, element)
修改指定位置上的元素。removeAt(position)
从链表中的删除指定位置的元素。remove(element)
从链表删除指定的元素。isEmpty()
如果链表中不包含任何元素,返回trun
,如果链表长度大于 0 则返回false
。size()
返回链表包含的元素个数,与数组的length
属性类似。toString()
由于链表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认toString
方法,让其只输出元素的值。forwardString()
返回正向遍历节点字符串形式。backwordString()
返回反向遍历的节点的字符串形式。