介绍
链表和数组一样,都用来存储一系列的元素。但是链表和数组实现的机制完全不同。从而导致其相关操作的效率也有所不同。
链表的特点
- 链表是一种物理存储单元上非连续、非顺序的存储结构
- 链表直观形态,每个节点元素都有保存了下一个节点的引用,头部指向第一个节点,最后一个节点指向null。
链表和数组的区别
优点
- 链表存储空间是非连续的而数组的存储空间是连续的。因此链表的内存利用率会更高一点。
- 链表在头部插入元素和中间插入元素的效率会比数组的效率要高
- 链表在存储数据过程中无须确定链表的长度,而数组需要确定数组的长度,即使某些语言数组是可变长度的,其背后的原理其实也新建了长度更长的数组然后将其拷贝到新数组中最后返回新数组的引用。
缺点
- 链表查询效率比数组的查询效率要低很多因为他访问元素的过程中需要从头开始遍历,而数组可以通过下标直接访问。
链表的组成
- Head :指向链表的第一个节点
- Node:节点(包含着两项数据,一个是data用于存放当前节点的元素,一个是next用于存放下一个节点的引用)
- Length:记录链表的长度
链表的常见方法
增
- append(element)
- insert(position,element)
删
- remove(data)
- removeAt(position)
改
- update(position,data)
查
- get(position)
- indexOf(data)
- isEmpty()
- size()
- toString()
链表的实现
基本实现
- 实现内部函数Element 用户创建Element对象存储数据以及下一个节点的引用
- 实现append()方法完成追加元素的操作
- 实现toString()方法将链表中的元素转化为字符串的形式并返回
- 添加head属性用于存放链表的第一个节点
- 添加length属性用来记录链表中元素的个数
实现上述内容,链表就有了最基本功能和“初步的框架”
//链表的实现
function Linklist (){
//相当于JAVA中的内部类,用于创建一个节点对象,存放数据和下一个元素的索引
function Element (data){
this.data = data
this.next = null
}
this.head = null
this.length = 0
//向链表添加元素
Linklist.prototype.append = function (value){
let ele = new Element(value)
let current = this.head
if (this.length == 0){
this.head = ele
}else {
while (current.next){
current = current.next
}
current.next = ele
}
this.length+=1
}
//将链表的内容转换成字符串以逗号进行分割
Linklist.prototype.toString = function (){
let current = this.head
let str = ""
//此处循环条件用current.next循环结束在current将会指向最后一个节点,如果条件是cureent的话循环结束后指向的是null。
while (current.next){
str+=`${current.data},`
current = current.next
}
str += `${current.data}`
return str
}
}
let linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
console.log(linklist.length)
console.log(linklist.toString())
运行结果
所有常用方法完的整实现
//链表的实现
function Linklist (){
function Element (data){
this.data = data
this.next = null
}
this.head = null
this.length = 0
//向链表添加元素
Linklist.prototype.append = function (value){
let ele = new Element(value)
let current = this.head
if (this.length == 0){
this.head = ele
}else {
while (current.next){
current = current.next
}
current.next = ele
}
this.length+=1
}
//将链表中的元素转换成字符串的形式返回
Linklist.prototype.toString = function (){
let current = this.head
let str = ""
while (current.next){
str+=`${current.data},`
current = current.next
}
str += `${current.data}`
return str
}
//判断链表是否为空
Linklist.prototype.isEmpty = function (){
return this.length == 0
}
//查询链表的长度
Linklist.prototype.size = function (){
return this.length
}
//获取下标对应的元素
Linklist.prototype.get = function (position){
if (position<0 || position >= this.length) return null
let current = this.head
for (let i = 0 ;i < position;i++){
current = current.next
}
return current.data
}
//向链表中的元素插入元素,允许在最后面插入元素也不算越界
Linklist.prototype.insert = function (position,data){
//越界判断
if (position<0 || position>this.length) return false
let ele = new Element(data)
let current = this.head
if (position == 0){
ele.next = this.head
this.head = ele
}else {
let i = 0;
let previous = null;
//我们通过循环主要是要获取的内容是position对应下标的节点以及他的上一个节点
while(i<position){//难点是这个条件用小于等于还是小于号;
previous = current//理解,对于这个循环来说,需要确定好每次循环此处current代表的是哪个节点(这里代表i下标对应的节点)
current = current.next//此处的current其实代表的但是当前i下标所对应的下一个节点
i++//我们通过上面两个current瞬时值,得出结论,循环只需要循环到当前position-1(也就是<position即可)次就可以得到【position】对应节点以及他的【position-1】对应节点
}
previous.next = ele
ele.next = current
}
this.length++
return true
}
//修改对应坐标的元素
Linklist.prototype.update = function (position,newData){
//越界判断
if (position<0 || position >= this.length) return false
let current = this.head
for (let i = 0;i < position;i++){
current = current.next
}
current.data = newData
return true
}
//根据内容获取所对应的下标
Linklist.prototype.indexOf = function (data){
let current = this.head
for (let i= 0;i<this.length;i++){//此处i是多少的时候对应current存放的就是i下标所对应的节点
if (current.data == data){//如果找到该元素则在此处返回i循环也因此结束无须使用break了
return i
}
//如果循环能正常结束自然,程序能运行到这里,代表没有找到该元素
current = current.next
}
return -1
}
//移除对应坐标的节点
Linklist.prototype.removeAt = function (position){
//越界判断
if (position<0 || position >= this.length) return null
if (position == 0){
this.head = this.head.next
}
let current = this.head
let previous = null
for (let i = 0;i<position;i++){
previous = current
current = current.next
}
previous.next = current.next
this.length-= 1
return current.data
}
//移除对应内容坐标的节点
Linklist.prototype.remove = function (data){
let index = this.indexOf(data)
return this.removeAt(index)
}
}
//第一阶段测试
console.log("*******第一阶段测试*******")
console.log()
var linklist = new Linklist()
console.log("是否为空:",linklist.isEmpty())
linklist.append("zhangsan")
linklist.append("lisi")
console.log("是否为空:",linklist.isEmpty())
console.log("length:",linklist.length)
console.log("size:",linklist.size())
console.log(linklist.toString())
console.log("==========================")
//get测试
console.log("*******get测试*******")
console.log()
var linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
console.log(linklist.length)
console.log(linklist.toString())
console.log(linklist.get(-1));
console.log(linklist.get(0));
console.log(linklist.get(1));
console.log(linklist.get(2));
console.log("==========================")
//insert 测试
console.log("*******insert测试*******")
console.log()
var linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
linklist.append("wangwu")
console.log(linklist.length)
console.log(linklist.toString())
console.log(linklist.insert(-1,"hhhh"))
console.log(linklist.insert(0,"zhaoliu"))
console.log(linklist.insert(1,"xiaoming"))
console.log(linklist.insert(5,"xiaodu"))
console.log(linklist.insert(7,"hhhh"))
console.log(linklist.toString())
console.log("==========================")
//update 测试
console.log("*******update测试*******")
console.log()
var linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
console.log(linklist.length)
console.log(linklist.toString())
console.log(linklist.update(0,"wangwu"))
console.log(linklist.toString())
console.log(linklist.update(1,"zhaoliu"))
console.log(linklist.toString())
console.log(linklist.update(-1,"haha"))
console.log(linklist.toString())
console.log(linklist.update(3,"xiaodu"))
console.log(linklist.toString())
console.log("==========================")
//indexOf测试
console.log("*******get测试*******")
console.log()
var linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
console.log(linklist.length)
console.log(linklist.toString())
console.log(linklist.indexOf("hh"));
console.log(linklist.indexOf("lisi"));
console.log("==========================")
//removeAt测试
console.log("*******removeAt测试*******")
console.log()
var linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
console.log(linklist.length)
console.log(linklist.toString())
console.log(linklist.removeAt(-1))
console.log(linklist.toString())
console.log(linklist.removeAt(2))
console.log(linklist.toString())
console.log(linklist.removeAt(1))
console.log(linklist.toString())
console.log("==========================")
//remove测试
console.log("*******remove测试*******")
console.log()
var linklist = new Linklist()
linklist.append("zhangsan")
linklist.append("lisi")
console.log(linklist.length)
console.log(linklist.toString())
console.log(linklist.remove("hhh"))
console.log(linklist.toString())
console.log(linklist.remove("lisi"))
console.log(linklist.toString())
console.log("==========================")