链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同。
数组:
要存储多个元素,数组(或成为列表)是最常用的数据结构
缺点:
数据的创建统筹需要申请一段连续的内存空间(一整块内存),并且大小是固定的(大多数编程语言数组都是固定的),所以当当前数组不能满足容量时,需要扩容。(一般情况下是申请一个更大的数组,比如与2倍,然后将原数组中的元素复制过去)
而且在数组开头或者中间位置插入数据消耗的成本很高,需要进行大量元素的位移。
链表:
不同于数组,链表中的元素在内存中不必是连续的空间
链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(或者成为指针琥或连接)组成。
相对于数组的优点:
内存空间不是必须连续的,可以充分利用计算机的内存,实现灵活的内存动态管理。
链表不必再创建时就确定大小,并且大小可以无限的延伸下去。
链表在插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高了很多。
单向链表:
只能从头遍历到尾或者从尾遍历到头(一般从头到尾)
也就是链表相连的过程是单向的
实现的原理是上一个链表中有一个指向下一个的引用。
缺点:
我们可以轻松的到达下一个节点,但是回到前一个节点是很难的,但是,在时间开发过程中
经常会遇到需要回到上一个节点的情况
function linkeList() {
function Node(data) {
this.data = data;
this.next = null;
}
this.length = 0;
this.head = null;
linkeList.prototype.append = function (data) {
// 1.判断添加的节点是否是第一个节点
// 2.创建新的节点
var newNode = new Node(data);
// 3.判断添加的节点是否是第一个节点
if (this.length == 0) {
this.head = newNode
} else {
// 3.1 加入的节点不是第一个节点
var current = this.head;
// 一直找到next为空的,添加节点。就是要在末尾添加节点,
while (current.next) {
current = current.next
}
current.next = new Node(data);
}
// 节点的长度加1
this.length += 1;
}
// 转字符串
linkeList.prototype.toString = function () {
// 将数据取出来,复制给current,一直去到next为null
var current = this.head;
var linkeString = ''
// 2.循环获取一个个节点
while (current) {
linkeString = linkeString + current.data
current = current.next
}
return linkeString
}
//根据获索引获取数据
linkeList.prototype.get = function (index) {
// 越界判断
if (index < 0 || index >= this.length) return null;
// 获取对应位置的数据
// 一直循环到indexOf 等于想要获取目标数据的索引,将其return出去
var current = this.head;
var indexOf = 0;
while (indexOf++ < index) {
current = current.next
}
// indexOf == index
return current.next
}
// 根据数据获取索引
linkeList.prototype.indexOf = function (data) {
// 直接赋值判断,因为初始化值head就等于null,不需要加越界判断
var current = this.head;
var index = 0;
while (current) {
if (current.data==data) {
return index
}
current = current.next;
index++
}
// 没有找到相对应的数据的索引,返回-1
return -1
}
// 插入
linkeList.prototype.insert = function (index, data) {
// 如果index == this.length,是可以将数据插入的,插入数据的位置为末尾
if (index < 0 || index > this.length) return null;
// 创建节点
var newNode = new Node(data)
if (index == 0) {
newNode.next = this.head;
this.head = newNode
} else {
var indexOf = 0;
var current = this.head;
var previous = null;
while (indexOf++ < index) {
previous = current;
current = current.next
}
newNode.next = current;
previous.next = newNode
}
this.length += 1;
return true
}
// 更新 有时间的小伙伴可以根据索引,写一个更新方法
linkeList.prototype.update = function (data, newData) {
// 如果全部循环没有找到对应的数据,返回false
var current = this.head;
var index = 0;
while (current && current.data != data) {
current = current.next
index++
}
if (index == this.length) {
return false
}
current.data = newData
return true
}
// 删除
linkeList.prototype.remove = (data) => {
// 1.根据data获取索引
var index = this.indexOf(data)
// 2.根据索引删除
return this.removeAt(index)
}
linkeList.prototype.removeAt = (index) => {
// 1.越界判断
if (index < 0 || index >= this.length) return null;
// 2.判断删除的是否是第一个节点
var current = this.head;
if (index == 0) {
this.head = this.head.next
} else {
var indexOf = 0;
var previous = null;
while (indexOf++ < index) {
previous = current
current = current.next
}
// 前一个节点的next指向current的next
previous.next = current.next
}
this.length -= 1;
return true
}
//
linkeList.prototype.isEmpty = () => {
// 只需要判断length是否等于0
return this.length == 0
}
//
linkeList.prototype.size = () => {
// 只需输入length的长度
return this.length
}
linkeList.prototype.reveser = (head) => {
if (!head) {
return null
}
if (head.next == null) {
return head
}
var new_head = this.reveser(head.next);
// 把当前节点连接到链表上
head.next.next = head;
head.next = null;
return new_head
}
linkeList.prototype.newHead = () => {
return this.head
}
}
var linke = new linkeList()
// 测试输出
linke.append(1)
linke.append(2)
linke.append('cde')
// console.log(linke.toString())
// console.log(linke.indexOf('cde'))
// linke.insert(0,6)
// console.log('update', linke.update('1', '6'))
// console.log("linke.remove('cde')",)
linke.remove('2')
var newObject = linke.newHead()
console.log('linke.reveser(newObject) ', linke.reveser(newObject))