链表是一种动态的数据结构,用来存储一系列有序的元素。每个元素由一个存储元素本身的节点和一个指向下一个元素的指针构成。
与数组的区别在于
要实现链表数据结构,关键在于保存head元素(即链表的头元素)以及每一个元素的next指针,有这两部分我们就可以很方便地遍历链表从而操作所有的元素。
(详见JavaScript数据结构——链表的实现与应用 )
单向链表具体实现
let?Node?=?function(element){
????this.element?=?element;?//?属性1:保存节点值
????this.next?=?null;?//?属性2:next指针
}
或者写成这样
class?Node?{
????constructor(element)?{
????????this.element?=?element;
????????this.next?=?null;?
????}
}
class?LinkedList{
????constructor(){
????????this.length?=?0;
????????this.head?=?null;
????}
????append(element){}???//?向链表尾部中添加新节点
????insert(position,?element){}?//?向链表的指定位置插入节点
????removeAt(position){}????//?删除链表中指定位置的元素,并返回这个元素的值
????remove(element){}???//?删除链表中对应的元素
????indexOf(element){}??//?在链表中查找给定元素的索引
????getElementAt(position){}????//?返回链表中索引对应的元素
????isEmpty(){}?//判断链表是否为空
????size(){}????//?返回链表的长度
????getHead(){}?//?返回链表的头元素
????clear(){}???//?清空链表
????toString(){}????//?辅助方法,按指定格式输出链表中的所有元素,方便验证结果
}
????getElementAt(position){
????????if?(position?<?0?||?position?>=?this.length)
????????????return?null;
????????let?current?=?this.head;
????????for(let?i=0;?i<position;?i++){
????????????current?=?current.next;?//?next()获得匹配元素集合中每个元素紧邻的同胞元素。(jQuery)
????????}
????????return?current;
????}
????append(element){
????????let?node?=?new?Node(element);
????????//?如果当前链表为空,则将head指向node
????????if?(this.head?===?null){
????????????this.head?=?node;
????????}else{
????????????//?否则在链表尾部元素后添加新元素
????????????let?current?=?this.getElementAt(this.length?-?1);
????????????current.next?=?node;
????????}
????????this.length++;
????}
????insert(position,?element){?//?向链表的指定位置插入节点
????????//?判断position是否超出边界
????????if?(position?<?0?||?position?>?this.length)
????????????return?false;
????????let?node?=?new?Node(element);
????????if?(position?===?0){
????????????node.next?=?this.head;
????????????this.head?=?node;
????????}else{
????????????let?previous?=?this.getElementAt(position?-?1);
????????????node.next?=?previous.next;
????????????previous.next?=?node;
????????}
????????this.length++;
????????return?true;
????}
????removeAt(position){
????????//?判断position合法
????????if?(position?<?0?||?position?>?this.length)
????????return?null;?//?找不到
????????let?current?=?this.head;
????????if?(position?===?0){
????????????this.head?=?current.next;
????????}else{
????????????let?previous?=?this.getElementAt(position?-?1);
????????????current?=?previous.next;
????????????previous.next?=?current.next;
????????}
????????this.length--;
????????return?current.element;
????}
如果当前链表只有一个节点,那么下一个节点为null,此时将head指向下一个节点等同于将head设置成null,删除之后链表为空。被删除的节点由于再也没有其它部分的引用而被丢弃在内存中,等待垃圾回收器来清除。
????indexOf(element){
????????let?current?=?this.head;
????????for(let?i=0;?i<this.length;?i++){
????????????if(current.element?===?element)
????????????????return?i;
????????????current?=?current.next;
????????}
????????return?-1;?//?找不到
????}
????remove(element){?
????????let?index?=?this.indexOf(element);
????????return?this.removeAt(index);
????}
????isEmpty(){
????????return?this.length?===?0; // 也可用head===null
????}
????clear(){
????????this.head?=?null;
????????this.length?=?0;
????}
????????let?linkedList?=?new?LinkedList();
????????//?测试用例1
????????linkedList.append(10);
????????linkedList.append(15);
????????linkedList.append(20);
????????console.log(linkedList.toString());
????????//?测试用例2
????????linkedList.insert(0,?9);
????????linkedList.insert(2,?11);
????????linkedList.insert(5,?25);
????????console.log(linkedList.toString());
????????//?测试用例3
????????console.log(linkedList.removeAt(0));
????????console.log(linkedList.removeAt(1));
????????console.log(linkedList.removeAt(3));
????????console.log(linkedList.toString());
????????//?测试用例4
????????console.log(linkedList.indexOf(20));
????????//?测试用例5
????????linkedList.remove(20);
????????//?测试用例6
????????console.log(linkedList.toString());
????????//?测试用例7
????????linkedList.clear();
????????console.log(linkedList.size());
双向链表具体实现
单向链表中每一个元素只有一个next指针,用来指向下一个节点,我们只能从链表的头部开始遍历整个链表,任何一个节点只能找到它的下一个节点,而不能找到它的上一个节点。
双向链表中的每一个元素拥有两个指针,一个用来指向下一个节点,一个用来指向上一个节点。在双向链表中,除了可以像单向链表一样从头部开始遍历之外,还可以从尾部进行遍历。下面是双向链表的数据结构示意图:
class?Node?{
????constructor(element)?{
????????this.element?=?element;?//?属性1:保存节点值
????????this.next?=?null;?//?属性2:next指针,指向后一个节点
????????this.prev?=?null;?//?属性3:prev指针,指向前一个节点
????}
}
class?DoubleLinkedList?extends?LinkedList{
????constructor(){
????????super();
????????this.tail?=?null;
????}
getTail(){}
}
????append(element){
????????let?node?=?new?Node(element);
????????//?如果链表为空,则将head和tail都指向当前添加的节点
????????if?(this.head?===?null){
????????????this.head?=?node;
????????????this.tail?=?node;
????????}else{
????????????//?否则将当前节点添加到链表的尾部
????????????this.tail.next?=?node;
????????????node.prev?=?this.tail;
????????????this.tail?=?node;
????????}
????????this.length++;
????}
????getElementAt(position){?//?如果要查找的元素的索引号大于链表长度的一半,则从链表的尾部开始遍历
????????if?(position?<?0?||?position?>=?this.length)
????????????return?null;
????????if?(position?>?Math.floor(this.length?/2)){
????????????let?current?=?this.tail;
????????????for(let?i=this.length-1;?i>position;?i--){
????????????????current?=?current.prev;
????????????}
????????????return?current;
????????}else{
????????????return?super.getElementAt(position);
????????}
????}
????insert(position,?element){
????????if?(position?<?0?||?position?>?this.length)
????????????return?false;
????????if(position?===?this.length){
????????????this.append(element);
????????}else{
????????????let?node?=?new?Node(element);
????????????//?插到头部
????????????if(position?===?0){
????????????????if(this.head?===?null){
????????????????????this.head?=?node;
????????????????????this.tail?=?node;
????????????????}else{
????????????????????node.next?=?this.head;
????????????????????this.head.prev?=?node;
????????????????????this.head?=?node;
????????????????}
????????????}else{
????????????????//?插到中间
????????????????let?current?=?this.getElementAt(position);
????????????????let?previous?=?current.prev;
????????????????node.next?=?current;
????????????????node.prev?=?previous;
????????????????previous.next?=?node;
????????????????current.prev?=?node;
????????????}
????????}
????????this.length++;
????????return?true;
????}
????removeAt(position){
????????if?(position?<?0?||?position?>?this.length)
????????????return?null;
????????let?current?=?this.head;
????????let?previous;
????????//?移除头部元素
????????if(position?===?0){
????????????this.head?=?current.next;
????????????this.head.prev?=?null;
????????????if?(this.length?===1)
????????????????this.tail?=?null;
????????}else?if(position?===?this.length?-1){
????????????//?移除尾部元素
????????????current?=?this.tail;
????????????this.tail?=?current.prev;
????????????this.tail.next?=?null;
????????}else{
????????????//?移除中间元素
????????????current?=?this.getElementAt(position);
????????????previous?=?current.prev;
????????????previous.next?=?current.next;
????????????current.next.prev?=?previous;
????????}
????????this.length--;
????????return?current.element;
????}
循环链表
循环链表的尾部指向它自己的头部。循环链表可以有单向循环链表,也可以有双向循环链表。下面是单向循环链表和双向循环链表的数据结构示意图:
原文:https://www.cnblogs.com/chri330dj/p/12423741.html