一.栈和队列
1.栈stack
????栈stack是一种特殊的数据结构,它的逻辑结构和线性表相同,但是他的运算操作收到了更多的限制,即只能从一端放入数据,也只能从一端读取数据。存取数据的一端称作栈顶top,另一端的栈底bottom不允许做任何存取的操作。取出栈最上面的元素(栈顶top)这个操作叫做pop,存入元素的操作叫push,当堆栈为空时叫做空栈。 ????举个通俗的栗子:洗完碗,我们一个叠一个放好,自然是先放最下面一个碗,然后才能继续往上放碗,拿碗的时候也是先拿上面的碗,拿完了才能拿下面的碗。 ????栈的特征是后进先出。至于栈的具体实现,我们可以自己写。最简单的实现方式就是用一个数组,只在数组的尾部进行元素的push和pop即可。当然更常用的是调用C++的STL库,里面有封装好的stack数据类型,使用起来更加方便。
2.队列queue
????队列queue是另一种限定存取位置的线性表。它只允许在队列的一段存入数据push,只允许在队列的另一端取出数据pop。允许插入的一端叫做队尾,允许取出的一端叫做队头。 ????举个通俗的栗子:一队人在食堂排队打饭,大家的素质都很高。每来一个人都自动站到队尾,队头的那个人先打饭,打完饭后离开队伍。这种先进先出的数据结构就叫做队列。
二.应用
1.stack数制转换
????一个数从10进制转换为2进制,方法为:将这个数不停的整除2,直到余数为0为止。所有的商运算结果倒序输出就是最终的答案。因为这个计算算过程是从低位到高位逐个进行的,但是输出过程是从高位到低位的。因此需要用栈来实现。
#include <stdio.h>
#include <stack>
using namespace std;
int main() {
int x;
printf("请输入一个十进制数:");
scanf("%d", &x);
int tmp = x;
stack<int>n;
while (tmp != 0) {
n.push(tmp % 2);
tmp /= 2;
}
printf("%d 的二进制为 ", x);
while (!n.empty()) {
printf("%d", n.top());
n.pop();
}
printf("\n");
}
2.stack括号匹配
????一串算式,里面会有很多括号,我们需要面对的难题是:
- 这个算式中的括号是否合法:(1+2))
- 加上了括号,算式的优先级又该如何考虑
????这里我们先解决第一个问题,也就是括号匹配问题。 ????自左向右开始扫描算式,只要遇到左括号‘(’就立刻让它进栈。如果遇到右括号‘)’,分两种情况考虑:如果堆栈是空的,那么直接报错后退出程序。如果堆栈不是空的,再看栈顶,若栈顶是左括号‘(’,就将左括号‘(’抛出,如果栈顶是右括号‘)’,就将当前读到的右括号压入栈中。 ????具体的代码实现我们在下一部分一并给出。
3.表达式的计算和优先级的处理(中缀转后缀)
????在前面,我们已经知道了如何进行括号的匹配。但是,在进行表达式计算的时候,我们还需要考虑到各运算符的优先级的问题。
- 加减<乘除<幂运算
- 当符号优先级一样时,从左往右先出现的运算符优先运算。(幂运算很特殊,要注意不遵守此条,可以考虑
a
b
c
a^{b^c}
abc,就是b和c先结合)
????基于上述考虑,我们已经有了如下思路: ????开两个堆栈,figures存放字母,opt存放运算符。读到数字就往figures里push,读到运算符就往opt里push。如果当前读到的运算符的优先级比opt的top低的话,那opt就出栈,并从figures中出栈2个字母参与运算,直到opt的栈顶优先级比当前读到的运算符低或者空栈,再将当前的运算符push。 ????注意:左括号在堆栈外优先级最高,一旦进入栈中优先级变最低。
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;
char operation[100000];
stack<char>x;
int compare_opt(char a, char b) {
int x, y;
if (a == '+' || a == '-') x = 1;
else if (a == '^')x = 3;
else if (a == '(')x = 0;
else x = 2;
if (b == '*' || b == '/')y = 2;
else if (b == '^')y = 10;
else if (b == '(')y = 0;
else y = 1;
return x - y;
}
int main() {
int N;
scanf("%d", &N);
getchar();
while (N) {
gets(operation);
int len = strlen(operation);
for (int i = 0; i < len - 1; i++) {
if ((operation[i] <= 'z' && operation[i] >= 'a') || (operation[i] <= 'Z' && operation[i] >= 'A')) {
printf("%c", operation[i]);
}
else {
if (operation[i] == '(') {
x.push(operation[i]);
}
else if (operation[i] == ')') {
while (x.top() != '(') {
printf("%c", x.top());
x.pop();
}
x.pop();
}
else {
if (x.empty()) {
x.push(operation[i]);
}
else {
while (!x.empty() && compare_opt(x.top(), operation[i]) >= 0) {
if (operation[i] == '^' && compare_opt(x.top(), operation[i]) == 0)break;
printf("%c", x.top());
x.pop();
}
x.push(operation[i]);
}
}
}
}
while (!x.empty()) {
printf("%c", x.top());
x.pop();
}
printf("\n");
N--;
}
}
4.queue输出杨辉三角行
????在计算机中,队列常常用于解决需要逐行处理的问题。杨辉三角行就是一个典型的分层处理的例子。杨辉三角行的每一行的数字正好对应二项式
(
a
+
b
)
i
(a+b)^i
(a+b)i展开各项的系数。 ????杨辉三角行的一个性质为:它的第i行的各个数字对应第i-1相应的两个数字的相加。我们可以采用一个队列,逐层处理这个分层结构。具体代码如下
#include <stdio.h>
#include <queue>
using namespace std;
int main() {
int n;
int x = 0, y;
queue<int>que;
scanf("%d", &n);
que.push(1);que.push(1);
for (int i = 1; i < n; i++) {
printf("\n");
que.push(0);
for (int j = 1; j <= i + 2; j++) {
y = que.front(); que.pop();
que.push(x + y);
x = y;
if (j != i + 2)printf(" %d", x);
}
}
}
5.利用堆栈把递归过程改成非递归过程
????先来看一个递归的经典,汉诺塔问题。
#include <stdio.h>
void Hanoi(int n, char A, char B, char C) {
if (n == 1)
printf("Move disk from %c to %c.\n", A, C);
else {
Hanoi(n - 1, A, C, B);
printf("Move disk from %c to %c.\n", A, C);
Hanoi(n - 1, B, A, C);
}
}
int main() {
int n;
scanf("%d", &n);
Hanoi(n, 'A', 'B', 'C');
}
????这里采用了分治法的思路。将n个盘子的移动分解为一个盘子(把n-1个盘子打包看作1个)和另一个盘子之间如何移动,将求解规模为n的问题分解成了两个规模为n-1的问题。按照这种自顶向下,逐层分解的原则,从而设计出来递归算法。 ????但是,稍加分析我们发现,这个算法的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),和递归深度的平方成正比。同时,由于递归调用时需要调用递归工作栈存放每一轮递归的局部变量的,因此空间复杂度为
O
(
n
)
O(n)
O(n)。 ????这很不妙。因此,我们希望利用栈把它改成非递归的,希望能对其时空复杂度进行优化。
|