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

来源:互联网 发布:php 压缩图片 编辑:程序博客网 时间:2024/05/21 17:35

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

 一、概述   

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

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

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

    链表的几个特点:

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

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

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

     


所以一个链表就是这样:

      

二、链表结构的JavaScript描述   

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

我们先初始化一个链表:

<span style="font-size:14px;"><span style="font-size:12px;">function LinkedList() {        var Node = function (element) {            this.element = element;            this.next = null; // 初始化 next指针为 null        };        var length = 0;  // 初始化 长度为0        var head = null; // 初始化 链表头为null}</span></span>

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


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

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

<span style="font-size:14px;"><span style="font-size:12px;">// 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        };</span></span>

    步骤:

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

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

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

     (4)将current.next指向node

     (5)链表长度length+1


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

<span style="font-size:14px;"><span style="font-size:12px;">// 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才是需要返回的内容!            }        };</span></span>

    步骤:

     (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)
<span style="font-size:14px;"><span style="font-size:12px;">// 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<position){ // while循环,不停地向后移直到要插入的地方                        previous = current;                        current = current.next;                        index++;                    }// 循环结束时,current为当前要插入位置上的节点                    node.next = current; // 将当前位置上的节点赋给node.next                    previous.next = node; // 让previous.next 指向node(原本指向的是current)                }                length++;                return true; // 插入成功            }        };</span></span>
 步骤:

       (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数据结构与算法》中文版


1 0