JavaScript数据结构--栈与队列

575 阅读4分钟

一、 栈

1. 栈的定义

栈,是限定仅在表尾进行插入和删除操作的线性表,允许插入和删除的一端称为栈顶,另一端称为栈底,有着后进先出(last in first out,简写LIFO)的特性。

日常生活中的例子,比如弹夹:最后压入的子弹最先射出

2. 栈的实现

从数据存储的角度来看,实现栈结构有2种常见方式:

  • 基于数组实现
  • 基于链表实现

本文实现栈结构基于数组

2.1 封装栈类

// 封装栈类
function Stack() {
    // 栈的属性
    this.items = [];
};

2.3 栈的常见操作与JavaScript实现:

  • push(element):添加一个新元素到栈顶
  • pop():移除栈顶的元素,同时返回被移除元素
  • peek():返回栈顶的元素,但并不对栈顶的元素做出任何的修改
  • isEmpty():检查栈内是否有元素,如果有返回true,没有返回false
  • clear():清除栈里的元素
  • size():返回栈的元素个数
// 封装栈类
function Stack() {
    // 栈的属性
    this.items = [];
    
    // 栈的方法
    // 将元素压入栈
    Stack.prototype.push = function (item) {
      this.items.push(item);
    };
    
    // 从栈中取出元素
    Stack.prototype.pop = function () {
      return this.items.pop();
    };
    
    // 查看栈顶元素
    Stack.prototype.peek = function () {
      return this.items[this.items.length - 1];
    };
    
    // 判断栈是否为空
    Stack.prototype.isEmpty = function () {
      return this.items.length === 0;
    };
    
    // 获取栈中元素的个数
    Stack.prototype.size = function () {
      return this.items.length;
    };
};

2.4 栈的使用

// 实例化一个对象
var stack = new Stack();
console.log(stack.isEmpty()); // true
stack.push(3);
stack.push(6);
stack.push(5);
stack.push(11);
console.log(stack.peek()); // 输出栈顶元素11
console.log(stack.size()); // 输出元素个数4
stack.pop(); // 删除栈顶元素11
console.log(stack.peek()); // 输出栈顶元素5

2.5 栈的应用--十进制转换二进制

十进制100转换二进制逻辑(除2取余,逆序排列),符合后进先出的栈特征

计算:100 / 2 = 50  余数:0
计算:50 / 2  = 25  余数:0
计算:25 / 2  = 12  余数:1
计算:12 / 2  = 6   余数:0
计算:6 / 2   = 3   余数:0
计算:3 / 2   = 1   余数:1
计算:1 / 2   = 0   余数:1

代码实现

//  十进制转换为二进制
function decToBin (number) {
    var st = new Stack();
    while (number > 0) {
        st.push(number % 2);
        number = Math.floor(number / 2);
    }
    var result = "";
    while (st.size() > 0) {
        result += st.pop();
    }
    return result;
}

2.6 栈的应用--git stash

git stash命令暂存对当前分支的所有操作,将未完成的修改保存到一个栈上,在任意时刻重新找回修改。运行该命令后,所有没有commit的代码,都会暂时从工作区移除,回到上次commit时的状态。

# 暂时保存没有提交的工作
$ git stash
Saved working directory and index state WIP on workbranch: 56cd5d4 Revert "update old files"
HEAD is now at 56cd5d4 Revert "update old files"
# 列出所有暂时保存的工作
$ git stash list
stash@{0}: WIP on workbranch: 56cd5d4 Revert "update old files"
stash@{1}: WIP on project1: 1dd87ea commit "fix typos and grammar"
# 恢复某个暂时保存的工作
$ git stash apply stash@{1}
# 恢复最近一次stash的文件
$ git stash pop
# 丢弃最近一次stash的文件
$ git stash drop
# 删除所有的stash
$ git stash clear

二、 队列

1. 队列的定义

队列和栈十分相似,他们都是线性表,元素都是有序的 。队列和栈不同的是,队列遵循先进先出(First in first out,简称FIFO)的原则。队列从尾部添加新元素,从顶部移除元素,最新添加的元素必须排列在队列的末尾。

日常生活中,最常见的队列就是排队,先进入队列的先服务,后进入的排在队尾

2. 队列的实现

从数据存储的角度来看,实现栈结构有2种常见方式:

  • 基于数组实现
  • 基于链表实现

本文实现队列结构基于数组(性能低于链表实现)

2.1 封装队列类

// 封装队列类
function Queue() {
    // 队列的属性
    this.items = [];
};

2.3 栈的常见操作与JavaScript实现:

  • enqueue(element):向队列尾部添加一个(或是多个)元素
  • dequeue():移除队列的第一个元素,并返回被移除的元素
  • front():返回队列的第一个元素——最先被添加的也是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息,与Stack类的peek()方法类似)
  • isEmpty():检查队列内是否有元素,如果有返回true,没有返回false
  • size():返回队列的元素个数
// 封装队列类
function Queue() {
    // 队列的属性
    this.items = [];
    
    // 队列的方法
    // 将元素加入队列中
    Queue.prototype.enqueue = function (item) {
      this.items.push(item);
    };
    
    // 从栈中删除前端元素
    Queue.prototype.dequeue = function () {
      return this.items.shift();
    };
    
    // 查看前端元素
    Queue.prototype.front = function () {
      return this.items[0];
    };
    
    // 判断队列是否为空
    Queue.prototype.isEmpty = function () {
      return this.items.length === 0;
    };
    
    // 获取队列中元素的个数
    Queue.prototype.size = function () {
      return this.items.length;
    };
};

2.4 队列的使用

// 实例化一个对象
var queue = new Queue();
console.log(queue.isEmpty()); // true
queue.enqueue(3);
queue.enqueue(6);
queue.enqueue(5);
queue.enqueue(11);
console.log(queue.front()); // 输出队列前端元素3
console.log(queue.size()); // 输出队列元素个数4
queue.dequeue(); // 删除队列元素3
console.log(queue.front()); // 输出队列前端元素6

2.5 队列的应用--斐波那契数列

要求: 使用队列计算斐波那契数列的第n项

分析: 斐波那契数列的前两项固定为1,后面的项为前两项之和,依次向后,这便是斐波那契数列。

function fibonacci(n) {
    const queue = new Queue();
    // 先将固定的前两项加入队列
    queue.enqueue(1);
    queue.enqueue(1);
    
    let index = 0;
    while(index < n - 2) {
        index += 1;
        // 取出队列一个元素
        const delItem = queue.dequeue();
        // 取出队列头部元素
        const frontItem = queue.front();
        // 计算下一个元素
        const nextItem = delItem + frontItem;
        // 将下一个元素的计算结果加入队列中
        queue.enqueue(nextItem);
        console.log(index, queue.front())
    }
    console.log(queue.size(), '队列元素个数');
    // 此时队列中有两个元素,先删除再取出最后一个元素
    console.log(index, queue.front());
    queue.dequeue();
    return queue.front();
}

console.log(fibonacci(9)); // 34