|
| 1 | +--- |
| 2 | +title: JavaScript 数据结构与算法(三)栈结构 |
| 3 | +date: 2020年07月21日 11:49:12 |
| 4 | +tags: [JavaScript, 数据结构, 算法, 栈] |
| 5 | +categories: [算法专辑] |
| 6 | +--- |
| 7 | + |
| 8 | +数组是一个线性结构,并且可以在数组的任意位置插入和删除元素。 |
| 9 | +但是有时候,我们为了实现某些功能,必须对这种任意性加以限制。 |
| 10 | +栈和队列就是比较常见的受限的线性结构。 |
| 11 | + |
| 12 | +栈(stack)是一种运算受限的线性表: |
| 13 | + |
| 14 | +- `LIFO(last in first out)`表示就是后进入的元素,第一个弹出栈空间。类似于自动餐托盘,最后放上的托盘,往往先把拿出去使用。 |
| 15 | +- 其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。 |
| 16 | +- 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素; |
| 17 | +- 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 |
| 18 | + |
| 19 | +如下图所示: |
| 20 | + |
| 21 | + |
| 22 | +栈的特点:**先进后出,后进先出**。 |
| 23 | + |
| 24 | +### 程序中的栈结构 |
| 25 | + |
| 26 | +- 函数调用栈:A(B(C(D()))): |
| 27 | + 即 A 函数中调用 B,B 调用 C,C 调用 D;在 A 执行的过程中会将 A 压入栈,随后 B 执行时 B 也被压入栈,函数 C 和 D 执行时也会被压入栈。所以当前栈的顺序为:A->B->C->D(栈顶);函数 D 执行完之后,会弹出栈被释放,弹出栈的顺序为 D->C->B->A; |
| 28 | + |
| 29 | +- 递归: |
| 30 | + 为什么没有停止条件的递归会造成栈溢出?比如函数 A 为递归函数,不断地调用自己(因为函数还没有执行完,不会把函数弹出栈),不停地把相同的函数 A 压入栈,最后造成栈溢出(Stack Overfloat)。 |
| 31 | + |
| 32 | +### 练习 |
| 33 | + |
| 34 | +题目:有 6 个元素 6,5,4,3,2,1 按顺序进栈,问下列哪一个不是合法的出栈顺序? |
| 35 | + |
| 36 | +- A:5 4 3 6 1 2 (√) |
| 37 | +- B:4 5 3 2 1 6 (√) |
| 38 | +- C:3 4 6 5 2 1 (×ばつ) |
| 39 | +- D:2 3 4 1 5 6 (√) |
| 40 | + |
| 41 | +题目所说的按顺序进栈指的不是一次性全部进栈,而是有进有出,进栈顺序为 6 -> 5 -> 4 -> 3 -> 2 -> 1。 |
| 42 | + |
| 43 | +解析: |
| 44 | + |
| 45 | +- A 答案:65 进栈,5 出栈,4 进栈出栈,3 进栈出栈,6 出栈,21 进栈,1 出栈,2 出栈(整体入栈顺序符合 654321)。 |
| 46 | +- B 答案:654 进栈,4 出栈,5 出栈,3 进栈出栈,2 进栈出栈,1 进栈出栈,6 出栈(整体的入栈顺序符合 654321)。 |
| 47 | +- C 答案:6543 进栈,3 出栈,4 出栈,之后应该 5 出栈而不是 6,所以错误。 |
| 48 | +- D 答案:65432 进栈,2 出栈,3 出栈,4 出栈,1 进栈出栈,5 出栈,6 出栈。符合入栈顺序。 |
| 49 | + |
| 50 | +### 栈结构实现 |
| 51 | + |
| 52 | +#### 栈常见的操作 |
| 53 | + |
| 54 | +- `push()`:添加一个新元素到栈顶位置。 |
| 55 | +- `pop()`:移除栈顶的元素,同时返回被移除的元素。 |
| 56 | +- `peek()`:返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)。 |
| 57 | +- `isEmpty()`:如果栈里没有任何元素就返回 `true`,否则返回 `false`。 |
| 58 | +- `size()`:返回栈里的元素个数。这个方法和数组的 `length` 属性类似。 |
| 59 | +- `toString()`:将栈结构的内容以字符串的形式返回。 |
| 60 | + |
| 61 | +#### JavaScript 代码实现栈结构 |
| 62 | + |
| 63 | +```js |
| 64 | +// 使用 ES6 实现 |
| 65 | +class Stack { |
| 66 | + items = []; |
| 67 | + |
| 68 | + // push() 压栈操作,给栈中添加元素 |
| 69 | + push(item) { |
| 70 | + this.items.push(item); |
| 71 | + } |
| 72 | + |
| 73 | + // pop() 出栈操作,从栈中取出元素,并返回取出的那个元素 |
| 74 | + pop() { |
| 75 | + return this.items.pop(); |
| 76 | + } |
| 77 | + |
| 78 | + // peek() 查看栈顶元素 |
| 79 | + peek() { |
| 80 | + return this.items[this.items.length - 1]; |
| 81 | + } |
| 82 | + |
| 83 | + // isEmpty() 判断栈是否为空 |
| 84 | + isEmpty() { |
| 85 | + return this.items.length === 0; |
| 86 | + } |
| 87 | + |
| 88 | + // size() 获取栈中元素个数 |
| 89 | + size() { |
| 90 | + return this.items.length; |
| 91 | + } |
| 92 | + |
| 93 | + // toString() 返回以字符串形式的栈内元素数据 |
| 94 | + toString() { |
| 95 | + let result = ""; |
| 96 | + for (let item of this.items) { |
| 97 | + result += item + " "; |
| 98 | + } |
| 99 | + return result; |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +#### 测试栈结构 |
| 105 | + |
| 106 | +```js |
| 107 | +const stack = new Stack(); |
| 108 | +stack.push(1); |
| 109 | +stack.push(22); |
| 110 | +stack.push(333); |
| 111 | +stack.push(4444); |
| 112 | +console.log(stack.items); //--> [1, 22, 333, 4444] |
| 113 | + |
| 114 | +console.log(stack.pop()); //--> 444 |
| 115 | +console.log(stack.pop()); //--> 333 |
| 116 | +console.log(stack.peek()); //--> 22 |
| 117 | +console.log(stack.isEmpty()); //--> false |
| 118 | +console.log(stack.size()); //--> 2 |
| 119 | +console.log(stack.toString()); //--> 1 22 |
| 120 | +``` |
| 121 | + |
| 122 | +### 栈结构的简单应用 |
| 123 | + |
| 124 | +利用栈结构的特点封装十进制转换为二进制的函数。 |
| 125 | + |
| 126 | +### 代码实现 |
| 127 | + |
| 128 | +```js |
| 129 | +// 十进制转换成二进制 |
| 130 | +function dec2bin(dec) { |
| 131 | + // new 一个 Stack,保存余数 |
| 132 | + const stack = new Stack(); |
| 133 | + |
| 134 | + // 当不确定循环次数时,使用 while 循环 |
| 135 | + while (dec > 0) { |
| 136 | + // 除二取余法 |
| 137 | + stack.push(dec % 2); // 获取余数,放入栈中 |
| 138 | + dec = Math.floor(dec / 2); |
| 139 | + } |
| 140 | + |
| 141 | + let binaryString = ""; |
| 142 | + // 不断地从栈中取出元素(0 或 1),并拼接到一起。 |
| 143 | + while (!stack.isEmpty()) { |
| 144 | + binaryString += stack.pop(); |
| 145 | + } |
| 146 | + |
| 147 | + return binaryString; |
| 148 | +} |
| 149 | +``` |
| 150 | + |
| 151 | +#### 测试 |
| 152 | + |
| 153 | +```js |
| 154 | +// 验证十进制转换二进制方法 |
| 155 | +console.log(dec2bin(10)); //--> 1010 |
| 156 | +console.log(dec2bin(100)); //--> 1100100 |
| 157 | +``` |
0 commit comments