链表
链表主要分为三种形式,分别为单向链表、双向链表和循环链表;
一、单向链表
链表常用方法:
方法 原理
insert(data ) 从插入数据
find(data ) 查找数据
remove() 删除
首先我们需要创建一个方法,并创建头结点
//创建单向链表的节点
function Node(element) {
this.element = element;
this.next = null;
}
function LList() {
//创建链表的头结点
this.head = new Node("head");
}
基本方法实现
1.数据插入
插入我们可以分为三种,其中最为基本的就是在链表最后插入数据;其二是在指定数据之前插入数据;其三为在指定数据之后插入数据;单向链表的插入的核心思想就是在插入的位置将node节点的next断开,然后再将前后两个旧节点与新节点的连接起来;
插入节点图示:
下面为具体代码:
//基本插入算法
this.insert=function (data) {
//1,创建新的节点
var buffer = new Node(data);
//2,获得最后的节点
var last = this.findLast();
//3,把最后节点的next指向新节点
last.next=buffer;
}
//在当前元素after之后插入数据element
this.insertAfter1=function (element,after) {
//找到当前节点after
let node=this.find(after);
// 创建一个新节点
let newNode= new Node(element);
//将新节点的下一个节点与当前节点的下一个节点链接起来
newNode.next=node.next;
//将当前节点的下一个节点指向新节点
node.next=newNode;
}
//在当前元素before之前插入数据element
this.insertBefore1=function (element,before) {
//找到当前节点的之前的节点
let preNode = this.findPrevious(before);
// 创建一个新节点
let newNode= new Node(element);
//将新节点的下一个节点与当前节点的前一个节点连接起来
newNode.next=preNode.next;
//将当前节点的前一个节点的下一个节点指向新节点
preNode.next=newNode;
}
2.查找数据
查找数据需要利用while循环,对链表进行遍历,其中最主要的就是while循环的循环条件
查找当前数据:
this.find=function (data) {
//创建一个节点
let buffer=this.head;
while(buffer!=null){
if(buffer.element==data){
return buffer;
}
//将当前节点的下一个节点赋值给当前节点
buffer=buffer.next;
}
//如果没有找到则返回空
return buffer;
}
查找当前节点的前一个节点:
this.findPrevious=function (data) {
//创建一个节点
let buffer=this.head;
//找到当前节点
let currentNode=this.find(data);
//如果buffer的下一个节点是当前节点,则返回buffer
while(buffer.next!=currentNode){
//将当前节点的下一个节点赋值给当前节点
buffer=buffer.next;
}
return buffer;
}
3.删除节点
删除节点其实与插入节点的原理差不多,都是断开需要删除节点的前后节点的链接,然后将前后两个节点通过next链接在一起。关于单向链表的插入和删除其基本原理都是先断开节点然后是链接节点,所以要明确要断开的节点和要连接的节点,这样事情就很好理解了。
删除节点图示:
代码如下:
this.remove = function (item) {
//获得当前节点的前一个节点,调用this.findPrevious函数
var prevNode = this.findPrevious(item);
console.log(prevNode);
if (!(prevNode.next == null)) {
prevNode.next = prevNode.next.next;
}
};
4.遍历节点
代码如下:
this.forEach=function(call) {
//1,获得头节点
var currNode = this.head;
var index=0;
//2,遍历节点数据
while(currNode!=null){
call(currNode.element,index);
currNode=currNode.next;
}
};
5.将节点向前移动n个节点
有两种方法:
(1)假若向前移动2个节点,将element3节点向前移动2个节点,移动到element1 节点之前;我们则先找到element1节点,断开element1节点与前一个节点的链接和element2与element3节点之间和element3与next节点的链接;最后依次链接起来即可;图示:
代码如下:
this.advance=function(currElement,n){
//获得当前节点
var currentNode = this.find(currElement);
//获得当前节点的前一个节点
var buffer = this.findPrevious(currElement);
var preNode=buffer;
while(preNode.element!='head'&&n>0){
preNode = this.findPrevious(preNode.element);
n--;
}
buffer.next=currentNode.next;
currentNode.next=preNode.next;
preNode.next=currentNode;
}
(2)第二种方法是将当前节点与前一个节点不断的交换位置,知道element3节点交换到element1节点之前即可;图示:
交换位置具体做法,图示:
代码如下:
//将当前节点想前移动n个节点
this.advance=function(currElement,n){
//获得当前节点
var currentNode=this.find(currElement);
while(prePreNode!=this.head&&n>0){
//获得当前节点的前一个节点
var preNode=this.findPrevious(currentNode.element);
//获得前一个节点的前一个节点
var prePreNode=this.findPrevious(preNode.element);
//将前一个节点与当前节点的next节点链接
preNode.next=currentNode.next;
//将当前节点与前一个节点链接起来
currentNode.next=preNode;
//将前前节点与当前节点链接
prePreNode.next=currentNode;
n--;
}
}
6.将当前节点向后移动n个节点
原理与向前移动相同;代码如下:
(1)第一种方法
//将当前节点向后移动n个节点
this.back=function(currElement,n){
//1,获得当前的节点
var currentNode = this.find(currElement);
//2,假设当前的节点为最后的节点
var lastNode=currentNode;
//当前节点的前一个节点和当前节点的后一个节点连在一起
var currentPre=this.findPrevious(currElement);
//3,在n大于0的条件下,下一个节点为空的时候停下来
while(lastNode.next!=null&&n>0){
lastNode = lastNode.next;
n--;
}
//4,currentNode节点和lastNode相等,说明currentNode本身就是最后的节点
if(currentNode==lastNode){
return;
}
currentPre.next=currentNode.next;
//把当前节点连在lastNode节点后面
currentNode.next=lastNode.next;
lastNode.next=currentNode;
}
(2)第二种方法
//将当前节点向后移动n个节点
this.back=function(currElement,n){
//获得当前节点
var currentNode=this.find(currElement);
// var buffer=currentNode;
while(currentNode.next!=null&&n>0){
//获得当前节点的前一个节点
var preNode=this.findPrevious(currentNode.element);
//获得当前节点的下一个节点
var nextNode=currentNode.next;
//将前一个节点与当前节点的next节点链接
preNode.next=currentNode.next;
currentNode.next=nextNode.next;
//将当前节点与前一个节点链接起来
nextNode.next=currentNode;
n--;
}
}
二、双向链表
双向链表与单向链表不同的地方就在于,双向链表不仅有后继同时还有前驱;
function Node(element) {
this.element = element;
this.next = null;
this.previous = null;
}
1.数据插入
//插入节点
this.insert=function(element) {
//创建新节点
var newNode = new Node(element);
//获得头节点
var currNode = this.head;
//获得最后的节点
while(currNode.next!=null){
//把当前节点的下一个节点赋值给当前的节点
currNode= currNode.next;
}
//把新创建的节点插入到最后的节点
currNode.next =newNode;
//把最后的节点给新节点的前驱赋值
newNode.previous=currNode;
}
this.insertAfter=function (element,after) {
//找到当前after节点
var currNode = this.find(after);
//创建新的节点
var newNode = new Node(element);
if(currNode.next==null){
//把after节点的后继给新节点
currNode.next=newNode;
//新节点的前驱指向当前的节点
newNode.previous= currNode;
}
else{
//把after节点的后继给新节点的后继
newNode.next=currNode.next;
currNode.next.previous=newNode;
newNode.previous = currNode;
currNode.next=newNode;
}
}
2.遍历节点
this.forEach=function (call) {
//1,获得头节点
var currNode = this.head;
while(currNode!=null){
call(currNode);
currNode = currNode.next;
}
}
//反序遍历
this.forEachReverse=function (call) {
//1 获得最后的节点
var lastNode = this.findLast();
//2,获得下标
var index=this.length-1;
//3,反向遍历
while(lastNode!=null){
call(lastNode,index);
index--;
lastNode= lastNode.previous;
}
}
3.查找节点
//查找当前节点
this.find=function(element){
//1,获得第一个节点
var currNode = this.head;
//2,遍历节点
while(currNode!=null&&currNode.element!=element){
//3,把当前节点的下一个节点给当前节点赋值
currNode = currNode.next;
}
//返回当前节点
return currNode;
}
//找到最后节点
this.findLastNode=function () {
//1,获得头节点
var currNode = this.head;
//2,遍历节点
while(currNode.next!=null){
//3,把当前节点的下一个节点给当前节点赋值
currNode = currNode.next;
}
//3,返回当前的节点
return currNode;
}
4.删除节点
this.remove = function (element) {
//1,获得当前的节点
var currNode = this.find(element);
//2,判断当前节点是否是最后的节点,为空,说明是最后一个节点,不为空,说明不是最后一个节点
if (currNode.next == null) {
//3,找到当前节点的前一个节点
var preNode = currNode.previous;
//4,前一个节点的后继为空
preNode.next = null;
} else {
//3,找到当前节点的前一个节点
var preNode = currNode.previous;
//4,找到当前节点的后一个节点
var lastNode = currNode.next;
//5,前一个节点的next指向lastNode
preNode.next = lastNode;
//6,后一个节点的前驱指向前一个节点
lastNode.previous = preNode;
}
}
三、循环链表
循环链表的的最后一个节点与头结点相连;
function LList() {
this.head = new Node('head');
//始终是在最后一个节点插入
this.insert = function (element) {
//1,设置当前的节点
var currentNode = this.head;
//2,创建新的节点
var newNode = new Node(element);
while (currentNode.next != null) {
//把当前节点的下一个节点给予当前的节点
currentNode = currentNode.next;
}
//当前节点的下一个节点指向新节点
currentNode.next = newNode;
//新的节点的前一个节点指向当前的节点
newNode.previous = currentNode;
//把后面的头节点连接在一起
newNode.next = this.head;
this.head.previous=newNode;
}
this.insertAfter = function (newElement, after) {
var after = this.find(after);
var newNode = new Node(newElement);
if (after == null || after.next == null) {
after = this.findLast();
after.next = newNode;
newNode.previous = after;
} else {
//after的后继的前驱指向当前的节点
after.next.previous = newNode;
newNode.next = after.next;
after.next = newNode;
newNode.previous = after;
}
}
this.find = function (element) {
var currentNode = this.head;
while (currentNode != null && currentNode.element != element) {
currentNode = currentNode.next;
}
return currentNode;
}
this.forEach = function (call) {
//1,获得当前的节点
var currentNode = this.head.next;
//2,设置遍历节点的下标
var index = 0;
var time = 0;
//3,遍历节点
while (currentNode != null&¤tNode.element!='head') {
call(currentNode, index);
//把当前的节点移动后面去
currentNode = currentNode.next;
//下标加1
index++;
}
}
this.forEachReverse = function (call) {
//1,找到最后的节点
var lastNode = this.findLast();
//2,反向的遍历
while (lastNode != null) {
call(lastNode);
lastNode = lastNode.previous;
}
}
this.findLast = function () {
//当前的头节点
var currentNode = this.head;
//遍历
while (currentNode.next != null) {
currentNode = currentNode.next;
}
return currentNode;
}
this.remove = function (element) {
//1,获得头节点
var currentNode = this.head;
//2,遍历节点
while (currentNode != null && currentNode.element != element) {
currentNode = currentNode.next;
}
currentNode.previous.next = currentNode.next;
currentNode.next.previous = currentNode.previous;
}
}