第三章:数据结构与算法javascript描述: 列表

在日常生活中,人们经常使用列表:待办事项列表,购物清单,十佳榜单,最后十名榜单等。计算机也在使用列表,尤其是列表中元素保存的是太多时。当不需要一个很长的序列中查找元素,或对其进行排序时,列表显得尤为有用。反之,如果数据结构非常复杂,列表的作用就没有那么大了。

本章展示了如果创建一个简单的列表类,我们首先给列表给出抽象的数据类型定义,然后描述如何实现抽象数据类型(ADT),最后,分析几个列表适合解决的实际问题。

一,列表的抽象数据类型定义

为了设计列表的抽象数据类型,需要给出列表的定义,包括列表应该有哪些属性,应该在列表上执行哪些操作。

列表是一组有序的数据,每个列表中的数据项称为元素。在javascript中,列表中的元素可以是任意数据类型。列表中可以保存多少元素并没有限定(在实际使用时会受到程序内存的限制)。

不包含任何元素的列表称为空列表。列表中包含元素的个数称为列表的length。在内部的实现上,用一个变量的listSize保存列表中元素的个数。可以在列表的末尾append一个元素,也可以给列表起始位置insert一个元素。使用remove方法从列表中删除元素,使用clear方法清空列表中的所有的元素。

还可以使用toString()方法显示列表中所有的元素,使用getElement()方法显示当前元素。 列表中拥有描述元素位置的属性。列表有前有后(front和end),使用next()方法可以从当前元素移动到下一个元素,使用prev()方法可以移动当前元素到上一个元素。还可以使用moveTo(n)方法直接移动到指定的位置。currPos属性表示列表中当前的位置。

列表的抽象数据类型并未指明列表的存储结构,在本章的实现中,我们使用一个dataStore来存储元素。

列表的抽象数据定义。

listSize(属性) 列表中元素的个数
pos(属性) 列表的当前位置
length(属性) 返回列表中元素的个数
clear(方法) 清空列表中所有的元素
toString(方法) 返回列表中的字符串形式
getElement(方法) 返回当前位置的元素
insert(方法 ) 在现有元素后插入新元素
append(方法) 在列表末尾增加新元素
remove(方法) 从列表中删除元素
front(方法) 将列表中的当前元素设置到第一个元素
end(方法) 将列表中的当前元素位置移动到最后一个
prev(方法) 将当前位置后移一位
next(方法) 将当前位置前移一位
currPos(方法) 返回列表的当前位置
moveTo(方法) 将当前位置移动到指定位置

二,实现列表类

根据上面定义的列表抽象数据类型,可以直接实现一个List类,让我们从定义构造函数开始,虽然它本身并不是列表抽象数据类型的一部分。

    function List() {
        this.listSize = 0;
        this.pos = 0;
        this.dataStore = [];//创建一个空数组保存列表的元素
        this.clear = clear;
        this.find = find;
        this.toString = toString;
        this.insert = insert;
        this.append = append;
        this.remove = remove;
        this.front = front;
        this.end = end;
        this.prev = prev;
        this.next = next;
        this.length = length;
        this.currPos = currPos;
        this.moveTo = moveTo;
        this.getElement = getElement;
        this.contains = contains;
    }

1.append: 给列表添加元素

我们实现的第一个方法是append(),该方法给列表的下一个位置添加一个新的元素,这个位置刚好等于变量listSize的值。

    function append(element) {
        this.dataStore[this.listSize++] = element;
    }

当新元素就位后,变量的listSize就增加1

2.remove:从列表中删除元素

接下来我们看一下如何从列表中删除一个元素。remove()方法是List类中较难实现的一个方法。首先,要在列表中找到该元素,然后删除它。并且调整底层数组对象以填补删除元素后留下的空白。好消息是可以使用splice()方法来简化这一过程,我们先从一个辅助方法find()开始,该方法用于查找要删除的元素。

3.find在列表中查找某一元素

find()方法通过数组对象dataStore进行迭代,查找给定的元素。如果找到,就返回该元素的位置。如果找不到,返回-1.这是在数组中找不到元素时返回的标准值。我们可以在remove()方法中利用此值做错误校验。

remove方法使用find()方法返回的位置对数组dataStore进行截取。数组改变后,将变量listSize的值减1。以反映列表的最新长度。如果元素删除成功,则返回true,否则返回false.

    function remove(element) {
        var foundAt = this.find(element);
        if (foundAt > -1) {
            this.dataStore.splice(foundAt,1);
            --this.listSize;
            return true;
        }
        return false;
    }

4.length列表中有多少个元素

length()返回列表中元素的个数。

    function length() {
        return this.listSize;
    }

5.toStirng:显示列表中的元素

现在可以创建一个方法,用来显示列表中元素。下面是一段简短的代码,实现了toString()方法。

    function toString() {
        return this.dataStore;
    }

严格的来说,此方法返回的是一个数组,而不是一个字符串,但它的目的是为了显示当前的一个状态,因此返回一个数组就足够了。

我们暂时测试下目前实现的代码,检验我们之前创建的方法。

    var names = new List();
    names.append(‘锤锤‘);
    names.append(‘牛牛‘);
    names.append(‘豆豆‘);
    console.log(names.dataStore);//["锤锤", "牛牛", "豆豆"]
    names.remove(‘牛牛‘);

    console.log(names.dataStore);//["锤锤", "豆豆"]

6.insert: 向列表中插入一个元素

接下来我们要讨论的方法是insert()。如果在前面的列表中删除“牛牛”,现在又想将它放回原来的位置,该怎么办?insert()方法需要知道元素插入到什么位置(因此,现在的实现方法是假设插入到列表中某个元素之后,知道足额写后,就可以定义insert()方法啦)。

    function insert(element, after) {
        var insertPos = this.find(after);
        if (insertPos > -1) {
            this.dataStore.splice(insertPos + 1, 0 ,element);
            ++this.listSize;
            return true
        }
        return false;
    }

实现的过程中,insert()方法使用到find()方法,find()方法会找到传入的after参数在列表中的位置,找到该位置后,使用splice()得到将元素插入该位置后,然后将变量listSize加1并返回true.表明插入成功。

7.clear:清空列表中的所有元素。

接下来,我们需要一个方法清空列表中所有元素,为插入新元素腾出空间。

    function clear() {
        delete this.dataStore;
        this.dataStore = [];
        this.listSize = this.pos = 0;
    }

clear()方法使用delete操作符删除数组dataStore,接着创建新的空数组。最后一行将listSize和pos值设为0,表明是一个空的新列表。

8.判定给定值是否在列表中

当需要判断一个给定值是否在列表中时,contains()方法就变得很有用。下面是该方法定义:

    function contains(element) {
        for (var i = 0; i < this.dataStore.length; ++i) {
            if (this.dataStore[i] == element) {
                return true;
            }
        }
        return false;
    }

9.遍历列表

最后的一组方法允许用户在列表上自由移动,最后一个方法getElement()返回列表当前的元素

    function front() {
        this.pos = 0;
    }

    function end() {
        this.pos = this.listSize - 1;
    }

    function prev() {
        if (this.pos > 0) {
            --this.pos;
        }
    }

    function next() {
        if (this.pos < this.listSize - 1) {
            ++this.pos;
        }
    }

    function currPos() {
        return this.pos;
    }

    function moveTo(position) {
        this.pos = position;
    }

    function getElement() {
        return this.dataStore[this.pos]
    }

现在我们创建一个由姓名组成的列表,来展示怎么使用它。

    var names = new List();
    names.append(‘锤锤‘);
    names.append(‘牛牛‘);
    names.append(‘豆豆‘);
    names.append(‘羊羊‘);
    names.append(‘兔兔‘);
    names.append(‘花花‘);

现在移动到列表中的第一个元素,并且显示它。

    names.front();
    console.log(names.getElement());//显示 “锤锤”

接下来向前移动一个单位并显示它:

    names.next();
    console.log(names.getElement()); //显示 “牛牛”

上面的代码展示这些行为实际上是迭代器的概念,这也是接下来要讨论的内容。

3.使用迭代器访问列表

使用迭代器,可以不必关系数据的内部存储方式。以实现对列表的遍历。前面提到的方法fornt(),end(),prev(),next()和currPos就实现了List类的一个迭代器。以下是和使用数组索引的方式相比,使用迭代器的一些优点。

  1. 访问列表元素时不必关心底层的数据存储结构
  2. 当为列表添加一个元素时,索引的值就不对了,此时只用更新列表,而不用更新迭代器。
  3. 可以用不同类型的数据存储方式实现List类,迭代器为访问列表里的元素提供了统一的方式。

了解了这些优点后,我使用一个迭代器遍历列表的例子:

    for(names.front(); names.currPos() < names.length(); names.next()) {
        console.log(names.getElement())
    }

在for循环的一开始,将列表中的当前位置设置为第一个元素。只要currPos的值小于列表的长度,就一直循环,每一次循环都调用next()方法将当前的位置向前移动一位。

同理,还可以从后向前遍历列表,代码如下:

    for (names.front(); names.currPos() >= 0; names.prev()) {
        console.log(names.getElement())
    }

循环从列表的最后一个元素开始,当 当前位置大于或等于0时,调用prev()方法后移一位。

迭代器只是用来在列表上随意移动,而不应该和人和为列表增加或删除元素的方法一起使用。

四,一个基于列表的应用。

1.读取文本

为了展示如何使用列表,我们实现一个类似Redbox的租赁自助查询系统。

var _movies = "(1)RogueTrader(《魔鬼营业员》)NNN(2)TradingPlaces(《颠倒乾坤》)NNN(3)WallStreet(《华尔街》)NNN(4)WallStreet2:MoneyNeverSleeps(《华尔街2:金钱永不眠》)NNN(5)BoilerRoom(《开水房》)NNN(6)GlengarryGlen Ross(《拜金一族》)NNN(7)Enron:TheSmartestGuysInTheRoom(《安然风暴:屋内聪明人》)NNN(8)Trader(《交易员》)NNN(9)AmericanPsycho(《美国精神病人》)NNN(10)BonfireoftheVanities(《虚荣的篝火》)";
var movies = _movies.split("NNN");

上面的split()程序将字符串分割为数组,多一个空格,虽然多一个空格无伤大雅,但是在比较字符串时是个灾难,因此,我们需要在循环里使用trim()方法删除每个数组元素末尾的空格。要是有一个函数能将这些操作封装起来就再好不过了。

   function createArr(file){
      var arr = file.split("NNN");
      for (var i = 0; i < arr.length; ++i) {
         arr[i] = arr[i].trim();
      }
      return arr;
   }

2.使用列表管理影碟租赁

下面要将数组保存到一个列表,代码如下:

    var movieList = new List();

    for (var i = 0;  i < movies.length; ++i) {
        movieList.append(movies[i])
    }

现在可以写一个函数来显示影碟清单了:

    function displayList(list) {
        for (list.front(); list.currPos() < list.length(); list.next()) {
            console.log(list.getElement())
        }
    }

displayList()函数对于原生的数据类型没有什么问题,比如由字符串组成的列表。但是它用不了自定义类型。比如我们将下面定义的Customer对象。让我对它稍微做修改,让它可以发现列表是由Customer对象组成的。这样就可以对应的进行显示了。下面是重新定义的displayList()函数:

    function displayList(list) {
        for (list.front(); list.currPos() < list.length(); list.next()) {
            if (list.getElement() instanceof Customer) {
                console.log(list.getElement()[‘name‘] + ", " +
                    list.getElement()["movie"]
                    );
            }

            else {
                console.log(list.getElement())
            }
        }
    }

对于列表中的每一个元素,都使用instanceof操作符判断元素是否是Customer对象。如果是,就使用name和movie做索引。得到客户检出的值。如果不是,就返回该元素即可。

现在已经有了movies,还需要创建一个新的列表,Customers,用来保存在系统中检出电影的客户:

    var customers = new List(); 

该列表包含Customer对象,该对象由用户的姓名和用户检出电影组成。下面是Costomer对象的构造函数。

    function Customer(name, movie) {
        this.name = name;
        this.movie = movie;
    }

接下来,需要创建一个允许用户检出的电影函数。该函数有两个参数:客户端姓名和客户想要检出的电影。如果改电影目前可以租赁,该方法会从清单里删除该元素,同时加入客户列表customers.这个操作会用到列表的contains()方法。

下面是用于检出电影的函数:

    function checkout(name, movie, filmList, customerList) {
        if (movieList.contains(movie)) {
            var c = new Customer(name, movie);
            customerList.append(c);
            filmList.remove(movie)
        }
        else {
            console.log(movie + "is not available")
        }
    }

下面使用简单的代码测试checkout函数

    var movies =  createArr(_movies);
    var movieList = new List();
    var customers = new List();
    for (var i = 0; i < movies.length; ++i) {
        movieList.append(movies[i])
    }

    //显示xx
    displayList(movieList)

    checkout("RogueTrader","交易员",movieList,customers);
    //xx

    displayList(customers)

对于列表的方法,就更新至此,可以根据自己的需要,增加更健壮的方法去实现。

 符:测试代码

    function List() {
        this.listSize = 0;
        this.pos = 0;
        this.dataStore = [];//创建一个空数组保存列表的元素
        this.clear = clear;
        this.find = find;
        this.toString = toString;
        this.insert = insert;
        this.append = append;
        this.remove = remove;
        this.front = front;
        this.end = end;
        this.prev = prev;
        this.next = next;
        this.length = length;
        this.currPos = currPos;
        this.moveTo = moveTo;
        this.getElement = getElement;
        this.contains = contains;
    }

    //给列表添加元素
    function append(element) {
        this.dataStore[this.listSize++] = element;
    }

    //find查找方法
    function find(element) {
        for (var i = 0;i < this.dataStore.length; ++i) {
            if (this.dataStore[i] == element) {
                return i;
            }
        }
        return -1;
    }

    //remove方法
    function remove(element) {
        var foundAt = this.find(element);
        if (foundAt > -1) {
            this.dataStore.splice(foundAt,1);
            --this.listSize;
            return true;
        }
        return false;
    }

    //length
    function length() {
        return this.listSize;
    }

    //toString
    function toString() {
        return this.dataStore;
    }

    //insert
    function insert(element, after) {
        var insertPos = this.find(after);
        if (insertPos > -1) {
            this.dataStore.splice(insertPos + 1, 0 ,element);
            ++this.listSize;
            return true
        }
        return false;
    }

    //clear
    function clear() {
        delete this.dataStore;
        this.dataStore = [];
        this.listSize = this.pos = 0;
    }

    //contains判断给定值是否在列表中
    function contains(element) {
        for (var i = 0; i < this.dataStore.length; ++i) {
            if (this.dataStore[i] == element) {
                return true;
            }
        }
        return false;
    }

    function front() {
        this.pos = 0;
    }

    function end() {
        this.pos = this.listSize - 1;
    }

    function prev() {
        if (this.pos > 0) {
            --this.pos;
        }
    }

    function next() {
        if (this.pos < this.listSize - 1) {
            ++this.pos;
        }
    }

    function currPos() {
        return this.pos;
    }

    function moveTo(position) {
        this.pos = position;
    }

    function getElement() {
        return this.dataStore[this.pos]
    }

    //var names = new List();
    //names.append(‘锤锤‘);
    //names.append(‘牛牛‘);
    //names.append(‘豆豆‘);
    //names.append(‘羊羊‘);
    //names.append(‘兔兔‘);
    //names.append(‘花花‘);

    //console.log(names.dataStore);

    //names.front();
    //console.log(names.getElement());//显示 “锤锤”

    //names.next();

    //console.log(names.getElement()); //显示 “豆豆”

    //  for(names.front(); names.currPos() < names.length(); names.next()) {
    //     console.log(names.getElement())
    // }

    var _movies = "(1)RogueTrader(《魔鬼营业员》) NNN(2)TradingPlaces(《颠倒乾坤》)NNN(3)WallStreet(《华尔街》)NNN(4)WallStreet2:MoneyNeverSleeps(《华尔街2:金钱永不眠》)NNN(5)BoilerRoom(《开水房》)NNN(6)GlengarryGlen Ross(《拜金一族》)NNN(7)Enron:TheSmartestGuysInTheRoom(《安然风暴:屋内聪明人》)NNN(8)Trader(《交易员》)NNN(9)AmericanPsycho(《美国精神病人》)NNN(10)BonfireoftheVanities(《虚荣的篝火》)   ";

   function createArr(file){
      var arr = file.split("NNN");
      for (var i = 0; i < arr.length; ++i) {
         arr[i] = arr[i].trim();
      }
      return arr;
   }

   var movies =  createArr(_movies)

   //将数组保存到一个列表
   var movieList = new List();

    for (var i = 0;  i < movies.length; ++i) {
        movieList.append(movies[i])
    }

    //console.log(movieList)
    var customers = new List(); 

    //显示影碟清单
    function displayList(list) {
        for (list.front(); list.currPos() < list.length(); list.next()) {
            if (list.getElement() instanceof Customer) {
                console.log(list.getElement()[‘name‘] + ", " +
                    list.getElement()["movie"]
                    );
            }

            else {
                console.log(list.getElement())
            }
        }
    }

    //displayList(movieList.dataStore)

    function Customer(name, movie) {
        this.name = name;
        this.movie = movie;
    }

    //检出电影的函数
    function checkout(name, movie, filmList, customerList) {
        if (movieList.contains(movie)) {
            var c = new Customer(name, movie);
            customerList.append(c);
            filmList.remove(movie)
        }
        else {
            console.log(movie + "is not available")
        }
    }

    displayList(movieList)
时间: 2024-10-10 07:33:26

第三章:数据结构与算法javascript描述: 列表的相关文章

数据结构与算法javascript描述

<数据结构与算法javascript描述>--数组篇 导读: 这篇文章比较长,介绍了数组常见的操作方法以及一些注意事项,最后还有几道经典的练习题(面试题). 数组的定义: JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性,索引可能是整数.然而,这些数字索引在内部被转换为字符串类型,这是因为 JavaScript 对象中的属性名必须是字符串.在内部被归类为数组.由于 Array 在 JavaScript 中被当作对象,因此它有许多属性和方法可以在编程时使用. 使

翻阅《数据结构与算法javascript描述》--数组篇

导读: 这篇文章比较长,介绍了数组常见的操作方法以及一些注意事项,最后还有几道经典的练习题(面试题). 数组的定义: JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性,索引可能是整数.然而,这些数字索引在内部被转换为字符串类型,这是因为 JavaScript 对象中的属性名必须是字符串.在内部被归类为数组.由于 Array 在 JavaScript 中被当作对象,因此它有许多属性和方法可以在编程时使用. 使用数组: 1.创建数组 使用 [] 操作符 ,var a

《数据结构与算法JavaScript描述》

<数据结构与算法JavaScript描述> 基本信息 作者: (美)Michael McMillan 译者: 王群锋 杜欢 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115363398 上架时间:2014-8-5 出版日期:2014 年8月 开本:16开 版次:1-1 所属分类:计算机 > 软件与程序设计 > 网络编程 > javascript 更多关于>>> <数据结构与算法JavaScript描述> 编辑推荐 随着

数据结构与算法javascript描述笔记--数组篇

数组的定义: JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性,索引可能是整数.然而,这些数字索引在内部被转换为字符串类型,这是因为 JavaScript 对象中的属性名必须是字符串.在内部被归类为数组.由于 Array 在 JavaScript 中被当作对象,因此它有许多属性和方法可以在编程时使用. 使用数组: 1.创建数组 ① 使用 [] 操作符 ,var arr=[] ,该方法效率最高. ② 调用 Array 的构造函数创建数组,var myArr=new

第一章:数据结构与算法javascript描述

在前端工程师中,常常有一种声音,我们为什么要学数据结构与算法,没有数据结构与算法,我们一样很好的完成工作.实际上,算法是一个宽泛的概念,我们写的任何程序都可以称为算法,甚至往冰箱里放大象,也要通过开门,放入,关门这样的规划,我们也可以视作为一种算法.可以说:简单的算法是人类的本能.而算法的知识的学习则是吸取前人的经验.对于复杂的问题进行归类,抽象,帮助我们脱离刀耕火种的时代,系统掌握一个算法的过程. 随着自身知识的增长,不论是做前端,服务端还是客户端,任何一个程序员都会开始面对更加复杂的问题,算

《数据结构与算法-Javascript描述》

今年的上半年,项目原因大部分时间在写js,这期间把easyui,echarts,bootstrap都用了点皮毛,写的多了,自然也多了些感觉,不过仅局限于运用层面,于是决定再系统的看些javascript方面的书,强化运用能力,便有了这本~来自于国内知名公司前端工程师翻译自国外的书,见名知意用Javascript角度来讲数据结构和算法,一方面可以把javascript的基础知识加强,一方面加深数据结构以及算法的理解和应用. 全书篇幅不长,第一章简明扼要对javascript做了简单的介绍,基本语法

《数据结构与算法 Javascript描述》读书笔记

当初买这本书的原因,在意的是有没有什么令人惊喜的东西,特别是针对Javascript代码的奇思妙想,所以就买下了这本书. 在买的几本书里面,最先看的也是这一本,但看起目录就觉得不大妙,翻起内容时候,才发现真不是那么的新奇的东西,甚至来说几乎不关Javascript的事. 我知道作者讲的是数据结构思想的东西,但是真跟Javascript没关系.作者几乎是把C语言算法的那一套搬到Javascript,看得我直满满都是C语言的影子.我不是排斥Javascript来讲算法的东西,只是觉得Javascri

&lt;&lt;数据结构与算法Javascript描述&gt;&gt;随笔笔记

序言: 程序问题三步走-->① 将问题抽象为数据结构 -->② 并根据相关算法来操作数据结构 -->③ 调用API来显示结果 一.数组和列表 二.栈和队列 三.链表 四.字典 五.散列 六.集合 七.二叉树 八.图 九.热门算法 

数据结构与算法JavaScript描述——队列

队列是一种列表,不同的是队列只能在队尾插入元素,在队首删除元素. 队列用于存储按顺序排列的数据,先进先出,这点和栈不一样,在栈中,最后入栈的元素反而被优先处理. 可以将队列想象成在银行前排队的人群,排在最前面的人第一个办理业务,新来的人只能在后面排队,直到轮到他们为止. 队列是一种先进先出的数据结构. 队列被用在很多地方,比如提交操作系统执行的一系列进程.打印任务池等,一些仿真系统用队列来模拟银行或杂货 店里排队的顾客. 1.对队列的操作 队列的两种主要操作是:向队列中插入新元素和删除队列中的元