zoukankan      html  css  js  c++  java
  • 链表数据结构与算法JavaScript描述(6)

    链表


    概念

    链表是由一组节点组成的集合。

    每个节点都使用一个对象的引用指向它的后继。

    指向另一个节点的引用叫做

    许多链表的实现都在链表最前面有一个特殊节点,叫做头节点

    链表的尾元素指向一个null节点。

    与数组的对比:

    • js中的数据被实现成了对象,与其他语言(比如C++和Java)的数组相比,效率很低。
    • 如果你发现数组在使用时很慢,就可以考虑使用链表来代替它。
    • 除了对数据的随机访问,链表几乎可以用在任何可以使用一维数组的情况中。
    • 如果需要随机访问,数组仍然是更好的选择。

    设计一个基于对象的链表

    我们设计的链表包含两个类。Node 类用来表示节点,LinkedList 类提供了插入节点、删除节点、显示列表元素的方法,以及其他一些辅助方法。

    Node 类

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

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

    LinkedList 类

    LinkedList 类提供了对链表进行操作的方法。该类的功能包括插入删除节点、在列表中查找给定的值。该类也有一个构造函数,链表只有一个属性,那就是使用一个Node 对象来保存该链表的头节点。

    function LinkedList() {
        this.head = new Node('head');
        this.find = find;
        this.display = display;
        this.insert = insert;
        this.findPrevious = findPrevious;
        this.remove = remove;
    }
    
    查找节点 find
    function find(item){
        var currNode = this.head;
        while(currNode.element != item){
            currNode = currNode.next;
        }
        return currNode;
    }
    
    插入新节点 insert
    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;
        }
    }
    
    查找节点的前一个节点 findPrevious

    从链表中删除节点时,需要先找到待删除节点前面的节点。找到这个节点后,修改它的next 属性,使其不再指向待删除节点,而是指向待删除节点的下一个节点。

    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 previousNode = this.findPrevious(item);
        if(previousNode.next != null){
            previous.next = previousNode.next.next;
        }
    }
    

    双向链表

    尽管从链表的头节点遍历到尾节点很简单,但发拿过来,从后向前遍历则没那么简单,可以给Node 对象增加一个previous 属性,该属性存储指向前驱节点的链接。

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

    双向链表的 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( )方法比单向链表的效率更高,因为不需要再查找前驱节点了。

    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 lastNode = this.head;
        while(lastNode.next != null){
            lastNode = lastNode.next;
        }
        return lastNode;
    }
    

    反序显示双向链表中的元素

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

    循环链表

    循环链表和单向链表相似,节点类型都是一样的。唯一的区别是,在创建循环链表时,让其头节点的 next 属性指向它本身,即

    head.next = head
    

    这种行为会传到至链表中的每个节点,使得每个节点的next属性都指向链表的头节点。换言之,链表的尾节点指向头节点,形成了一个循环链表。


    链表的其他方法

    为了使链表更好用,需要再定义其他一些方法:

    • advance(n) 在链表中向前移动 n 个节点
    • back(n) 在双向链表中向后移动 n 个节点
    • show( ) 只显示当前节点

    练习

    实现advance(n)方法,使当前节点向前移动 n 个节点。

    分析:讲真,其实我没明白题目这里的向前移动指的是当前节点更靠近head 还是更靠近尾节点。不过这不重要,原理都差不多,分为两步,先将当前节点从链表中删除,然后在顺着链表移动(前移或后移)n个节点处插入该节点,dang dang dang dang ,搞定!下面是伪代码,只是表达下思路:

    function advance(n, item){
        var currNode = this.find(item);
        var newPreviousNode = null;
        for(n; n > 0; n--){
            newPreviousNode = currNode.next;
        }
        this.remove(currNode);
        this.insert(item, newPreviousNode);
    }
    
  • 相关阅读:
    Redis笔记
    java多线程 interrupt(), interrupted(), isInterrupted()方法区别
    策略模式
    外观模式
    Java线程池原理与架构分析
    状态模式
    模板方法模式
    LeetCode | Path-Sum
    实用工具
    IDEA springboot配置
  • 原文地址:https://www.cnblogs.com/clover77/p/9188493.html
Copyright © 2011-2022 走看看