目录
自定义的栈和队列的实现
栈的顺序存储
#define maxsize 10
struct sta
{
int data[maxsize];
int top; //top指向栈顶元素
};
//栈的初始化
void InitStack(sta& s)
{
s.top = -1;
}
//判断栈是否为空
bool StackEmpty(sta& s)
{
if (s.top == -1) return true;
return false;
}
//进栈
bool Push(sta& s, int x)
{
if (s.top == maxsize - 1) return false;
s.top++;
s.data[s.top] = x;
return true;
}
//出栈
bool pop(sta& s, int& x)
{
if (s.top == -1) return false;
x = s.data[s.top];
s.top--;
return true;
}
//读栈顶元素
bool GetTop(sta& s, int& x)
{
if (s.top == -1) return false;
x = s.data[s.top];
return true;
}
队列的顺序存储
#define maxsize 10
struct que
{
int data[maxsize];
int front; //front指向队首元素
int rear; //rear指向队尾元素的下一个位置
};
//队列的初始化
void InitQueue(que& q)
{
q.front = 0;
q.rear = 0;
}
//判断队列是否为空
bool QueueEmpty(que& q)
{
if (q.front == q.rear) return true;
return false;
}
//进队
bool EnQueue(que& q, int x)
{
//判断是否队满
if ((q.rear + 1) % maxsize == q.front) return false;
q.data[q.rear] = x;
q.rear = (q.rear + 1) % maxsize;
return true;
}
//出队
bool DeQueue(que& q, int& x)
{
//判断是否队空
if (q.rear == q.front) return false;
x = q.data[q.front];
q.front = (q.front + 1) % maxsize;
return true;
}
//读队首元素元素
bool GetHead(que& q, int& x)
{
//判断是否队空
if (q.rear == q.front) return false;
x = q.data[q.front];
return true;
}
这种情况下front指向队首元素,rear指向队尾元素的下一个位置,队列中必须牺牲一个单元来区分队空和队满两种情况。
该种情况下队列长度为:(q.rear + maxsize - q.front) % maxsize
为了解决牺牲一个队列单元的问题,可以在类型中使用一个数据成员size来表示队列中的元素个数;或者也可以在类型中增加一个数据成员tag,为0表示最近的一次操作是删除,为1表示最近的一次操作是插入,这样当q.front == q.rear时,如果tag为0则表示队空,为1则表示队满。
队列的链式存储
#define maxsize 10
//链式队列的结点
struct quenode
{
int data;
quenode* next;
};
//带头结点的链式队列
struct quelink
{
quenode* front,* rear;
};
//队列的初始化
void InitQueue(quelink* q)
{
q->front = q->rear = new quenode;
q->front->next = nullptr;
}
//判断队列是否为空
bool QueueEmpty(quelink* q)
{
//也可以用q->front == nullptr来判断队空
if (q->front == q->rear) return true;
return false;
}
//进队
void EnQueue(quelink* q, int x)
{
//链式队列不用判断是否队满
quenode* cur = new quenode;
cur->data = x;
cur->next = nullptr;
q->rear->next = cur;
q->rear = cur;
}
//出队
bool DeQueue(quelink* q, int& x)
{
//判断是否队空
if (q->front == q->rear) return false;
quenode* temp = q->front->next;
x = temp->data;
q->front->next = temp->next;
//如果队列中只有一个结点,令尾节点重新指向头结点
if (temp == q->rear)
{
q->rear = q->front;
}
delete temp;
return true;
}
//读队首元素元素
bool GetHead(quelink* q, int& x)
{
//判断是否队空
if (q->front == q->rear) return false;
quenode* temp = q->front->next;
x = temp->data;
return true;
}
手写代码版的简易栈
#define maxsize 100
int main()
{
int sta[maxsize]; //栈
int top = -1; //栈顶指针
//入栈
sta[++top] = 1;
//出栈
top--;
//读栈顶元素
int x = sta[top];
//读栈顶元素并出栈
int x = sta[top--];
//判断是否栈空
if (top == -1);
return 0;
}
手写代码版的简易队列
#define maxsize 100
int main()
{
int que[maxsize]; //队
int front = 0, rear = 0; //队首指针和队尾指针
//入队
que[rear++] = 1;
rear %= maxsize;
//出队
front = (front + 1) % maxsize;
//读队首元素
int x = que[front];
//读队首元素并出队
int x = que[front++];
front %= maxsize;
//判断是否队空
if (front == rear);
//计算队列的长度
int len = (rear + maxsize - front) % maxsize;
return 0;
}
栈在括号匹配中的应用
bool bracketcheck(string str)
{
sta s;
InitStack(s);
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '(' || str[i] == '[' || str[i] == '{')
{
Push(s, str[i]);
}
else
{
//如果栈为空,说明当前右括号没有匹配的左括号
if (StackEmpty(s)) return false;
char x;
pop(s, x); //弹出栈顶元素
//判断括号是否匹配
if (str[i] == ')' && x != '(') return false;
else if (str[i] == ']' && x != '[') return false;
else if (str[i] == '}' && x != '{') return false;
}
}
//最后判断栈是否为空,若不为空说明有左括号未匹配
return StackEmpty(s);
}
栈在表达式求值中的应用
【注】
②:根据这条性质的说明,栈中的括号只可能含有左括号“(”。
另外,在括号中遇到多个运算符时,也要参照③中的规则。
③:若栈中含有优先级高于等于当前运算符的其他运算符,那么这些运算符应该先运算完才对。
比如若当前遇到了+,然后栈中含有一个×,那么根据运算符的优先级,应该先算×,后算+,所以先将×出栈。
又比如,当前遇到了+,然后栈中含有一个-,虽然他们的优先级相等,但是根据“左优先”原则,也应该先算-,后算+,所以先将-出栈。
再比如,当前遇到了×,然后栈中含有一个+,那么根据运算符的优先级就不用将+弹出栈了,因为应该先算×,后算+。
实际上,中缀转后缀,以及计算后缀表达式,这两步可以结合起来同时做:
实现代码如下:
//计算运算符优先级
int priority(char op)
{
if (op == '*' || op == '/') return 2;
else if (op == '+' || op == '-') return 1;
}
//简单的计算
int caculate(int left, int right, char op)
{
int temp = 0;
if (op == '+') temp = left + right;
else if (op == '-') temp = left - right;
else if (op == '*') temp = left * right;
else if (op == '/') temp = left / right;
return temp;
}
//中缀转后缀并计算
int turn_caculate(string str)
{
stack<char> op_stack; //符号栈
stack<int> num_stack; //操作数栈
for (int i = 0; i < str.size(); i++)
{
if (str[i] == ' ') continue;
int temp = 0;
//如果接下来遇到的是操作数,直接入操作数栈
if ('0' <= str[i] && str[i] <= '9')
{
for (int j = i; j < str.size(); j++)
{
if ('0' <= str[j] && str[j] <= '9')
{
temp = temp * 10 + (str[j] - '0');
i++;
}
else
{
i--;
break;
}
}
num_stack.push(temp);
}
//如果遇到的是符号
else
{
switch (str[i])
{
//如果是左括号,直接入符号栈
case '(':
if (str[i] == '(') op_stack.push(str[i]);
break;
//如果是右括号,依次弹出符号栈内运算符直到左括号为止
case ')':
while (op_stack.top() != '(')
{
//边弹符号边计算
char op = op_stack.top();
op_stack.pop();
int right = num_stack.top();
num_stack.pop();
int left = num_stack.top();
num_stack.pop();
temp = caculate(left, right, op);
num_stack.push(temp);
}
//最后弹出符号栈中的左括号
op_stack.pop();
break;
//如果是运算符
default:
//依次弹出符号栈中,优先级高于或等于当前运算符的所有运算符,直到符号栈空或遇到左括号
while (!op_stack.empty() && op_stack.top() != '(' && priority(op_stack.top()) >= priority(str[i]))
{
//同样边弹运算符边计算
char op = op_stack.top();
op_stack.pop();
int right = num_stack.top();
num_stack.pop();
int left = num_stack.top();
num_stack.pop();
temp = caculate(left, right, op);
num_stack.push(temp);
}
//最后将当前运算符入栈
op_stack.push(str[i]);
break;
}
}
}
//最后将符号栈中剩余的运算符依次弹出并计算
while (!op_stack.empty())
{
int temp = 0;
char op = op_stack.top();
op_stack.pop();
int right = num_stack.top();
num_stack.pop();
int left = num_stack.top();
num_stack.pop();
temp = caculate(left, right, op);
num_stack.push(temp);
}
return num_stack.top();
}
这里偷了懒,栈直接用的容器,没有设置除0判断啥的。
3.3 栈和队列的应用-课后习题
2、车厢调度
string mobilize(string train)
{
sta s;
InitStack(s);
string ans;
for (int i = 0; i < train.size(); i++)
{
if (train[i] == 'H')
{
Push(s, train[i]);
}
else
{
ans.push_back(train[i]);
}
}
while (!StackEmpty(s))
{
char x;
pop(s, x);
ans.push_back(x);
}
return ans;
}
3、利用栈实现该递归函数的非递归计算
这题说实话,这个函数没怎么看懂,这个下标n其实就是一个参数,函数实际上长这样:
举个例子,比如计算P(3, x)的话:
书上所给的答案,就是利用滚动数组的思想,用fv1和fv2来保存每次所需要的两个P()函数值,所以这题为啥要用栈呢。。我这里的循环是从2到n,按书上的答案来的话,栈的作用实际上就是让循环从n到2,我没太明白这题用栈的意义。
double func(int n, double x)
{
double fv1 = 1;
double fv2 = 2 * x;
for (double i = 2; i <= n; i++)
{
double temp = 2 * x * fv2 - 2 * (i - 1) * fv1;
fv1 = fv2;
fv2 = temp;
}
if (n == 0) return 1;
return fv2;
}