DS博客作业02--栈和队列
这个作业属于哪个班级 | 数据结构--网络2011/2012 |
---|---|
这个作业的地址 | DS博客作业02--栈和队列 |
这个作业的目标 | 学习栈和队列的结构设计及运算操作 |
姓名 | 唐宇悦 |
0.PTA得分截图
1.本周学习总结
1.1 栈
★栈中的数据元素遵守”后进先出(先进后出),时进时出”的原则,
栈顶与栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底(只能在栈顶进行插入和删除操作哦!)
顺序栈的结构、操作函数
顺序栈结构定义
#define MAXSIZE 10
struct StackNode {
int data[MAXSIZE];
int top;
};
顺序栈操作函数
// 创建空栈
StackNode* createStack() {
StackNode* stack = (StackNode*)malloc(sizeof(StackNode));
if (stack == NULL) {
cout << "Memory allocate failed." << endl;
return NULL;
}
for (int i = 0; i < MAXSIZE; i++) {
stack->data[i] = 0;
}
stack->top = -1;
return stack;
}
// 入栈
void Push(StackNode* stack, int item) {
if (stack == NULL) {
cout << "The stack is not created." << endl;
return;
}
if (stack->top == MAXSIZE - 1) {
cout << "The stack is full." << endl;
return;
}
else {
stack->data[++(stack->top)] = item;
return;
}
}
//出栈,并返回出栈数据
int Pop(StackNode* stack) {
if (stack == NULL) {
cout << "The stack is not created." << endl;
return 0;
}
if (stack->top == -1) {
cout << "The stack is empty." << endl;
return 0;
}
else {
return (stack->data[(stack->top)--]);
}
}
// 获取栈元素个数
int getStackLength(StackNode* stack) {
if (stack == NULL) {
cout << "The stack is not created." << endl;
return -1;
}
return (stack->top + 1);
}
链栈的结构、操作函数
★与顺序栈相比,链栈的结点空间可以动态申请,因此,不存在栈满上溢的情况。
链栈的基本操作包括:
- 链栈的初始化操作。链栈的初始化操作就是把链栈初始化为空,设栈顶指针为top,初始化时,不带头结点top==NULL;带头结点top->next ==NULL;
- 判断链栈是否为空。判断链栈是否为空,就是判断链栈的头结点指针域是否为空,即:top->next NULL;带头结点,不带头结点的链栈栈空条件为:topNULL;
- 进栈操作。进栈操作就是将数据元素data插入到链栈的栈顶,就是将新结点插入到链表的第一个结点之前,将新结点插入到链表中分为3个步骤p->data = data; p->next = top->next; top->next = p;p为指向新结点的指针;
- 出栈操作。出栈操作就是将栈的第一个结点删除,并将结点元素赋值给data,在元素出栈前要判断栈是是否为空;
- 取栈顶元素。取栈顶元素就是把栈顶的元素取出,并返回。在去栈顶元素之前,同样要判断栈是否为空;
- 求链栈的长度。求链表的长度就是返回链栈中的元素个数,从栈顶指针开始,通过指针域找到下一个结点,并使用变量计数,直到栈底为止;
- 销毁链栈操作。链栈的空间是动态申请的,在程序结束时要把这些结点空间通过free函数释放;
- 打印栈中元素。打印栈中元素即:将栈中元素输出。
链栈结构定义
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node* next;
}LStackNode,*LinkStack;
链栈操作函数
//链栈的初始化
void InitStack(LinkStack* top)
{
if ((*top = (LinkStack)malloc(sizeof(LStackNode))) == NULL)//为头结点开辟一个存储空间
{
exit(-1);
}
(*top)->next = NULL; //将链栈的头结点指针域置为空
}
//判断链栈是否为空
int StackEmpty(LinkStack top)
{
if (top->next==NULL)
{
return 1;
}
return 0;
}
//进栈操作
void PushStack(LinkStack top, DataType data)
{
LStackNode* p;
p = (LStackNode*)(malloc(sizeof(LStackNode)));
if (p == NULL)
{
printf("内存分配失败!\n");
}
else
{
p->data = data;
p->next = top->next;
top->next = p;
}
}
//出栈操作
void PopStack(LinkStack top,DataType* data)
{
LStackNode* p;
p = top->next;
if (p==NULL)
{
printf("栈为空!\n");
}
else
{
top->next = p->next;
*data = p->data;
free(p); //释放p指向的结点
}
}
//取栈顶元素
int GetTop(LinkStack top, DataType *data)
{
LStackNode* p;
p = top->next;
if (StackEmpty(top))
{
printf("栈为空!\n");
}
else
{
*data = p->data;
}
return *data;
}
//求表长操作
int StackLength(LinkStack top)
{
int count = 0;
LStackNode *p;
p = top;
while (p->next != NULL)
{
count++;
p = p->next;
}
return count;
}
//销毁链栈
void DestoryStack(LinkStack top)
{
LStackNode *p;
LStackNode *q;
p = top;
while (!p)
{
q = p;
p = p->next;
free(q);
}
}
//打印栈中元素
void StackPrint(LinkStack top)
{
LStackNode* p;
if (StackEmpty(top))
{
printf("栈为空!\n");
}
printf("栈中元素为:\n");
p = top;
while (p->next != NULL)
{
p = p->next;
printf("%-3d", p->data);
}
printf("\n");
}
1.2 栈的应用
表达式
表达式求和:
#include <iostream>
#include <stack>
#include <stdio.h>
#include <string.h>
using namespace std;
//符号数组
char symbol[7] = {'+', '-', '*', '/', '(', ')', '#'};
//栈内元素的优先级
int in[7] = {3, 3, 5, 5, 1, 6, 0};
//栈外元素的优先级
int out[7] = {2, 2, 4, 4, 6, 1, 0};
///通过符号字符获取它的数组下标
int get(char c)
{
switch(c)
{
case '+':
return 0;
case '-':
return 1;
case '*':
return 2;
case '/':
return 3;
case '(':
return 4;
case ')':
return 5;
case '#':
return 6;
default:
return 6;
}
}
///比较栈内运算符c1和栈外运算符c2的优先级
char precede(char c1, char c2)
{
int i1 = get(c1);
int i2 = get(c2);
if(in[i1] > out[i2])
{
return '>';
}
else if(in[i1] < out[i2])
{
return '<';
}
else
{
return '=';
}
}
///计算基本表达式的值
int figure(int a, int theta, int b)
{
switch(theta)
{
case 0:
return a + b;
case 1:
return a - b;
case 2:
return a * b;
default:
return a / b;
}
}
///计算表达式的值
int EvaluateExpression(const char *exp)
{
stack<int> data; //数据栈
stack<int> oper; //符号栈
oper.push(get('#'));
int sum = 0;
int flag = 1; //表示正负号 1,表示正 0,表示负
int a, theta, b;
if(!('-' == *exp || '(' == *exp || isdigit(*exp)))
{
cout << "表达式出错1" << endl;
return -1;
}
if('-' == *exp)
{
flag = 0;
exp++;//指向下一个字符
}
int index = oper.top();
while(*exp || symbol[index] != '#')
{
if(isdigit(*exp))
{
sum = 0;
if(flag)
{
while(isdigit(*exp))
{
sum = sum * 10 + *exp - '0';
exp++;
}
}
else
{
while(isdigit(*exp))
{
sum = sum * 10 - *exp - '0';
exp++;
}
}
data.push(sum);
flag = 1;
}
else
{
switch(precede(symbol[oper.top()], *exp))
{
case '>' :
b = data.top();
data.pop();
a = data.top();
data.pop();
theta = oper.top();
oper.pop();
data.push(figure(a, theta, b));
break;
case '<' :
oper.push(get(*exp));
if(*exp)
{
exp++;
}
break;
case '=' :
oper.pop();
if(*exp)
{
exp++;
}
break;
}
}
index = oper.top();
}
return data.top();
}
int main()
{
char s[105];
///只能算整形,浮点会错
printf("请务必保证输入的表达式正确的,时间有限,本人未加表达式查重功能\n例如:(2+4)/2+(3-1)\n");
scanf("%s",s);
cout << EvaluateExpression(s) << endl;
return 0;
}
1.3 队列
队列是一种先进先出的线性表,是一种常用的数据结构。
它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作
顺序队列的结构、操作函数
// 定义队列
typedef struct {
int data[MaxSize]; //定义数组,存放队列元素
int front, rear; // 队头和对尾指针(存放数组下标)
}SeqQueue;
//初始化
void InitSeqQueue(SeqQueue& Q) {
Q.front = Q.rear = 0;
}
//入队
void EnSeqQueue(SeqQueue& Q, int x) {
if (Q.rear == MaxSize)
{
cout << "队列已满,不能入队";
}
else {
// 先插入值到队尾,再将rear+1
Q.data[Q.rear] = x;
Q.rear++;
}
}
//出队
void DeSeqQueue(SeqQueue& Q, int& x) {
if (Q.front == Q.rear) {
cout << "队列已空,不能出队";
}
else {
// 先取出队头元素,再将front+1
x = Q.data[Q.front];
Q.front++;
}
}
环形队列的结构、操作函数
//初始化
typedef int Elemtype;
typedef struct
{
Elemtype data[MAXN];
int front, rear;
}Cseque;
void Init(Cseque &q)
{
q.front = q.rear = 0;
}
int EnQueue(Cseque &q,Elemtype x)/* 进队操作, 返回1:队满 */
{
if((q.rear+1)%MAXN==q.front)
{
cout << "队满" << endl; return 0;
}
else
{
q.rear = (q.rear + 1) % MAXN;
q.data[q.rear] = x;
return 1;
}
}
int DeQueue(Cseque &q, Elemtype *x)/* 出队操作 返回1:队空 */
{
if(q.front==q.rear)
{
cout << "队空" << endl; return 0;
}
else
{
q.front = (q.front + 1) % MAXN;
*x = q.data[q.front ];
return 1;
}
}
void OutputQueue(Cseque &q) /* 输出队列中元素 */
{
if (q.front==q.rear) cout << "队空" << endl;
int i = q.front + 1;
while (i<=q.rear)
{
cout << q.data[i] << " ";
i++;
}
cout << endl;
}
链队列的结构、操作函数
typedef int Elemtype;
//数据结点
typedef struct node
{ /* 定义队列结构 */
int data; /* 队列元素类型为int */
struct node *next;
}QNode;
//链队结点
typedef struct qptr
{
QNode *front;
QNode *rear;
}LQueue;
void EnQueue(LQueue *q,Elemtype x) /* 进队操作 */
{
QNode *p;
p =(QNode*)malloc(sizeof(QNode));
p->data = x; p->next = NULL;
q->rear->next = p;
q->rear = p;
}
int EmptyQue(LQueue *q)//队列是否为空
{
if (q->front == q->rear) return 0;
else return 1;
}
int DeQueue(LQueue *q, Elemtype *x) /* 出队操作 1:对空 */
{
QNode *p;
if(!EmptyQue(q))
{
cout << "队空" << endl; return 0;
}
else
{
p = q->front->next;
q->front->next = p->next;
*x = p->data; free(p);
if (q->front->next == NULL)
q->rear = q->front; //队中只有一个元素的情况下,出队后修改队尾指针
return 1;
}
}
void OutputQueue(LQueue *q) /* 输出队列中元素 */
{
QNode *p;
p = q->front->next;
if (!EmptyQue(q)) cout << "队空";
while (p)
{
cout << p->data<< " ";
p = p->next;
}
cout << endl;
}
void GetBack(LQueue *q)//获取对尾元素
{
cout <<"队尾元素为:"<< q->rear->data;
cout << endl;
}
void GetHead(LQueue *q)//获取对头元素
{
cout << "队头元素为:" << q->front->next->data;
cout << endl;
}
队列应用
舞伴问题
int QueueLen(SqQueue Q)//队列长度
{
return Q->rear - Q->front;//初始化时front和rear都在0处
}
int EnQueue(SqQueue& Q, Person e)//加入队列
{
if (Q->rear + 1 == MAXQSIZE)//队满
{
return false;
}
Q->rear++;
Q->data[Q->rear] = e;
//cout << e.name<<e.sex<<endl;
return true;
}
int QueueEmpty(SqQueue& Q)//队列是否为空
{
return (Q->rear == Q->front);
}
int DeQueue(SqQueue& Q, Person& e)//出队列
{
if (Q->front == Q->rear)
{
return false;
}
/*以下两句的位置不可调换*/
Q->front++;
e = Q->data[Q->front];
return true;
}
void DancePartner(Person dancer[], int num) //配对舞伴
{
int i;
Person e;
for (i = 0; i < num; i++)
{
e = dancer[i];
if (e.sex == 'F')
{
EnQueue(Fdancers, e);
}
if (e.sex == 'M')
{
EnQueue(Mdancers, e);
}
}
while (!QueueEmpty(Fdancers) && !QueueEmpty(Mdancers))
{
DeQueue(Fdancers, e);
cout << e.name;
DeQueue(Mdancers, e);
cout <<' '<<' '<<e.name;
cout << endl;
}
}
2.PTA实验作业
2.1 符号配对
2.1.1 解题思路及伪代码
基本思路就是一个一个字符处理,发现一个要处理的左符号(/,[,(,{)就入栈,发现一个需要处理的右符号(/,],),})就与栈顶对比,如果是对应的左符号,那么把栈顶弹出,否则说明这个右符号不能配对,输出NO等信息并退出。如果一直处理到程序结尾都没有退出就是YES。
定义字符串 c 存放待匹配符号
定义字符栈 s 做匹配处理
定义 i,k 控制循环
将字符串输入并存入a,逐个判断a中的字符是否为
while(1){
if 输入的是结束标志
then 结束循环
输入字符串a
for(i=0;i<a.size();i++)
if a[i]是符号
then 存入b
else if a[i]是/*
then 将<存入b且i+1
else if a[i]是*/
将>存入b且i+1
end
定义 n=k;
for i=0 to nif b[i]等于[、{、(或<
then 将b[i]入栈
else if 栈不为空
if 栈顶元素与此时的b[i]匹配
then 将栈顶出栈
else
将b[i]入栈
else
将b[i]入栈
end for
if 栈空
then输出YES
else{
输出NO
重置k=0
while 栈不空
将栈中元素逐个放入c中
将c数组中的元素逐个比较
for i=0 to k-1
for j=i+1 to kif 匹配
then c[i]c[j]都置为0
for i=0 to kif c[i]等于(、[、{
then输出c[i]-?
结束循环
else if c[i]等于'<'
then输出/*-?
结束循环
else if c[i]等于'>'
then输出?-*/
结束循环
else if c[i]等于)、]、}
then输出?-c[i]
结束循环
end
2.1.2 总结解题所用的知识点
字符栈的运用
2.2 银行业务队列简单模拟
2.2.1 解题思路及伪代码
for i=0 to n-1
if 客户编号是奇数
then 入A队
else 入B队
end
for i=0 to n-1
if A队非空
then 取出2个A队元素并输出
if B队非空
then 取出1个B队元素并输出
end
2.2.2 总结解题所用的知识点
queue用法
3.阅读代码
3.1 题目及解题代码
class MyStack {
public:
queue<int> queue1;
queue<int> queue2;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
queue2.push(x);
while (!queue1.empty()) {
queue2.push(queue1.front());
queue1.pop();
}
swap(queue1, queue2);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int r = queue1.front();
queue1.pop();
return r;
}
/** Get the top element. */
int top() {
int r = queue1.front();
return r;
}
/** Returns whether the stack is empty. */
bool empty() {
return queue1.empty();
}
};
3.2 该题的设计思路
为了满足栈的特性,即最后入栈的元素最先出栈,在使用队列实现栈时,应满足队列前端的元素是最后入栈的元素。可以使用两个队列实现栈的操作,其中queue1
用于存储栈内的元素queue2作为入栈操作的辅助队列。
入栈操作时,首先将元素入队到queue2,然后将queue1的全部元素依次出队并入队到queue2,此时queue2的前端的元素即为新入栈的元素,再将queue1和queue2
互换,则queue1的元素即为栈内的元素,queue1的前端和后端分别对应栈顶和栈底。
由于每次入栈操作都确保queue1的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除queue1的前端元素并返回即可,获得栈顶元素操作只需要获得queue1的前端元素并返回即可(不移除元素)。
由于queue1用于存储栈内的元素,判断栈是否为空时,只需要判断queue1是否为空即可。
- 时间复杂度:入栈操作 O(n),其余操作都是 O(1)。
入栈操作需要将queue1 中的 n 个元素出队,并入队 n+1 个元素到queue2 ,共有 2n+1次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是O(n)。
出栈操作对应将queue1的前端元素出队,时间复杂度是 O(1)。
获得栈顶元素操作对应获得queue1的前端元素,时间复杂度是O(1)。
判断栈是否为空操作只需要判断queue1是否为空,时间复杂度是 O(1)。
- 空间复杂度:O(n),其中n是栈内的元素。需要使用两个队列存储栈内的元素。
3.3 分析该题目解题优势及难点。
1.题目较灵活,一个队列或者两个队列的方法都能做
2.使用两个队列的方法较复杂容易出错
3.题目涉及到栈和队列两种数据结构,需掌握好两种结构才能更好做题。
栈是一种后进先出的数据结构,元素从顶端入栈,然后从顶端出栈。
队列是一种先进先出的数据结构,元素从后端入队,然后从前端出队。