学习笔记——栈与单双链表一些方法的实现

312 阅读4分钟

JavaScript的数据结构与算法

栈常见的一些操作

push(element):添加一个新元素到栈顶位置
pop():移除栈顶的元素,同时返回被移除的元素
peek():返回栈顶的元素,不对栈做任何的修改(这个方法不会移除栈顶的元素,仅仅返回他)
isEmpty():如果栈里面没有任何元素,就返回true,否则返回false。
size():返回栈里面元素的个数,这个方法和数组的length属性很类似
toString():将栈结构的内容以字符形式返回

栈的两种实现方式

  • 基于数组
  • 基于链表

对象与方法的区分

Methods:和某一个对象实力有关系。

function:普通的函数

封装一个栈结构

 //封装一个栈结构
      //实现栈中各个函数的方法
      function stack() {
        this.items = [];
        //1.将元素压入到栈
        Stack.prototype.push = function (element) {
          this.items.push(element);
        };
        //2.从栈中取出元素
        Stack.prototype.pop = function () {
          return this.items.pop();
        };
        //3.查看一下栈顶元素
        Stack.prototype.peek = function () {
          return this.items[items.length - 1];
        };
        //4.判断栈是否为空
        Stack.prototype.isEmpty = function () {
          return this.items.length === 0;
        };
        //5.获取栈中元素的个数
        Stack.prototype.size = function () {
          return this.items.length;
        };
        //6.toString方法
        Stack.prototype.toString = function () {
          var resultString = "";
          for (var i = 0; i < this.items.length; i++) {
            resultString += this.items[i] + "";
          }
          return resultString;
        };
      }
      //栈的使用
      var s = new stack();

栈的使用

    // // 栈的使用
      // var s = new Stack();
​
      //函数:十进制转二进制
      function dec2bin(decNumber) {
        //1.定义对象
        var stack = new Stack();
        //2.循环操作
        while (decNumber > 0) {
          //获取余数,并且放入到栈中
          stack.push(decNumber % 2);
          //获取整除后的结果,并且放入到栈中,作为下一次运行的数字
          decNumber = Math.floor(decNumber / 2);
        }
        //从栈中取出0和1
        var binaryString = "";
        while (!stack.isEmpty()) {
          binaryString += stack.pop();
        }
        return binaryString;
      }
​
      //测试十进制转二进制的函数
      alert(dec2bin(100))

队列结构

受限的线性结构

先进先出

  • 线结构
  • 队列结构

队列:他是一种受限的线性表,先进先出进行删除操作

而在表的后端进行删除操作

打印队列:

  • 有五份文档需要打印,这些文档会按照顺序放入到打印队列中
  • 打印机会依次从队列中取出文档,优先放入的文档,优先被取出,并且对该文档进行打印
  • 以此类推,知道队列中不再有新的文档

线程队列:

  • 在开发中,为了让人物可以并行处理,通常会开启多个线程
  • 但是我们不能让大量的线程同时运行处理任务(占用过多的资源)
  • 这个时候,如果需要开启线程处理任务的情况,我们就会使用线程队列
  • 线程队列会依照次序来启动线程,并处理对应的任务

队列的常见操作

equeue(element)向队列尾部添加一个或多个新的项
dequeue()移除队列的第一(即排在最前面的)项,并返回被移除的元素
front()返回队列中第一个元素---最先被添加,也将是最先被移除的元素。队列不做任何的变动,(不移除元素,只返回元素信息--与Stack 的peek方法类似)
isEmpty()如果队列中不包含任何元素,返回true,否则返回false
size()返回队列包含的元素个数,与数组length属性类似
toString()将队列中的内容,转换成字符串形式

方法的实现步骤

  //封装队列
      function Queue() {
        this.items = [];
        //1.将元素加入到队列中
        Queue.prototype.enqueue = function (element) {
          this.items.push(element);
        };
        //2.从队列中删除前端元素
        Queue.prototype.dequeue = function () {
          return this.items.shift();
        };
        //3.查看前端元素
        Queue.prototype.front = function () {
          return this.items[0];
        };
​
        //4.查看队列是否为空
        Queue.prototype.isEmpty = function () {
          return this.items.length == 0;
        };
        //5.查看队列中元素的个数
        Queue.prototype.size = function () {
          return this.items.length;
        };
        //6.toString方法
        Queue.prototype.toString = function () {
          var resultString = '';
          for (var i = 0; i < this.items.length; i++) {
            resultString += this.items[i] + '';
          }
          return resultString;
        };
      }
      //使用队列
      var queue = new Queue();
      queue.enqueue('abc');
      queue.enqueue('def');
      queue.enqueue('ghi');
      queue.enqueue('jkl');
      alert(queue);
      queue.dequeue()
      alert(queue)

击鼓传花游戏

  //面试题:击鼓传花的实现
      function passGame(nameList, num) {
        //1.首先创建一个栈队列
        var queue = new Queue();
        //2.将所有人一次加入到队列中
        for (var i = 0; i < nameList.length; i++) {
          queue.enqueue(nameList[i]);
        }
        //3.开始查数字
        while (queue.size() > 1) {
          //不是num的时候加入到队列的末尾
          //是num这个数字的时候,将其从队列中山删除
          //3.1num数字之前的人重新放在队列的末尾
          for (var i = 0; i < num - 1; i++) {
            queue.enqueue(queue.dequeue());
          }
          //3.2num对应这个人,直接从队列中删除
          queue.dequeue();
          //4.获取剩下的那个人
          alert(queue.size());
          var endName = queue.front();
          alert("最终剩下的那个人:" + endName);
          return nameList.indexOf(endName);
        }
      }
​
      names = ["Lili", "Lucy", "tom", "Lilei", "why"];
      alert(passGame(names, 3));

封装优先级队列

//封装优先级队列
    function priorityQueue(){
      //在priorityQueue重新小黄见了一个类,可以理解成内部类
      function priorityElement(element,priority){
        this.element=element
        this.priority=priority
      }
      //封装属性
      this.items=[]
  
    //实现插入方法
    priorityQueue.prototype.enqueue=function(element,priority){
      //1.创建priorityElement对象
      var queueElement=new priorityElement(element,priority)
      //2.判断队列是否为空
      if(this.items.length==0){
        this.items.push(queueElement)
      }else{
        var added=falsefor(var i=0;i<this.items.length;i++){
          if(queueElement.priority<this.iitems[i]){
            this.items.splice(i,0,queueElement)
            added=true
            break
          }
        }
        if(!added){
          this.items.push(queueElement)
        }
      }
    }
      }
      //测试代码
      var pq=new PriorityQueue()

链表作用

链表和数组一样,可用于存储一系列的元素,但链表和数组的实现机制完全不同

数组:我们要存储多个多个元素,可能最常用的就是数组

数组 缺点:

  • 数组的创建需要申请一段连续的内存空间,并且大小都是固定的,,所以当数组不能满足需求时,需要扩容
  • 并且在数组的中间插入的成本很高,需要进行大量元素的位移

链表的优点:

  • 内存空间不必是连续的,可以陈宏芬利用计算机的内存,实现灵活的内存动态管理
  • 链表不必在创建时就确定大小,并且可以无限延伸下去
  • 链表插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高很多

image.png

链表的常见操作

append(element)向链表尾部添加一个新的项
insert(position,element)向链表的特定位置插入一个新的项
get(position)获取对应位置元素
indexOf(element)返回元素在列表中的索引。如果列表中没有该元素,则返回-1
update(position)修改某个位置的元素
removeAt(position)从列表的特定位置移除一项
remove(element)从列表中移除一项
isEmpty()如果链表中不包含任何的元素,返回true,如果链表的长度大于0则返回false
size()返回链表中包含元素个数,与数组的length属性相类似
toString()由于列表使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其输出元素的值

整体操作方法与数组非常类似,因为链表本身就是一种可以代替数组的结构

append方法的实现

<script>
      //封装链表类
      function LinkList() {
        //内部的类:节点类
        function Node(data) {
          this.data = data;
          this.length = 0;
          //1.追加方法
          LinkList.prototype.append = function (data) {
            //1.创建新节点
            var newNode = new Node(data);
            //判断是否添加的是的第一个节点
            if (this.length == 0) {
              this.head = newNode;
            } else {
              var current = this.head;
              while (current.next) {
                current = current.next;
              }
              current.next = newNode;
            }
            //3.length+1
            this.length += 1;
          };
        }
      }
    </script>

toString方法

//2.toString方法
          LinkList.prototype.toString = function () {
            //1.定义变量
            var current = this.head;
            var ListString = "";
            //2.循环获取一个节点
            while (current) {
              listString += current.data + "";
              current = current.next;
            }
            return listString;
          };
        }

insert方法

    LinkList.prototype.insert = function (position, data) {
            //1.对position进行越界判断
            if (position < 0 || position > this.length) return false;
            //2.根据data创建newNode
            var newNode = new Node(data);
            //3.判断插入的位置是否是第一个
            if (position == 0) {
              newNode.next = this.head;
              this.head = newNode;
            } else {
              var index = 0;
              var current = this.headvar;
              previous = null;
              while (index++ < position) {
                previous = current;
                current = current.next;
              }
              newNode.next = current;
              previous.next = newNode;
            }
          };

get方法

 //4.get方法
          LinkedList.prototype.get=function(position){
            //1.越界处理
            if(position<0||position>=this.length)return null;
            //2.获取对应的data
            var current = this.head
            var index=0
            while(index++<position){
              current=current.next
            }
            return current.data
​
          }
        }

update方法

//6.update方法
          LinkedList.prototype.update = function (position, newData) {
            //1.越界处理
            if (position < 0 || position >= this.length) return false;
            //2.查找正确的节点
            var current = this.head;
            var index = 0;
            while (index++ < position) {
              current = current.next;
            }
            //3.将position位置的node的data修改成newData
            current.data = newData;
            return true;
          };

removeAt方法

  //7.removeAt方法
          LinkedList.prototype.removeAt = function (position) {
            //1.越界处理
            if (position < 0 || position >= this.length) return null;
            //2.判断是否删除第一个节点
            var current = this.head;
            if (position == 0) {
              this.head = this.head.next;
            } else {
              var index = 0;
              var previous = null;
              while (index++ < position) {
                previous = current;
                current = current.next;
              }
              //前一个节点的next指向,current的next即可
              previous.next = current.next;
            }
            this.length -= 1;
            return current.data;
          };

remove的方法

//8.remove的方法
          LinkList.prototype.remove = function (data) {
            //1.获取data在列表中的位置
            var position = this.indeOf(data);
            //根据位置信息,删除节点
            return this.removeAt(position);
          };

isEmpty方法

//9.isEmpty方法
          LinkList.prototype.isEmpty = function () {
            return this.length==0;
          };

size()方法

 LinkList.prototype.size = function (data) {
           return this.length
          };

单向链表

缺点:

  • 只能从头到尾遍历
  • 链表的相连过程是单向的
  • 实现原理也是上一个链表中有指向下一个的引用

比较明显的缺点:

我们可以轻松到达下一个节点,但是回到前一个节点是很难的

双向链表

  • 既可以从头遍历到尾,又可以从尾遍历到头
  • 链表相连的过程是双向的
  • 一个节点既有向前连接的引用,也有向后连接的引用

双向链表的特点:

  • 可以使用一个head和一个tail分别指向头部和尾部的节点
  • 每个节点都由三部分组成:前一个节点的指针(prev)/保存的元素/后一个节点的指针(next)
  • 双向链表的第一个节点的prev是null
  • 双向链表的最后的节点的next是null

双向链表的常见操作

append(element)向链表尾部添加一个新的项
insert(position,element)链表的特定位置插入一个新的项
get(position)获取对应位置元素
indexOf(element)返回元素在列表中的索引。如果列表中没有该元素,则返回-1
update(position,element)修改某个位置的元素
removeAt(position)从列表的特定位置移除一项
remove(element)从列表中移除一项
isEmpty()如果链表中不包含任何的元素,返回true,如果链表的长度大于0则返回false
size()返回链表中包含元素个数,与数组的length属性相类似
toString()由于列表使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其输出元素的值
forwardString()返回正向遍历的节点字符串形式
backwordString()返回反向遍历的节点字符串形式

1.追加方法

//1.追加方法
        LinkedList.prototype.append = function (data) {
          //1.创建新的节点
          var newNode = new Node(data);
          //2.判断添加的是第一个节点
          if (this.length == 0) {
            this.head = newNode;
          } else {
            //找到最后一个节点
            var current = this.head;
            while (current.next) {
              current = current.next;
            }
            //最后的next指向新的节点
            current.next = newNode;
          }
        };

toString,forwardString,backwardString方法

//2.toString方法
        //2.1
        DoublyLinkList.prototype.toString = function () {
          return this.backwardString();
        };
​
        //2.2forwardString方法
        DoublyLinkList.prototype.forwardString = function () {
          //1.定义变量
          var current = this.tail;
          var resultString = "";
          //2.依次向前遍历,获取每一个节点
          while (current) {
            resultString += current.data + "";
            current = current.next;
          }
          return resultString;
        };
        //2.3backwardString方法,向后
        DoublyLinkList.prototype.backwardString = function () {
           //1.定义变量
           var current = this.head;
          var resultString = "";
          //2.依次向后遍历,获取每一个节点
          while (current) {
            resultString += current.data + "";
            current = current.next;
          }
          return resultString;
        }
        };

indexOf方法

//indexOf方法
        DoublyLinkList.prototype.indexOf = function (data) {
          //1.定义变量
          var current = this.head;
          var index=0
          //2.查找和data相同的节点
          while(current){
            if(current.data==data){
              return index;
            }
            curent=current.next
            index+=1
          }
          return -1
        };

update方法

//6.update方法
        DoublyLinkList.prototype.update = function (position, newData) {
          //1.越界处理
          if (position < 0 || position >= this.length) return false;
          //2.寻找正确的节点
          var current = this.head;
          var index = 0;
          while (index++ < position) {
            current = current.next;
          }
          //3.修改找到节点的data信息
          current.data = newData;
          return true;
        };

removeAt方法

        //removeAt方法
        DoublyLinkList.prototype.removeAt = function (data) {
          if (position < 0 || position >= this.length) return null;
          //2.判断是否只有一个节点
          var current = this.head;
          if (this.length == 1) {
            this.head = null;
            this.tail = null;
          } else {
            if (position == 0) {
              this.head.next.prev = null;
              this.head = this.head.next;
            } else if (position == this.length - 1) {
              current = this.tail;
              this.tail.prev.next = null;
            } else {
              var index = 0;
              while (index++ < position) {
                currrent = current.next;
                current.next.prev = current.prev;
              }
            }
          }
        };

remove方法

 //remove方法
        DoublyLinkList.prototype.remove = function (data) {
          //1.获取data下标值
          var index = this.indexOf(data);
          //2.根据Index删除对应位置的节点
          return this.removeAt(index);
        };

isEmpty方法

//isEmpty方法
         DoublyLinkList.prototype.isEmpty = function (data) {
          return this.length==0
        };

size方法

DoublyLinkList.prototype.size = function (data) {
          return this.length==0
        };

获取链表的第一个元素

//获取链表的第一个元素
        DoublyLinkList.prototype.getHead = function (data) {
          return this.head.data
        };

获取链表的最后一个元素

DoublyLinkList.prototype.getTail = function (data) {
          return this.tail.data
        };

——本文学习于coderwhy老师的数据结构与算法