zoukankan      html  css  js  c++  java
  • js堆,栈与队列

    栈的定义

    栈是计算机科学中的一种抽象数据类型,只允许在有序的线性数据集合的一端(称为堆栈顶端,英语:top)进行加入数据(英语:push)和移除数据(英语:pop)的运算。因而按照后进先出(LIFO, Last In First Out)的原理运作。(百科全书)

    栈的常用操作

    栈中有两个基本的操作

    • 推入 :从栈的顶端推入一个数据,依次往下推
    • 弹出 :讲栈顶端的数据移除

    栈的基本提点就是

    • 先进后出,后进先出
    • 除了头尾的节点,每个元素都有一个先驱和一个后继

    对于栈的画面的理解,可以想象成一个步枪弹夹添加子弹和射击的过程
    弹夹只有一个出入口进行推入和弹出

    js模拟实现一个栈

    <body style=' 80%;height:80%; margin: 10% auto;'>
    <ul id="stackBox" style="100px;border:1px solid #1fb19e;">
    </ul>
    <div style="400px; display:flex;">
        <input id="funStackBox" value="执行函数">
        <div style="margin-left:100px;">
            <button onclick="pushStack()" type='primary'>进栈</button>
            <button onclick="popStack()" type='primary'>出栈</button>
        </div>
    </div>
    <script>
        // 栈盒子
        let stackBox = document.getElementById('stackBox')
        // 执行函数盒子
        let funStackBox = document.getElementById('funStackBox')
    
        // 栈类
        class Stack {
            constructor(length) {
                this.list = []
                this.length = length
            }
            // 新增栈
            addStack(val) {
                if (this.length > this.list.length) {
                    this.list.push(val)
                    let beforeDom = document.getElementById(this.list.length - 1)
                    // 新增栈
                    let el = document.createElement('li')
                    el.id = this.list.length
                    el.innerText = this.list[this.list.length - 1]
                    stackBox.insertBefore(el, beforeDom)
                }else{
                    console.log('栈满了')
                }
            }
            // 弹出栈
            popStack() {
                let delDom = document.getElementById(this.list.length)
                stackBox.removeChild(delDom)
                return this.list.pop()
            }
            // 栈的大小
            stackLength() {
                console.log('当前栈大小为'+this.list.length)
                return this.list.length
            }
            // 栈的顶层元素
            stackIsHas() {
                return this.list.length?this.list[this.list.length]:console.log('没有栈了')
            }
            //查看所有元素
            showStack() {
                return this.list.join('
    ');
            }
            //清空队列
            clearStack() {
                this.list = [];
                this.showStack()
                console.log('栈空了')
            }
        }
        stackOne = new Stack(5)
        /**
         * @author: 周靖松
         * @information: 进栈
         * @Date: 2019-06-10 12:47:16
         */
        function pushStack() {
            stackOne.addStack(funStackBox.value)
            console.log(funStackBox.value, '进栈')
            stackOne.stackLength()
            stackOne.stackIsHas()
        }
        /**
         * @author: 周靖松
         * @information: 出栈
         * @Date: 2019-06-10 12:47:16
         */
        function popStack() {
            let popStack = stackOne.popStack(funStackBox.value)
            console.log(popStack, '出栈')
            stackOne.stackLength()
            stackOne.stackIsHas()
        }
    
    
    </script>
    
    </body>
    

    效果图如下

    栈被创建的时机

    上边说了栈的基本结构和方法,那么栈被创建的时候又做了什么事情呢

    首先在我们的js在解释执行代码的时候,最先遇到的就是全局代码,所以在一开始的时候首先就会向栈里边压入一个全局执行上下文

    全局上下文 只有在全局执行环境被销毁的时候才会被弹出销毁
    全局执行上下文 只有一个 就是浏览器中的window对象,this默认指向这个全局对象

    然后当执行一个函数的时候,会在开始先创建一个执行上下文压入栈中,如果里边又执行其他的函数的时候,又会创建一个新的执行上下文压入执行栈中,直到函数执行完毕,就会把函数的上下文从执行栈中弹出

    function fun3() {
    console.log('3')
    }
    
    function fun2() {
        fun3();
    }
    
    function fun1() {
        fun2();
    }
    
    fun1();
    

    比如说上边这段代码 ,他的进栈出栈顺序就是一开始我们放的那两张图的效果

    function fun1() {
         console.log('1')
    }
    function fun2() {
         console.log('2')
    }
    function fun3() {
         console.log('3')
    }
    

    如果是这种的话 则会是最下边有一个全局的栈,然后三个函数分别进栈出栈

    栈中的执行上下文

    刚才我们在上边提到了执行上下文的概念,执行上下文是跟函数相关的,执行上下文分为两个阶段

    • 创建阶段
    • 执行阶段

    首先创建阶段

    • 扫描变量和函数(确定变量环境)
    • 确定this指向
    • 确定词法环境

    简单说一下词法环境和变量环境的区别,我个人理解的就是说词法环境是包含变量环境的

    在js里边原型链大家都不陌生 ,js在当前的对象里边找不到所使用的属性的话会去他的上一级去找
    直到Object,再找不到就会undefined ,这里边 当前对象的作用域就是他的变量环境,而词法环境则是与之关联的的执行上下文中声明的变量

    在创建阶段 函数的声明会被储存在当前的变量环境之中,var的变量的话则会被设置成undefined
    ,所以我们在声明之前就可以访问到var声明的变量 ,but他是一个undfined

    然后就是执行阶段了

    这个时候已经完成了对所有变量的分配,开始执行代码

    队列的定义

    队列是一种比较高效的数据结构,他与栈不同的是,队列只能在队尾插入元素,在队首删除元素,
    队列用生活中的事物距离的话,大家可以想想一下沙漏,先进入的沙子先出去,后进去的沙子后出去

    队列比栈高效的地方就在于,循环的时候,栈会开辟一个新的临时栈,然后进行排序,再循环,最后在确保不打乱原有顺序的情况下 排列回去

    队列则不需要这么多步骤

    js模拟队列实现

    队列常用的一些操作有

    • 向队尾添加一个元素
    • 删除队首元素
    • 显示所有元素
    • 清空队列
    • 队列是否为空

    就先拿这些常用的方法实现以下,老规矩,请大家看代码

         // 队列类
        class Queue {
            constructor() {
                this.dataStore = []
            }
    
            //新增
            addQueue(val) {
                this.dataStore.push(val);
            }
    
    
            //删除队列首的元素
            delQueue() {
                return this.queueIsNone()?console.log('队列空了'):this.dataStore.shift()
            }
    
            //队列是否为空
            queueIsNone() {
                return this.dataStore.length == 0
            }
    
            //查看所有元素
            showQueue() {
                return this.dataStore.join('
    ');
            }
    
            //清空队列
            clearQueue() {
                this.dataStor = [];
                this.showQueue()
                console.log('队列空了')
            }
        }
    
        // 队列实例
        let queueOne = new Queue()
        /**
         * @author: 周靖松
         * @information: 进队
         * @Date: 2019-06-11 21:01:28
         */
        function QueueIn (){
            queueOne.addQueue(funStackBox.value)
            console.log(queueOne.showQueue())
        }
        /**
         * @author: 周靖松
         * @information: 出队
         * @Date: 2019-06-11 21:01:35
         */
        function QueueOut (){
            queueOne.delQueue()
            console.log(queueOne.showQueue())
        }
        /**
        * @author: 周靖松
        * @information: 清空队列
        * @Date: 2019-06-11 21:05:35
        */    
        function QueueClear(){
            queueOne.clearQueue()
        }
    

    上边的代码 大家可以自己写个html试一下哈,html就不写了直接贴一下效果图

    另外虽然刚才我们再例子中是用数据去模拟的栈和队列的实现,but 数组他其实并不是一个栈内存,他是一个堆内存

    堆的定义

    若是满足以下特性,即可称为堆:是计算机科学中的一种特别的树状数据结构。可以给堆中的任意节点添加新的节点(百科全书)

    堆和栈的区别

    在JS中,每一个数据都需要一个内存空间。内存空间又被分为两种,栈内存(stack)与堆内存(heap)。

    • 栈为自动分配的内存空间,它由系统自动释放
    • 堆是动态分配的内存,大小不定也不会自动释放

    也不是说不会自动释放,堆在没有引用的时候,下一次垃圾回收机制出现的时候会回收他的内存

    js 的变量分为基本类型和引用类型

    • 基本类型 (Undefined、Null、Boolean、Number和String)
      基本类型在内存中占据空间小、大小固定 ,他们的值保存在栈(stack)空间,是按值来访问

    • 引用类型 (对象、数组、函数)
      引用类型占据空间大、大小不固定, 栈内存中存放地址指向堆(heap)内存中的对象。是按引用访问的

    盗个图举个例子

    js不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间,所以栈中存的是一个指向堆内存的一个地址

    当我们要访问堆内存中的引用数据类型时,实际上我们首先是从栈中获取了该对象的地址引用(或者地址指针),然后再从堆内存中取得我们需要的数据。

    今天就堆栈队列的内容就大概说到这里 下一篇博客 在继续说一下, 有什么说的不对或者不足的地方,请大家批评指正

  • 相关阅读:
    又见博弈
    两道来自CF的题
    温习及回顾
    笔试面试总结
    Python Cha4
    初学ObjectiveC
    设计模式汇总(三)
    转贴XML的写法建议
    让从Objec中继承的类也拥有鼠标事件
    关于异常处理的一些看法
  • 原文地址:https://www.cnblogs.com/netUserAdd/p/10979069.html
Copyright © 2011-2022 走看看