数组
我们知道数组是一种线性结构, 并且可以在数组的任意位置插入和删除数据.
但是有时候, 我们为了实现某些功能, 必须对这种任意性加以限制.
而栈和队列就是比较常见的受限的线性结构, 我们先来学习栈结构.
栈(stack),它是一种运算受限的线性表,后进先出(LIFO)
LIFO(last in first out)表示就是后进入的元素, 第一个弹出栈空间. 类似于自动餐托盘, 最后放上的托盘, 往往先把拿出去使用.
其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。
向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;
从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈结构的图解
程序中什么是使用栈实现的呢?
- 学了这么久的编程, 是否听说过, 函数调用栈呢?
- 我们知道函数之间和相互调用: A调用B, B中又调用C, C中又调用D.
- 那样在执行的过程中, 会先将A压入栈, A没有执行完, 所有不会弹出栈.
- 在A执行的过程中调用了B, 会将B压入到栈, 这个时候B在栈顶, A在栈底.
- 如果这个时候B可以执行完, 那么B会弹出栈. 但是B有执行完吗? 没有, 它调用了C.
- 所以C会压栈, 并且在栈顶. 而C调用了D, D会压入到栈顶.
- 所以当前的栈顺序是: 栈顶A->B->C->D栈顶
- D执行完, 弹出栈. C/B/A依次弹出栈.
- 所以我们有函数调用栈的称呼, 就来自于它们内部的实现机制. (通过栈来实现的)
- 栈面试题
- 栈的操作
- 栈常见有哪些操作呢?
- push(element): 添加一个新元素到栈顶位置.
- pop():移除栈顶的元素,同时返回被移除的元素。
- peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
- isEmpty():如果栈里没有任何元素就返回true,否则返回false。
- clear():移除栈里的所有元素。
- size():返回栈里的元素个数。这个方法和数组的length属性很类似。
实现他们:
<script>
function Stack() {
// 栈中的属性
var items = []
// 栈相关的方法
// 压栈操作
// 我们的实现是将最新的元素放在了数组的末尾, 那么数组末尾的元素就是我们的栈顶元素
this.push = function (element) {
items.push(element)
}
// 出栈操作
// 出栈操作应该是将栈顶的元素删除, 并且返回. 因此, 我们这里直接从数组中删除最后一个元素, 并且将该元素返回就可以了
this.pop = function () {
return items.pop()
}
// peek操作
// peek方法是一个比较常见的方法, 主要目的是看一眼栈顶的元素.
// 注意: 和pop不同, peek仅仅的瞥一眼栈顶的元素, 并不需要将这个元素从栈顶弹出.
this.peek = function () {
return items[items.length - 1]
}
// 判断栈中的元素是否为空
// 直接判断数组中的元素个数是为0, 为0返回true, 否则返回false
this.isEmpty = function () {
return items.length == 0
}
// 获取栈中元素的个数
// 因为我们使用的是数组来作为栈的底层实现的, 所以直接获取数组的长度即可.(也可以使用链表作为栈的顶层实现)
this.size = function () {
return items.length
}
}
var Stack=new Stack()
- 十进制转二进制:
- 为什么需要十进制转二进制?
- 现实生活中,我们主要使用十进制。
- 但在计算科学中,二进制非常重要,因为计算机里的所有内容都是用二进制数字表示的(0和1)。
- 没有十进制和二进制相互转化的能力,与计算机交流就很困难。
- 如何实现十进制转二进制?
- 要把十进制转化成二进制,我们可以将该十进制数字和2整除(二进制是满二进一),直到结果是0为止。
- 举个例子,把十进制的数字10转化成二进制的数字,过程大概是这样:
<script>
// 十进制转换成二进制
function change(number, m) {
while (number > 0) {
var a = number % m
number =parseInt( number / m)
Stack.push(a)
}
var str = ""
while (!Stack.isEmpty()) {
str += Stack.pop()
}
return str
}
var res = change(20, 2)
console.log(res)
</script>
练习一下:
创建一个栈
function Stack() {
// 栈中的属性
var items = []
// 栈相关的方法
// 压栈操作
// 我们的实现是将最新的元素放在了数组的末尾, 那么数组末尾的元素就是我们的栈顶元素
this.push = function (element) {
items.push(element)
}
// 出栈操作
// 出栈操作应该是将栈顶的元素删除, 并且返回. 因此, 我们这里直接从数组中删除最后一个元素, 并且将该元素返回就可以了
this.pop = function () {
return items.pop()
}
// peek操作
// - peek方法是一个比较常见的方法, 主要目的是看一眼栈顶的元素.
// 注意: 和pop不同, peek仅仅的瞥一眼栈顶的元素, 并不需要将这个元素从栈顶弹出.
this.peek = function () {
return items[items.length - 1]
}
// 判断栈中的元素是否为空
// 直接判断数组中的元素个数是为0, 为0返回true, 否则返回false
this.isEmpty = function () {
return items.length == 0
}
// 获取栈中元素的个数
// 因为我们使用的是数组来作为栈的底层实现的, 所以直接获取数组的长度即可.(也可以使用链表作为栈的顶层实现)
this.size = function () {
return items.length
}
}
var Stack=new Stack();
第一题:括号匹配
//思路:将输入的字符串中所有括号存入新字符串中;遍历新字符串,将所有的左括号全部入栈,右括号都出栈;判断栈内是否为空,如果为空就返回false;如果不为空就将遍历取出的每个元素与栈内的栈顶元素相比较,如果括号成对就pop出栈,最后在判断栈内是否为空,如果为空就返回true(配对成功),不为空就返回false
<script>
function match(str) {
var restr = ""
//遍历输入得字符串将所有括号存入新字符串中
for (var i = 0; i < str.length; i++) {
if (str[i] == "(" || str[i] == ")" ||
str[i] == "[" || str[i] == "]" ||
str[i] == "{" || str[i] == "}") {
restr += (str[i])
}
//遍历新字符串,如果是左括号就入栈,是右括号就出栈
}
for (var j = 0; j < restr.length; j++) {
if (restr[j] == "(" || restr[j] == "{" || restr[j] == "[") {
Stack.push(restr[j])
continue
}
//判断栈内是否为空
if (!Stack.isEmpty()) {
//如果栈内不为空,就将每个栈顶与其比较
switch (Stack.peek() + restr[j]) {
//匹配就将他移除
case "()":
Stack.pop();
break;
case "{}":
Stack.pop();
break;
case "[]":
Stack.pop();
break;
//不匹配就返回False
default:
return false
}
} else {
//如果栈为空(就是没有左括号)返回false
return false
}
}
//栈为空,匹配成功,返回true
return Stack.isEmpty()
}
console.log(match("()[]"))
</script>
第二题:判断回文数
<!-- 回文数:正读反读都能读通的数据 -->
思路:将传入的字符串全部入栈,再将其全部出栈保存到新字符串中;比较两个字符串是否一致,来判断是否是回文数
<script>
function huiwen(str){
var restr=""
//将字符串的每一个元素入栈
for(var i=0;i<str.length;i++){
Stack.push(str[i])
}
//将所有元素出栈,保存到新的字符串中
while(!Stack.isEmpty()){
restr+=Stack.pop()
}
//比较两个字符串:如果一样就返回true 不一样就返回false
if(str==restr){
return true
}else{
return false
}
}
console.log(huiwen("123421"))
</script>
第三题:十进制与任意进制的转换
//思路:[形参一(十进制数),形参二(进制数)]
如果形参一大于形参二,将形参一重复的被形参二取余,将余数push到栈内;每次取余后都要除形参二取整
如果形参一小于形参二,不够除就直接将形参一push到栈中 注意:16进制时,10==>A,11==>B,12==>C,13==>D,14==>E
最后将栈中的元素全部pop出来,存入字符串中返回
<script>
function change(number, m) {
//定义空字符串保存结果
var str = ""
while (number > 0) {
//被除数小于除数的就直接入栈
if (number < m) {
//超过10的用字母表示
var arr = ["A", "B", "C", "D", "E"]
if (number >= 10) {
var a = number - 10
var re = arr[a]
} else {
var re = number
}
Stack.push(re)
break
} else {
//被除数与除数求余,余数入栈
var res = number % m
number = parseInt(number / m)
Stack.push(res)
}
}
//将栈中的元素转换成字符串并返回
while (!Stack.isEmpty()) {
str += Stack.pop()
}
return str
}
// console.log(1111)
console.log(change(225, 16))
</script>
第四题:佩斯糖果
//思路:将sweetBox中的所有元素都出栈,并判断起元素是否时我们想要的,如果是就push到新的栈中;如果不是就不做任何操作。将新栈中的元素重新再push到sweetBox中
<script>
function clear() {
var stack = new Stack();
//取出sweetBox中的所有元素
while (!sweetBox.isEmpty()) {
var res = sweetBox.pop()
//去除yellow,剩下的进入一个新栈中
if (res == "yellow") {
} else {
stack.push(res)
}
}
//将新栈中的push到sweetBox中
while(!stack.isEmpty()){
var res2=stack.pop()
sweetBox.push(res2)
}
//返回sweetBox
return sweetBox
}
console.log(clear())
</script>