JavaScript算法学废宝典--前置技能一--链表

链表是由一组节点组成的集合。每个节点都使用一个对象的引用指向它的后继,指向另一个节点的引用叫做链。数组元素靠它们的位置进行引用,链表元素则是靠相互之间的关系进行引用。

基于对象设计一个链表

Node类

Node类包含两个属性:element保存节点上的数据,next保存指向下一个节点的链接,使用构造函数来创建节点。

function Node(element){
   this.element = element;
   this.next = null;
}

LinkedList类

该类的功能包括插入删除节点,在列表中查找给定的值。该类也是一个构造函数,链表只有一个属性,使用一个Node对象来保存该链表的头结点。

function LList(){
   this.head = new Node("head");
   this.find = find;
   this.insert = insert;
   this.display = display;
}

插入新节点

向链表中插入新节点时,需要明确指出要在哪个节点前面或者后面插入,这里需要先创建一个辅助方法find()用来查找给定数据并返回给定数据对应的节点。
find()方法演示了如何在链表上进行移动。首先,创建一个新节点,并将链表的头节点赋给这个新创建的节点。然后在链表上进行循环,如果当前节点的 element属性和我们要找的信息不符,就从当前节点移动到下一个节点。如果查找成功,该方法返回包含该数据的节点,否则,返回null。

function find(item){
    var currNode = this.head;
    while(currNode.element!=item){
        currNode = currNode.next;
    }
    return currNode;
}

通过find()找到“后面”的节点,就可以通过insert()方法将新节点插入链表中了,首先将新节点的next属性设置为“后面”节点的next属性对应的值,然后设置“后面”节点的next属性指向新节点。

function insert(newElement,item){
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    current.next = newNode;
}

定义一个display()方法显示链表中的元素:

function display(){
    var currNode = this.head;
    while(!(currNode.next == null)){
        console.log(currNode.next.element);
        currNode = currNode.next;
    }
}

汇总以上全部代码并进行测试:

function Node(element){
    this.element = element;
    this.next = null;
}
function LList(){
    this.head = new Node("head");
    this.find = find;
    this.insert = insert;
    this.display = display;
}
function find(item){
    var currNode = this.head;
    while(currNode.element!=item){
        currNode = currNode.next;
    }
    return currNode;
}
function insert(newElement,item){
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    current.next = newNode;
}
function display(){
    var currNode = this.head;
    while(!(currNode.next == null)){
        console.log(currNode.next.element);
        currNode = currNode.next;
    }
}
var cities = new LList();
cities.insert("Conway","head");
cities.insert("Russellville","Conway");
cities.insert("Alma","Russellville");
cities.display();//Conway  Russellville  Alma

从链表中删除一个节点

当删除节点时,需要先找到待删除节点前面的节点。找到后修改它的next属性,使其执行待删除节点的下一个节点,通过一个方法findPrevious()来查找指定节点的前一个节点。
该方法遍历链表检查每一个节点的下一个节点是否是待删除节点,找到就返回该节点。

function findPrevious(item){
   var currNode = this.head;
   while(!(currNode.next == null) && (currNode.next.element != item)){
       currNode = currNode.next;
   }
   return currNode;
}

接下来是删除方法remove()

function remove(item){
    var prevNode = this.findPrevious(item);
    if(!(prevNode.next == null)){
        prevNode.next = prevNode.next.next;
    }
}

修改LList类添加两个新加的方法

function LList(){
    this.head = new Node("head");
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.findPrevious = findPrevious;
    this.remove = remove;
}

汇总以上全部代码并进行测试:

function Node(element){
    this.element = element;
    this.next = null;
}
function LList(){
    this.head = new Node("head");
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.findPrevious = findPrevious;
    this.remove = remove;
}
function find(item){
    var currNode = this.head;
    while(currNode.element!=item){
        currNode = currNode.next;
    }
    return currNode;
}
function insert(newElement,item){
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    current.next = newNode;
}
function display(){
    var currNode = this.head;
    while(!(currNode.next == null)){
        console.log(currNode.next.element);
        currNode = currNode.next;
    }
}
function findPrevious(item){
    var currNode = this.head;
    while(!(currNode.next == null) && (currNode.next.element != item)){
        currNode = currNode.next;
    }
    return currNode;
}
function remove(item){
    var prevNode = this.findPrevious(item);
    if(!(prevNode.next == null)){
        prevNode.next = prevNode.next.next;
    }
}
var cities = new LList();
cities.insert("Conway","head");
cities.insert("Russellville","Conway");
cities.insert("Carlisle","Russellville");
cities.insert("Alma","Carlisle");
cities.display();//Conway Russellville Carlisle Alma
cities.remove("Carlisle");
console.log('删除Carlisle==============')//删除Carlisle==============
cities.display();//Conway Russellville Alma

双向链表

尽管从链表的头节点遍历到尾节点很简单,但反过来,从后向前遍历则没那么简单。通过给 Node 对象增加一个属性,该属性存储指向前驱节点的链接,这样就容易多了。此时向链表插人一个节点需要更多的工作,我们需要指出该节点正确的前驱和后继。但是在从链表中删除节点时,效率提高了,不需要再查找待删除节点的前驱节点了。
为Node类增加一个previous属性:

function Node(element){
     this.element = element;
     this.next = null;
     this.previous = null;
 }

修改LList类,删除查找上一个节点的方法findPrevious:

function LList(){
   this.head = new Node("head");
   this.find = find;
   this.insert = insert;
   this.display = display;
   this.remove = remove;
   this.dispReverse = dispReverse;
   this.findLast = findLast;
}

修改insert方法,赋值previous属性,设置该节点的前驱:

function insert(newElement,item){
     var newNode = new Node(newElement);
     var current = this.find(item);
     newNode.next = current.next;
     newNode.previous = current;
     current.next = newNode;
 }

双向链表的 remove()方法比单向链表的效率更高,因为不需要再查找前驱节点了。首先需要在链表中找出存储待删除数据的节点,然后设置该节点前驱的 next 属性,使其指向待删除节点的后继,设置该节点后继的 previous 属性,使其指向待删除节点的前驱。

function remove(item){
    var currNode = this.find(item);
    if(!(currNode.next==null)){
        currNode.previous.next = currNode.next;
        currNode.next.previous = currNode.previous;
        currNode.next = null;
        currNode.previous = null;
    }
}

设置findLast()方法查找链表中的最后一个节点,用来辅助反序显示链表元素这类任务:

function findLast(){
   var currNode = this.head;
   while(currNode.next!=null){
       currNode = currNode.next;
   }
   return currNode;
}

反序显示链表中的元素:

function dispReverse(){
   var currNode = this.head;
   currNode = this.findLast();
   while(!(currNode.previous == null)){
       console.log(currNode.element);
       currNode = currNode.previous;
   }
}

汇总以上全部代码并进行测试:

function Node(element){
    this.element = element;
    this.next = null;
    this.previous = null;
}
function LList(){
    this.head = new Node("head");
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.remove = remove;
    this.dispReverse = dispReverse;
    this.findLast = findLast;
}
function dispReverse(){
    var currNode = this.head;
    currNode = this.findLast();
    while(!(currNode.previous == null)){
        console.log(currNode.element);
        currNode = currNode.previous;
    }
}
function findLast(){
    var currNode = this.head;
    while(currNode.next!=null){
        currNode = currNode.next;
    }
    return currNode;
}
function find(item){
    var currNode = this.head;
    while(currNode.element!=item){
        currNode = currNode.next;
    }
    return currNode;
}
function insert(newElement,item){
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    newNode.previous = current;
    current.next = newNode;
}
function display(){
    var currNode = this.head;
    while(!(currNode.next == null)){
        console.log(currNode.next.element);
        currNode = currNode.next;
    }
}
function remove(item){
    var currNode = this.find(item);
    if(!(currNode.next==null)){
        currNode.previous.next = currNode.next;
        currNode.next.previous = currNode.previous;
        currNode.next = null;
        currNode.previous = null;
    }
}

var cities = new LList();
cities.insert("Conway","head");
cities.insert("Russellville","Conway");
cities.insert("Carlisle","Russellville");
cities.insert("Alma","Carlisle");
cities.display();//Conway Russellville Carlisle Alma
cities.remove("Carlisle");
console.log('删除Carlisle==============')//删除Carlisle==============
cities.display();//Conway Russellville Alma
console.log('反序==============')//反序==============
cities.dispReverse();//Alma Russellville Conway

循环链表

循环链表和单向链表相似,区别是在创建循环链表的时候,每个头节点的next属性指向它的本身,即:

head.next = head

这样每个节点的next属性都指向链表的头节点,链表的尾节点也指向头节点,形成了循环链表。
注意修改判断条件不然会进入死循环。例如:

function display(){
    var currNode = this.head;
    while(!(currNode.next == null) && currNode.next.element != "head"){
        console.log(currNode.next.element);
        currNode = currNode.next;
    }
}

全部代码

function Node(element){
   this.element = element;
   this.next = null;
}
function LList(){
   this.head = new Node("head");
   this.head.next = this.head;
   this.find = find;
   this.insert = insert;
   this.display = display;
   this.findPrevious = findPrevious;
   this.remove = remove;
}
function find(item){
   var currNode = this.head;
   while(currNode.element!=item){
       currNode = currNode.next;
   }
   return currNode;
}
function insert(newElement,item){
   var newNode = new Node(newElement);
   var current = this.find(item);
   newNode.next = current.next;
   current.next = newNode;
}
function display(){
   var currNode = this.head;
   while(!(currNode.next == null) && currNode.next.element != "head"){
       console.log(currNode.next.element);
       currNode = currNode.next;
   }
}
function findPrevious(item){
   var currNode = this.head;
   while(!(currNode.next == null) && (currNode.next.element != item) && currNode.next.element != "head"){
       currNode = currNode.next;
   }
   return currNode;
}
function remove(item){
   var prevNode = this.findPrevious(item);
   if(!(prevNode.next == null)){
       prevNode.next = prevNode.next.next;
   }
}

var cities = new LList();
cities.insert("Conway","head");
cities.insert("Russellville","Conway");
cities.insert("Carlisle","Russellville");
cities.insert("Alma","Carlisle");
cities.display();
cities.remove("Carlisle");
console.log('删除Carlisle==============')
cities.display();

你可能感兴趣的:(javascript,链表,开发语言)