数据结构

对于数据结构,没有官方的一个定义。按照普遍的理解可以这样区分

  • 数据结构就是在计算机中,存储和组织数据的方式。打个比方,在家里,有桌子,椅子,床等。
  • 计算机中数据量是非常庞大,如何高效的方式去存储和组织数据。打个比方,如何布置家里,整理会让家里看起来更加宽敞。
  • 打个更贴切的比喻,比如国家的图书馆,存放了上万本的书籍。我们需要通过标签,类别,在合适的时候,能快速的定位,取到一本书。

常见的数据结构

  • 栈(Stack)
  • 堆(Heap)
  • 数组(Array)
  • 链表(Linked List)
  • 队列(Queue)
  • 图(Graph)
  • 散列表(Hash)
  • 数(Tree)

每一种都有对应的应用场景,不通数据结构对应的不同操作,性能也都是不一样。有的查询特别快,插入头和尾速度很快,比如链表。有的做范围查找很快。因此开发中选型那种数据结构,是一个很关键的部分。

  • 关于栈,结合这么一个贴切的例子来理解,一条单车道的单行线,一端被堵住了,而另一端入口处没有任何提示信息,堵住之后就只能后进去的车子先出来,这时这个堵住的单行线就可以被看作是一个栈容器,车子开进单行线的操作叫做入栈,车子倒出去的操作叫做出栈。在车流量较大的场景中,就会发生反复的入栈、栈满、出栈、空栈和再次入栈,一直循环。所以,栈就是类似于一端被堵住的单行线,车子类似于栈中的元素,栈中的元素满足后进先出的特点。你可以参看下图:
栈示意图

先来看如下的代码,通过代码来体现出栈在JS中的应用。

var a = 2
function add(b, c) {
  console.log(' == add ==');
  return b + c
}
function addAll(b, c) {

  console.log(' == addAll ==');
  var d = 10
  result = add(b, c)
  return a + result + d
}
addAll(3, 6)
  • 首先执行上下文
  • 调用addAll的方法,把该函数压到栈上。
  • 调用add方法,再把该函数压到栈上。
  • add的方法执行完毕,出栈。
  • addAll的方法执行完毕,出栈。

初始化上下文的时候,我们可以看出来,栈上已经压了一个初始化的,这时候看global的数据,a已经赋值为2,add方法和addall的方法也都初始化完成了。

初始化上下文

执行到addall的方法,可以看到,在stack中,也就是栈中,进入了一个函数addall,而且也看到了local开面数据已经赋值上去了。

image.png

执行到add的方法的时候,可以看到。add的方法也加入到了栈的队列中。并且当前这个方面的私有属性也都看到。

image.png

最后一次执行的时候,移除add的栈内存,在移除addall的栈内存,全部清空。这次程序就结束了运行。在面试中经常就会对把栈内存和堆内存,变量提升等作为考点。当开发中遇到死循环或者是递归一直调用没办法终结的时候,就会出现栈溢出。

栈溢出
  • JS的基本类型就5种Undefined、Null、Boolean、Number和String,它们都是直接按值存储在栈中的,每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。
  • 如对象(Object)、数组(Array)、函数(Function) …,它们是通过拷贝和new出来的,这样的数据存储于堆中,但是它的引用对象是在栈中。就像上面的方法,可以看到add和addall的方法,都是存在栈内存中。需要先从栈中获得对象的地址指针。

堆的特点就是无序,以key-value的方式存在。从上面栈的例子上看,栈中只是保存了一个引用的指针类型,具体的实现方法,其实是存在堆内存中。通过引用指针的key,可以快速找到堆内存中的具体方法。可以比喻成书架,可以随便放书本,但是我们通过书本的名字可以快速找到具体的哪本书。

数组

数组在不同语言中,都有各自的封装,在JS中封装的程度也是很好,数组的去重,遍历,查找,删除等都已经有封装。而且在开发中也是经常会使用到,这边就不做太多的说明。


    var daysOfWeek = new Array()
    var daysOfWeek = new Array(7)
    var daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday',
        'Thursday', 'Friday', 'Saturday')

    // 直接赋值数组内容
    var daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
        'Thursday', 'Friday', 'Saturday'];
  • 数组的创建通常需要申请一段连续的内存空间,并且大小是固定的。
  • 当数据不能满足容量需求的时候,需要扩容。
  • 数组的开头或者中间插入数据的成本很高,需要进行大量的元素位移。

链表

  • 和数组一样,也是可以存储多个元素,但是链表的元素在内存中是不需要连续的空间。
  • 链表的每个一个元素由一个存储元素的本身节点和一个指向下一个元素的引用(指针)组成
  • 链表访问任何一个位置的元素的时候,需要从头开始访问,无法跳过任何一个元素。
  • 无法通过下标直接访问元素,需要从头一个个访问,直到找到对应的元素。
单向链表示例

图中示意的,地址指向的就是下一个节点的内存地址。双向链表的意思,就是可以在找到上一个节点。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // 封装链表的构造函数
    function LinkedList() {
        // 封装一个Node类, 用于保存每个节点信息
        function Node(element) {
            this.element = element
            this.next = null
        }

        // 链表中的属性
        this.length = 0
        this.head = null

        // 链表尾部追加元素方法
        LinkedList.prototype.append = function (element) {
            // 1.根据新元素创建节点
            var newNode = new Node(element)

            // 2.判断原来链表是否为空
            if (this.head === null) { // 链表尾空
                this.head = newNode
            } else { // 链表不为空
                // 2.1.定义变量, 保存当前找到的节点
                var current = this.head
                while (current.next) {
                    current = current.next
                }

                // 2.2.找到最后一项, 将其next赋值为node
                current.next = newNode
            }

            // 3.链表长度增加1
            this.length++
        }

        // 链表的toString方法
        LinkedList.prototype.toString = function () {
            // 1.定义两个变量
            var current = this.head
            var listString = ""

            // 2.循环获取链表中所有的元素
            while (current) {
                listString += "," + current.element
                current = current.next
            }

            // 3.返回最终结果
            return listString.slice(1)
        }

        // 根据下标删除元素
        LinkedList.prototype.insert = function (position, element) {
            // 1.检测越界问题: 越界插入失败
            if (position < 0 || position > this.length) return false

            // 2.定义变量, 保存信息
            var newNode = new Node(element)
            var current = this.head
            var previous = null
            index = 0

            // 3.判断是否列表是否在第一个位置插入
            if (position == 0) {
                newNode.next = current
                this.head = newNode
            } else {
                while (index++ < position) {
                    previous = current
                    current = current.next
                }

                newNode.next = current
                previous.next = newNode
            }

            // 4.length+1
            this.length++

            return true
        }

        // 根据位置移除节点
        LinkedList.prototype.removeAt = function (position) {
            // 1.检测越界问题: 越界移除失败, 返回null
            if (position < 0 || position >= this.length) return null

            // 2.定义变量, 保存信息
            var current = this.head
            var previous = null
            var index = 0

            // 3.判断是否是移除第一项
            if (position === 0) {
                this.head = current.next
            } else {
                while (index++ < position) {
                    previous = current
                    current = current.next
                }

                previous.next = current.next
            }

            // 4.length-1
            this.length--

            // 5.返回移除的数据
            return current.element
        }

        // 根据元素获取链表中的位置
        LinkedList.prototype.indexOf = function (element) {
            // 1.定义变量, 保存信息
            var current = this.head
            index = 0

            // 2.找到元素所在的位置
            while (current) {
                if (current.element === element) {
                    return index
                }
                index++
                current = current.next
            }

            // 3.来到这个位置, 说明没有找到, 则返回-1
            return -1
        }

        // 根据元素删除信息
        LinkedList.prototype.remove = function (element) {
            var index = this.indexOf(element)
            return this.removeAt(index)
        }

        // 判断链表是否为空
        LinkedList.prototype.isEmpty = function () {
            return this.length == 0
        }

        // 获取链表的长度
        LinkedList.prototype.size = function () {
            return this.length
        }

        // 获取第一个节点
        LinkedList.prototype.getFirst = function () {
            return this.head.element
        }
    }

    // 测试链表
    // 1.创建链表
    var list = new LinkedList();

    // 2.追加元素
    list.append(15)
    list.append(10)
    list.append(20)
    console.log(list);

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容