JavaScript数据结构与算法学习总结--链表

    数据结构与算法这个内容很多人一听都会觉得头大,我在最开始接触到的时候也是云里雾里。因为大一刚学编程的时候,一直连入门都如不进去,导致后来学习数据结构和算法也提不起兴趣。直到开始接触Web前端,学习了JavaScript这门脚本语言,便想学习一些基于JS的数据结构与算法知识,并且记录下来。

 一、概述   

数据结构分为线性结构与非线性结构,我觉得线性结构相对于非线性结构更好理解一些。而在线性结构中,链表属于相对较复杂的一种数据结构。

    我们先简要提一下数组。数组是最简单最基本的结构,其每一个元素在内存中连续存放。我们可以通过数组下标index任意取得自己想要的元素。

    不同于数组,链表中的元素在内存中不连续存放,这点非常重要。虽然其存放并不连续,链表中的每一个节点也有迹可循,即每个节点是和其前一个节点直接相连的。因此,我们要取得链表中的任意一个节点,必须通过从链表的第一个元素开始依此查找,直到找到我们所需要的节点。这就像是我们要爬到山顶但没有地图,我们需要从起始点开始,根据路标来找到下一个地点,最终到达目的地。

    链表的几个特点:

    1. 链表元素在内存中不连续存放

    2. 链表的最后一个节点始终指向null

    下面来说说JS中的链表创建。JS是一种基于对象的语言。在JS中,我们描述链表中的节点,需要指明一个节点对象node,这个节点包含两部分:节点的内容element和它指向下一个节点的指针next:

     JavaScript数据结构与算法学习总结--链表_第1张图片


所以一个链表就是这样:

      JavaScript数据结构与算法学习总结--链表_第2张图片

二、链表结构的JavaScript描述    

接下来利用JS对链表进行描述:

我们先初始化一个链表:

function LinkedList() {
        var Node = function (element) {
            this.element = element;
            this.next = null; // 初始化 next指针为 null
        };
        var length = 0;  // 初始化 长度为0
        var head = null; // 初始化 链表头为null
}

主要包括:一个节点对象,初始化链表长度为0;初始化表头为null。因为对链表的任何操作都要从表头开始,所以表头的定义很重要!


好了,接下来我们要为这个链表添加一些方法。链表常用的方法主要包括:

1、向链表尾部添加一个节点 append(element)

// append方法 在最后一位添加元素
        this.append = function (element) {
            var node = new Node(element);
            var current;  // current为当前所指的节点,遍历时从head开始
            if (head === null) { // 如果是空链表,将node设置为链表头
                head = node;
            } else {
                current = head;  // 从head开始遍历
                while (current.next) {
                    current = current.next;
                }
                current.next = node; // 遍历到最后一个节点时,current指向最后一个节点,此时将其next指向要添加的node即可
            }
            length++; // 添加后记得链表长度加1
        };

    步骤:

     (1)初始化一个节点对象,初始化一个变量储存当前元素

     (2)判断链表是否为空。若为空,则将待添加的节点设置为表头

     (3)若非空,从head开始遍历,直到current.next为null,说明current遍历到了最后一个节点

     (4)将current.next指向node

     (5)链表长度length+1


2、移除指定位置节点 removeAt(position)

// removeAt 移除指定位置元素,并返回被移除的元素(对于最后一个元素不用特殊对待,依旧遵循此方法)
        this.removeAt = function (position) {
            if(position<0 || position>=length){ // 若位置索引不存在 返回null
                return null;
            }else{
                var current = head;
                var previous;
                var index = 0;
                if(position == 0){  // 删除第一个元素
                    head = current.next;
                }else{
                    while(index < position){
                        previous = current;
                        current = current.next;
                        index++;
                    }
                    previous.next = current.next;
                }
                length--;
                return current.element;  // 记住,current只是承载这个节点的容器,element才是需要返回的内容!
            }
        };

    步骤:

     (1)判断所删除的下标是否在链表范围内

     (2)若无此下标,返回null

     (3)若有此下标,初始化current为head,表示当前遍历指向;初始化previous,用来存储当前节点的前一个节点;初始化index为0,表示当前下标

     (4)若要删除的为第一个节点,直接将current.next赋值给head

     (5)若不是第一个节点,while循环遍历到要删除的位置,将current.next指向previous.next,就删除了当前元素。被删除元素被回收

     (6)返回被删除元素,链表长度length-1



3、在任意位置插入元素 insert(position,element)
// insert方法 在任意位置插入元素
        this.insert = function(position,element){
            if(position<0 || position>=length){
                return false;
            }else{
                var node = new Node(element); // 因为是插入节点,所以要先新建一个Node对象~
                var current = head; // 初始化 current 指向 head
                var previous;
                var index = 0;
                if(position == 0){  // 书上这里是 position===0,这里为什么要用===不是很明白。有读者理解的话可以告诉我哦~
                    node.next = current; // 一定要先将current的值赋给node.next。如果先将node给head,current就变成了node,会出错
                    head = node;
                }else{
                    while(index
  步骤:

       (1)判断所要插入的下标是否在链表范围内

       (2)若无此下标,返回null

       (3)若有此下标,初始化一个node节点;初始化current为head,表示当前遍历指向;

                 初始化previous,用来存储当前节点的前一个节点;初始化index为0,表示当前下标

       (4)若要插入的为第一个位置,直接将current赋值给node.next,再将node赋值给head

       (5)若不是第一个位置,while循环遍历到要添加的位置,将node.next指向current节点,再让previous.next指向node,就插入了新元素

       (6)链表长度length+1,返回true


以上就是链表中比较重要的几个方法啦~


除了这些,还有一些比较常用的也很好理解的方法:

indexOf(element):查找某元素在链表中的位置

remove(element):删除某元素(不同于removeAt已知元素下标,这个是已知元素内容)

isEmpty:判断是否为空

size:链表大小(长度)

toString:将链表内容转化为字符串便于输出

下面我们来展示这些方法JavaScript实现:

// indexOf 查找某元素下标
        this.indexOf = function(element){
            var current = head;
            var index = 0;
            while(current){
                if(current.element == element){
                   return index
                }
                current = current.next;
                index++;
            }
            return '-1';  // 如果遍历完了还找不到此元素,返回-1
        };

// remove 已知某节点内容,找到并删除他它
        this.remove = function(element){
            var index = this.indexOf(element);
            return this.removeAt(index); // 若没有此元素则index为-1,返回false
        };

// isEmpty 判断是否为空
        this.isEmpty = function(){
            return length===0;
        };

// size返回链表长度
        this.size = function(){
            return length;
        };

// toString方法,转换为字符串
        this.toString = function(){
            var current = head;
            var string = '';  // 初始化一个空string便于存放
            while(current){
                string = current.element;
                current = current.next;
            }
            return string;
        };






【参考资料】《学习JavaScript数据结构与算法》中文版


你可能感兴趣的:(数据结构,web前端)