链表结构

116 阅读2分钟

链表

相对于数组, 链表有一些优点:

  • 内存空间不是比是连续的. 可以充分利用计算机的内存. 实现灵活的内存动态管理.
  • 链表不必在创建时就确定大小, 并且大小可以无限的延伸下去.
  • 链表在插入和删除数据时, 时间复杂度可以达到O(1). 相对数组效率高很多.

相对于数组, 链表有一些缺点:

  • 链表访问任何一个位置的元素时, 都需要从头开始访问.(无法跳过第一个元素访问任何一个元素).
  • 无法通过下标直接访问元素, 需要从头一个个访问, 直到找到对应的问题

单项链表的实现

 //封装节点类
 class Node {
   constructor(data) {
     this.data = data;
     this.next = null;
   }
 }
 ​
 //封装链表
 class LinkedList {
   constructor() {
     //头指针为null
     this.head = null;
     this.length = 0;
   }
   //添加节点
   appen(data) {
     //创建节点
     let newNode = new Node(data);
     //判断链表中是否有节点
     if (this.length == 0) {
       this.head = newNode;
     } else {
       //定义指针,从头开始遍历
       let current = this.head;
       //遍历到next指向null的节点,然后添加节点
       while (current.next) {
         current = current.next;
       }
       current.next = newNode;
     }
     this.length += 1;
   }
 ​
   //打印链表
   toString() {
     let current = this.head;
     let listString = "";
     while (current) {
       listString += current.data + " ";
       current = current.next;
     }
     return listString;
   }
 ​
   //插入方法
   insert(position, data) {
     //创建节点
     let newNode = new Node(data);
     //判断postion是否越界
     if (position < 0 || position > this.length) {
       return false;
     }
 ​
     //是否插入到的是0位置
     if (position == 0) {
       newNode.next = this.head;
       this.head = newNode;
     } else {
       let index = 0;
       let current = this.head;
       let previous = null;
       while (index++ < position) {
         previous = current;
         current = current.next;
       }
       newNode.next = current;
       previous.next = newNode;
     }
 ​
     this.length += 1;
     return true;
   }
 ​
   //获取指定节点的元素
   get(position) {
     //判断越界
     if (position < 0 || position > this.length - 1) return null;
     //查找节点
     let index = 0;
     let current = this.head;
     while (index++ < position) {
       current = current.next;
     }
     return current.data;
   }
   //查找索引
   indexOf(data) {
     let index = 0;
     let current = this.head;
     let flag = false;
     while (index < this.length) {
       if (data == current.data) {
         return index;
         flag = true;
       }
       index++;
       current = current.next;
     }
     if (!flag) {
       return -1;
     }
   }
   //更新数据
   upData(position, data) {
     //判断越界
     if (position < 0 || position > this.length - 1) return false;
     //查找节点
     let index = 0;
     let current = this.head;
     while (index++ < position) {
       current = current.next;
     }
     current.data = data;
     return true;
   }
 ​
   //移除特定位置的节点
   removeAt(position) {
     if (position < 0 || position > this.length - 1) return false;
     //删除第一个节点
     let current = this.head;
     if (position == 0) {
       this.head = current.next;
     } else {
       //删除普通的节点
       let index = 0;
       let previous = null;
       while (index++ < position) {
         previous = current;
         current = current.next;
       }
       previous.next = current.next;
     }
     this.length -= 1;
     return current.data;
   }
 ​
   //根据data删除相应的节点
   remove(data) {
     //     let previous = null;
     //     let current = this.head;
     //     while (current) {
     //       if (data == current.data) {
     //         previous.next = current.next;
     //         return current.data;
     //       }
     //       previous = current;
     //       current = current.next;
     //     }
     //     if (!current) {
     //       return false;
     //     }
 ​
     //直接使用封装好的方法
     let index = this.indexOf(data);
     return this.removeAt(index);
   }
 ​
   isEmpty() {
     return this.length == 0;
   }
   size() {
     return this.length;
   }
 }
 ​
 //创建链表
 let nodeList = new LinkedList();
 ​
 nodeList.appen("a");
 nodeList.appen("b");
 nodeList.appen("d");
 nodeList.appen("e");
 alert(nodeList.isEmpty());
 alert(nodeList.size());
 ​

双向链表

由于单项链表我们只能从一个方向遍历链表,如果我们向在遍历的过程中向前移动指针,这个就无法实现了。因此出现了双项链表,我们可以从头到尾或者从尾到头的进行遍历。遍历过程中指针可以前后移动,方便我们获取值。

缺点:

  1. 双项链表占用的内存空间较大。
  2. 实现双项链表比较繁琐。

双项链表的实现

 //节点类
 class Node {
   constructor(data) {
     this.prev = null;
     this.next = null;
     this.data = data;
   }
 }
 ​
 //双项链表类
 class doublyLinkedList {
   constructor() {
     //头指针
     this.head = null;
     //尾指针
     this.tail = null;
     //链表长度
     this.length = 0;
   }
 ​
   //append方法
   append(data) {
     const newNode = new Node(data);
     //判断是否是第一个节点
     if (this.length == 0) {
       this.head = newNode;
       this.tail = newNode;
     } else {
       this.tail.next = newNode;
       newNode.prev = this.tail;
       this.tail = newNode;
     }
     //length+1
     this.length += 1;
   }
 ​
   //插入节点
   insert(position, data) {
     //判断是否越界
     if (position < 0 || position > this.length) return false;
     //创建新的节点
     const newNode = new Node(data);
     //判断是否有元素
     if (this.length == 0) {
       this.head = newNode;
       this.tail = newNode;
     } else {
       //判断是否是插入是不是第一个位置
       if (position == 0) {
         newNode.next = this.head;
         this.head.prev = newNode;
         this.head = newNode;
       } else {
         let index = 0;
         let current = this.head;
         let previous = null;
         //找到要插入的位置
         while (index++ < position) {
           previous = current;
           current = current.next;
         }
         //如果是添加到最后
         previous.next = newNode;
         newNode.prev = previous;
         if (position == this.length) {
           newNode.next = null;
           this.tail = newNode;
         } else {
           //进行链接
           newNode.next = current;
           current.prev = newNode;
           //修改成功,返回true
         }
       }
     }
     this.length += 1;
     return true;
   }
   //获取指定节点的值
   get(position) {
     //越界判断
     if (position < 0 || position >= this.length) return null;
     if (position <= this.length / 2) {
       let index = 0;
       let current = this.head;
       while (index++ < position) {
         current = current.next;
       }
       return current.data;
     } else {
       let index = this.length - 1;
       let current = this.tail;
       while (index-- > position) {
         current = current.prev;
       }
       return current.data;
     }
   }
   //查找节点所在的位置
   indexOf(data) {
     let index = 0;
     let current = this.head;
     while (current) {
       if (current.data == data) {
         return index;
       }
       current = current.next;
       index += 1;
     }
     return -1;
   }
   //修改链表的某一个节点
   update(position, data) {
     if (position < 0 || position >= this.length) return false;
     if (position <= this.length / 2) {
       let index = 0;
       let current = this.head;
       while (index++ < position) {
         current = current.next;
       }
       current.data = data;
     } else {
       let index = this.length - 1;
       let current = this.tail;
       while (index-- > position) {
         current = current.prev;
       }
       current.data = data;
     }
     return true;
   }
   //删除下标对应节点
   removeAt(position) {
     if (position < 0 || position >= this.length) return false;
     //判断是否只有一个节点
     let current = this.head;
     if (this.length == 1) {
       this.head = null;
       this.tail = null;
     } else {
       //删除第一个节点
       if (position == 0) {
         this.head.next.prev = null;
         this.head = this.head.next;
       } else if (position == this.length - 1) {
         //删除最后一个节点
         current = this.tail;
         this.tail.prev.next = null;
         this.tail = this.tail.prev;
       } else {
         let index = 0;
         while (index++ < position) {
           current = current.next;
         }
         current.prev.next = current.next;
         current.next.prev = current.prev;
       }
     }
     this.length -= 1;
     return current.data;
   }
   //删除元素
   remove(data) {
     let index = this.indexOf(data);
     return this.removeAt(index);
   }
   //输出
   toString() {
     return this.backwardString();
   }
   //向后遍历
   backwardString() {
     //遍历到的元素,起初指向this.head,即第一个元素
     let current = this.head;
     let resultString = "";
 ​
     while (current) {
       resultString += current.data + " ";
       current = current.next;
     }
     return resultString;
   }
   //向前遍历
   forwardString() {
     let current = this.tail;
     let resultString = "";
 ​
     while (current) {
       resultString += current.data + " ";
       current = current.prev;
     }
     return resultString;
   }
   //判断链表是否为空
   isEmpty() {
     return this.length == 0 ? true : false;
   }
   //获取链表长度
   size() {
     return this.length;
   }
 ​
   //获取链表第一个元素
   getHead() {
     return this.head.data;
   }
   //获取链表最后一个元素
   getTail() {
     return this.tail.data;
   }
 }
 ​
 let DoublyList = new doublyLinkedList();
 ​
 DoublyList.insert(0, "foo");
 DoublyList.append("aaa");
 DoublyList.append("bbb");
 DoublyList.append("ccc");
 DoublyList.append("ddd");
 DoublyList.update(4, "fdfd");
 alert(DoublyList.toString());
 alert(DoublyList.getHead());
 alert(DoublyList.getTail());
 ​