DS博客作业02--栈和队列
0.PTA得分截图
1.本周学习总结
1.1 总结栈和队列内容
栈的总结
- 栈是一种操作受限制的线性表,只允许在栈顶进行插入删除操作,插入称为进栈或入栈,删除叫做出栈,没有元素的栈称为空栈。其特点是先进后出或后进先出。
- 顺序存储结构是在内存中开辟一个连续的空间用来存储数据,因此对于内存的需求和苛刻,必须是连续的空间.在数据查找(特别是不按照规律排列的数据),时间复杂度教少.效率高.
链式存储结构是采取连表指针来指示数据的存储位置,这就可以是在内存中随意的存储,没有必须连续储存空间的要求,对于内存的要求相对教容易.但是要是是从小到大顺序排列的数据,链式存储结构的时间复杂度教小,效率高.但是要是不规则排布的数据一般时间复杂度较高,效率更低 - 栈的存储结构
1.栈的顺序存储结构
typedef struct
{
ElemType *base;//栈底
ElemType *top;//栈顶
int stackSize;//栈的当前可使用的最大容量
}sqStack;
或者
typedef int ElemType;
typedef struct
{
ElemType *data;
int top; // 用于标注栈顶的位置
int MAXSIZE;
}ST;
2.栈的链式存储结构
typedef char ElemType;
typedef struct linknode
{
ElemType data;
struct linknode *next;
}LinkStNode;LinkStNode *s;
- 栈的操作
1.栈的初始化
顺序栈
Stack CreateStack(int MaxSize)
{
Stack p = (Stack)malloc(sizeof(struct ST));
p->top = -1;
p->MaxSize = MaxSize;
return p;
}
链栈
void InitStack(LinkStNode*&s)//初始化栈
{
s=(LinkStNode*)malloc(sizeof(LinkStNode));
s->next=NULL;
}
2.入栈
顺序栈
Status Push(SqStack *s, SElemType e) {
if (s->top == MAXSIZE - 1) { //栈满
return ERROR;
}
s->top ++; //栈顶指针加1
s->data[s->top] = e; //将新元素赋值给栈顶空间
return OK;
}
链栈
void Push(LinkStNode*&s)//进栈
{
ElemType e;
int i=0;
LinkStNode *p;
cout<<"依次进栈的元素为:";
while (i<n)
{
cin>>e;
p=(LinkStNode*)malloc(sizeof(LinkStNode));
p->data=e;
p->next=s->next;
s->next=p;
i++;
}
}
3.出栈
顺序栈
Status Pop(SqStack *s, SElemType *e) {
if (s->top == -1) { //空栈
return ERROR;
}
*e = s->data[s->top]; //将要删除的栈顶元素赋值给e
s->top --; //栈顶指针减1
return OK;
}
链栈
void Pop(LinkStNode *&s)//出栈操作
{
LinkStNode *p;
while(s->next!=NULL)
{
p=s->next;
s->next=p->next;
free(p);
}
}
4.判断栈空
顺序栈
Status StackEmpty(SqStack S)
{
if(S.top == S.base)
return TRUE;
else
return FALSE;
}
链栈
void StackEmpty(LinkStNode *s)//判断栈是否为空
{
if(s->next==NULL)
{
cout<<"栈为空"<<endl;
}
else
cout<<"栈非空"<<endl;
}
5.栈的应用
- 表达式转换
伪代码
输入一串表达式
flag=1
建立一个栈
for i=0 str[i]为真 i++
if 字符为0到9或‘.’
对于数字,小数,多位数进行输出
end if
if 字符为(,且下一个字符为+或-(有正负号的情况)
正号不输出,负号输出,且负号与数字之间无空格
同时考虑带有正负号的小数,多位数
end if
if 字符为+或-
if 第一个符号为-,直接输出(负数在符号在首位)
end if
对+,-号进行入栈、出栈、输出处理
end if
if 符号为*或/
对*,/号进行入栈、出栈、输出处理
end if
if 符号为 ( 或 )
对*,/号进行入栈、出栈、输出处理
end if
end for
while 栈不空
输出剩余的符号
end while
具体代码
#include<iostream>
#include<stack>
#include<string>
#define Max 25
using namespace std;
int main()
{
int i;
stack<char>st;
char str[Max];
cin >> str;
int flag = 1;
for (i = 0;str[i];i++)
{
if ((str[i] <= '9'&&str[i] >= '0') || str[i] == '.')
{
if (!flag)
{
cout << " ";
}
cout << str[i];
while (str[i + 1] == '.' || (str[i + 1] >= '0' && str[i + 1] <= '9'))
{
i++;
cout << str[i];
}
flag = 0;
}
if (str[i] == '(' && (str[i + 1] == '+' || str[i + 1] == '-'))
{
while (str[i - 1] != ')')
{
if (i != 0 && str[i] != '('&&str[i] != ')')
{
cout << " ";
}
if (str[i] != '+'&&str[i] != '('&&str[i] != ')')
cout << str[i];
while (str[i + 1] == '.' || (str[i + 1] >= '0' && str[i + 1] <= '9'))
{
i++;
cout << str[i];
}
i++;
}
i--;
continue;
}
if (str[i] == '+' || str[i] == '-')
{
if (i == 0)
{
if (str[i] == '-')
cout << "-";
continue;
}
while (!st.empty())
{
if (st.top() != '(')
{
cout << " " << st.top();
st.pop();
}
else
break;
}
st.push(str[i]);
}
if (str[i] == '*' || str[i] == '/')
{
while (!st.empty())
{
if (st.top() == '*' || st.top() == '/')
{
cout << " " << st.top();
st.pop();
}
else
break;
}
st.push(str[i]);
}
if (str[i] == '(' || str[i] == ')')
{
if (str[i] == '(')
st.push(str[i]);
else
{
while (st.top() != '(')
{
cout << " " << st.top();
st.pop();
}
st.pop();
}
}
}
while (!st.empty())
{
cout << ' ' << st.top();
st.pop();
}
return 0;
}
队列的总结
- 队列是操作受限制的线性表,允许在队尾插入,在队头删除。特点与栈相反,是先进先出。
- 顺序队列顾名思义就是顺序存储的队列,一般在数组上实现,为避免假溢出,多采用循环的方式来管理顺序队列,链式队列自然是用链表来存储的队列,没有空间溢出的问题
- 队列的存储结构
1.队列的顺序存储结构
typedef struct{
int data[MAXQSIZE]; //容量
int front; //队头
int rear; //队尾
}SqQueue;
2.队列的链式存储结构
typedef int QElemType;
typedef struct QNode { //结点结构
QElemType data;
struct QNode *next;
}QNode;
typedef struct QNode * QueuePtr;
typedef struct { //队列的链表结构
QueuePtr rear;
QueuePtr front;
}LinkQueue;
- 队列的操作
1.队列的初始化
顺序队循环队列
int InitQueue(SqQueue &Q) {//构造一个空队列Q
Q = new Queue; //为队列分配一个最大容量为MAXSIZE的数组空间
if (!Q->data)
exit( OVERFLOW); //存储分配失败
Q->front = Q->rear = 0; //头指针和尾指针置为零,队列为空
return OK;
}
链队
void InitLinkQueue(LinkQueue* LQ)
{
//创建一个头结点
LinkQueueNode* pHead = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
assert(pHead);
LQ->front = LQ->rear = pHead; //队头和队尾指向头结点
LQ->front->_next = NULL;
}
2.入队列
循环队列
int EnQueue(SqQueue &Q, Person e)
{
if (((Q->rear + 1) % MAXQSIZE) == Q->front)return 0;
else
{
Q->rear = (Q->rear + 1) % MAXQSIZE;
Q->data[Q->rear] = e;
}
return 1;
}
链队列
void EnterLinkQueue(LinkQueue* LQ, DataType data)
{
//创建一个新结点
LinkQueueNode* pNewNode = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
assert(pNewNode);
pNewNode->_data = data; //将数据元素赋值给结点的数据域
pNewNode->_next = NULL; //将结点的指针域置空
LQ->rear->_next = pNewNode; //将原来队列的队尾指针指向新结点
LQ->rear = pNewNode; //将队尾指针指向新结点
}
3.出队列
循环队列
int DeQueue(SqQueue &Q, Person &e)//出队列
{
if (Q->rear == Q->front)return 0;
else
{
Q->front = (Q->front + 1) % MAXQSIZE;
e = Q->data[Q->front];
return 1;
}
}
链队列
void DeleteLinkQueue(LinkQueue* LQ,DataType* data)
{
if (IsEmpty(LQ))
{
return false;
}
//pDel指向队头元素,由于队头指针front指向头结点,所以pDel指向头结点的下一个结点
LinkQueueNode* pDel = LQ->front->_next;
*data = pDel->_data; //将要出队的元素赋给data
LQ->front->_next = pDel->_next; //使指向头结点的指针指向pDel的下一个结点
//如果队列中只有一个元素,将队列置空
if (LQ->rear = pDel)
{
LQ->rear = LQ->front;
}
free(pDel); //释放pDel指向的空间
}
4.队空
循环队列
int IsFull(SeqCirQueue* Q)
{
if ((Q->rear + 1) % MaxSize == Q->fornt)
{
return 1;
}
return 0;
}
链队
int IsEmpty(SeqQueue* SQ)
{
if (SQ->fornt == SQ->rear)
{
return 1;
}
return 0;
}
5.队列应用
- 银行业务队列简单模拟
伪代码
定义两个队列qu1,qu2
输入n
for i=0 to n
输入编号
if编号为偶数,入2队,队列长度增加
else入1队,队列长度增加
end for
if 1队长大于2队长的2倍(说明2队列的顾客先处理完)
while (1队不空或2队不空)
if 1队不空 进行输出和出栈操作(此时要考虑空格输出的差别)
if 2队不空 进行输出和出栈操作
end while
else
while (1队不空或2队不空)
if 1队不空 进行输出和出栈操作
if 2队不空 进行输出和出栈操作(此时要考虑空格输出的差别)
end while
具体代码
#include<iostream>
#include<queue>
using namespace std;
int main()
{
int i, n, num;
int count1 = 0, count2 = 0;
queue<int>qu1;
queue<int>qu2;
cin >> n;
for (i = 0;i < n;i++)
{
cin >> num;
if (num % 2 == 0)
{
qu2.push(num);
count2++;
}
else
{
qu1.push(num);
count1++;
}
}
if (count1 > count2 * 2)
{
while (!qu1.empty() || !qu2.empty())
{
if (!qu1.empty())
{
cout << qu1.front();
qu1.pop();
if (!qu1.empty())
{
cout << " " << qu1.front();
qu1.pop();
if (!qu1.empty())
cout << " ";
}
}
if (!qu2.empty())
{
cout << qu2.front();
qu2.pop();
cout << " ";
}
}
}
else
{
while (!qu1.empty() || !qu2.empty())
{
if (!qu1.empty())
{
cout << qu1.front();
qu1.pop();
if (!qu1.empty())
{
cout << " " << qu1.front();
qu1.pop();
}
cout << " ";
}
if (!qu2.empty())
{
cout << qu2.front();
qu2.pop();
if (!qu2.empty())
cout << " ";
}
}
}
return 0;
}
栈及队列的STL容器
- 栈
1.头文件: #include
2.定义栈,以如下形式实现: stacks; 其中Type为数据类型(如 int,float,char等)
3.主要操作
s.push(item); //将item压入栈顶
s.pop(); //删除栈顶的元素,但不会返回
s.top(); //返回栈顶的元素,但不会删除
s.size(); //返回栈中元素的个数
s.empty(); //检查栈是否为空,如果为空返回true,否则返回false
- 队列
1.头文件 : #include
2.定义队列,以如下形式实现: queueq; 其中Type为数据类型(如 int,float,char等)
3.主要操作
q.push(item) //将item压入队列尾部
q.pop() //删除队首元素,但不返回
q.front() //返回队首元素,但不删除
q.back() //返回队尾元素,但不删除
q.size() //返回队列中元素的个数
q.empty() //检查队列是否为空,如果为空返回true,否则返回false
1.2对栈和队列的认识及学习体会
1.对栈和队列的认识
栈和队列都可以看作是特殊的线性表,在操作上有一定的限制,但正是这些限制,使它们能够较为简单地完成某些特定的问题。栈就像一个箱子,往箱子里放东西,最先放进去的东西在最下面,在往外拿的时候,首先要拿最上面的也就是最后放进去的东西,所谓后进先出就是这样。而队列就好比一队人排队办理业务,先买票的人买完走之后,后面的人才能买,而新来的需要排在队列末尾,这就是先进先出。
2.体会
栈与队列的学习与之前的顺序表有很多相通之处,所以理解起来并不是很困难。但我们写代码的时候,一般都是直接用STL容器里栈和队列的函数,虽然很方便,但也无形中削弱了理解力,pta里的函数题目也多多少少弥补了这一点。较为简单的问题没什么困难,但对于复杂点的题目,应付起来还是很困难。还是需要多多练习。
2.PTA实验作业
2.1电路布线
2.1.1代码截图
2.1.2本题PTA提交列表及说明
说明:本题与迷宫问题类似,但本题只需输出最短路径的长度即可。刚开始对于坐标的判断错误,就是出口的坐标是(2,1),而我用数组却是(1,0),于是加了一个边框,使得数组[2][1]变为入口,然后就过了。
2.2银行排队问题之单队列多窗口服务
2.2.1代码截图
2.2.2本题PTA提交列表及说明
说明:部分正确是由于将每个窗口人数的人数累加放入了找最少等待时间窗口的循环中,导致每个窗口的人数都比较多,把累加代码放在窗口循环之外。段错误是由于在更改代码时将大括号的位置更改了,导致一些空间访问出错,改过来就好了。
3.阅读代码
3.1 题目及解题代码
3.1.1 该题的设计思路
设计思路:压入序列每次入栈的同时,将压入的元素与弹出序列的元素相比,若相等则出栈元素,继续比较,若不等则继续入栈。最后若栈为空,弹出序列是可以的
时间复杂度O(n²):双层循环,执行次数n²
空间复杂度O(n):新建立了一个栈
3.1.2 该题的伪代码
建栈
for i=0 to 压入序列长度
元素入栈
while 栈不空&&执行次数小于弹出序列长度&&两序列相应元素相等
出栈
end while
end for
3.1.3 运行结果
3.1.4析该题目解题优势及难点
优势:新建了一个栈,相较于其他解法更加简单易懂。运用了表达式? x : y ,使代码更加简洁。
难点:找到一个合适的处理方式来比较两个序列中的元素
3.2题目及解题代码
3.2.1该题的设计思路
设计思路:将所有数据排序后,进行操作使每一个数都比前一个数至少大1
时间复杂度:O(n),sort排序一次,所有数据遍历一次
空间复杂度:O(n),重建vector存放排序之后的数据
3.2.2该题伪代码
新建vector存放数据
将数据由小到大排序
for 遍历数据
保证每一个数都比前一个数至少大1
end for
返回最少次数
3.2.3运行结果
3.2.4分析该题目解题优势及难点
优势:使用sort函数,很方便的将数据排序好。巧妙利用数之间的关系,使后一个数比前一个数至少大1,而不是一个一个加来加去
难点:如何使后一个数比前一个数至少大1