杨佳琴

栈和队列

0.PTA得分截图

1.本周学习总结

1.1.1 栈的介绍

栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据,最后一个数据被第一个读出来。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。
栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶,另一端为栈底;栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈,删除则称为退栈。

  1. 栈中元素的特性
    . 具有线性关系
    . 后进先出
  2. 栈的进栈出栈原则
    . 栈顶出栈->栈底最后出栈
    . 时进时出->元素未完全进栈时,即可出栈
  3. 两种存储结构:顺序存储结构和链式存储结构

1.1.2 顺序栈

顺序栈介绍

利用一组地址连续的存储单元依次存放栈底到栈顶的数据元素,栈底位置固定不变,栈顶位置随着入栈和出栈操作而变化。

顺序栈四要素:

  1. 栈空条件:top = -1
  2. 栈满条件:top = MaxSize - 1
  3. 进栈e操作:top++;st->data[top] = e
  4. 退栈操作:e = st->data[top];top--

定义结构体

假设栈的元素个数最大不超过正整数MaxSize,所有元素都具有同一元素数据类型ElemType,则栈类型SqStack定义为

typedef struct
{
	ElemType data[MaxSize];
	int top;//栈顶指针
}Stack;
typedef Stack* SqStack;

初始化栈

void InitStack(SeqStack *S)
{
    /* top为-1表示空栈 */
    S->top = -1;
}

栈顶元素下标 top 等于 -1 为空栈,所以只需将 top 赋值 -1 即可完成顺序栈的初始化。

销毁栈

void DestroyStack(SqStack &s)
{
delete s;
}

判断栈是否为空

bool StackEmpty(SqStack s)
{
return(s->top==-1)
}

进栈

在栈不满的条件下,先将栈指针增1,然后在该位置上插入元素e。

bool Push(SqStack &s, ElemType e)
{
	if (s->top == MaxSize - 1)
		return false;
	s->top++;
	s->data[s->top] = e;
	return true;
}

出栈

在栈不为空的条件下,先将栈顶元素赋给e,然后将栈指针减1.

bool Pop(SqStack &s, ElemType &e)
{
	if (s->top == -1)//栈为空的情况下,栈下溢出
		return false;
	e = s->data[s->top];//取栈顶指针元素
	s->top--;
	return true;
}

取栈顶元素

在栈不为空的条件下,将栈顶元素赋给e。

bool GetTop(SqStack *s, ElemType &e)
{
	if (s->top == -1)//栈为空的情况
		return false;
	e = s->data[s->top];
	return true;
}

1.1.3 共享栈

如果需要用到两个相同类型的栈,可以用一个数组data[0...MaxSize来实现这两个栈,这称为共享栈。
. 栈空条件:栈1空为top1 == -1;栈2空为top2 == MaxSize
. 栈满条件:top1 == top2 - 1
. 元素x进栈操作:进栈1操作为top++;data[top1] = x;进栈2操作为top2--;data[top2] = x;
. 出栈x操作:出栈1操作为x = data[top1];top1--;出栈2操作为x = data[top2];top2++;

定义结构体

data数组表示共享栈的存储空间,top1和top2分别为两个栈的栈顶指针,这样改共享栈通过data,top1和top2来标识,也可以将它们设计为一个结构体类型:

typedef struct
{
	ElemType data[MaxSize];//存放共享栈中的元素
	int top1, top2;//两个栈的栈顶指针
}DStack;

1.1.4 链栈

采用链表存储的栈称为链栈,这里采用带头结点的单链表实现。
链栈四要素:
. 栈空的条件:s->next == NULL
. 栈满的条件:由于只有内存溢出时才出现栈满,通常不考虑,所以看作不存在栈满
. 元素e的进栈操作:新建一个结点存放元素e(由p指向它),将结点p插入到头节点之后,头插法
. 出栈操作:取出首结点的data值并将其删除

定义结构体

typedef int ElemType;
typedef struct linknode
{
ElemType data;//数据域
struct linknode* next;//指针域
}LiNode, LiStack;

初始化栈

void InitStack(LiStack &s)
{
	s = new LiNode;
	s->next = NULL;
}

销毁栈

释放链栈占用的全部空间结点,和单链表的销毁算法相同

void DestroyStack(LiStack& s)
{
	LiStack node;
	while (s != NULL)
	{
		node = s;
		s = s->next;
		delete node;
	}
}

判断栈是否为空

栈s为空的条件时s->next=NULL,同空链表

bool StackEmpty(LiStack s)
{
	return(s->next==NULL)
}

进栈

新建一个结点,用于存放元素e(由p指向它),然后将其插入头节点之和作为新的首结点

void Push(LiStack& s, ElemType e)
{
	liStack p;
	p = new Linode;
	p->data = e;
	p->next = s->next;
	s->next = p;
}

出栈

在栈不为空的情况下提取首结点的值赋给参数e,然后将其删除

void Pop(LiStack& s, ElemType& e)
{
	LiStack p;
	if (s->next == NULL)//栈空
		return false;
	p = s->next;//p指向开始结点
	e = p->data;//取值
	s->next = p->next;//删除p结点
	delete p;//释放p结点
	return true;
}

取栈顶元素

在栈不为空的条件下提取首结点的数据域赋给参数e

void GetTop(LiStack s, ElemType& e)
{
	if (s->next == NULL)//栈空
		return false;
	e = s->next->data;
	return true;
}

1.1.5 栈c++模板

#include <stack>///头文件
stack<int>s;//初始化栈,参数表示元素类型
s.push(t);//入栈元素t
s.top();//返回栈顶元素
s.pop();//出栈,物理删除栈顶数据,不返回该元素
s.empty();//栈空时,返回true
s.size();//访问栈中的元素个数

1.2 栈的应用

  1. 数制转换
  2. 括号匹配检验
  3. 迷宫求解
  4. 表达式求值 & 中缀表达式转后缀表达式

1.3 队列

1.3.1 队列的介绍

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除。
特点:先进先出
队尾:允许插入的一端
对头:允许删除的一端

1.3.2 顺序队列

采用顺序存储结构的队列称为顺序队,即分配一块连续的存储空间来存放队列中的元素,并用两个整型变量来反映队列中元素的变化,它们分别储存队首元素和队尾元素的下标位置,分别称为队首指针(队头指针)和队尾指针。

定义结构体

typedef struct
{
	ElemType data[MaxSize];
	int front, rear;//队首和队尾指针
}Queue;
typedef Queue* SqQueue;

初始化队列

void InitQueue(SqQueue& q)
{
	q = new Queue;
	q->front = q->rear = -1;
}

销毁队列

void DestroyQueue(SqQueue& q)
{
	delete q;
}

判断队列是否为空

bool QueueEmpty(SqQueue q)
{
	return(q->front == q->rear);
}

进队列

bool enQueue(SqQueue& q, ElemType e)
{
	if (q->rear + 1 == MaxSize)//队满
		return false;
	q->rear = q->rear + 1;
	q->data[q->rear] = e;
	return true;
}

出队列

bool deQueue(SqQueue& q, ElemType& e)
{
	if (q->front == q->rear)//队空
		return false;
	q->front = q->front + 1;
	e = q->data[q->front];
	return true;
}

1.3.3 循环队列

把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的表达式从逻辑上看成一个环。

. 队头指针front循环增一:front=(front+1)%MaxSize
. 队尾指针rear循环增一:rear=(rear+1)%MaxSize
. 队空条件:q->rearq->front
. 队满条件:(q->rear+1)%MaxSize
q->front

定义结构体

typedef struct
{
	ElemType data[MaxSize];
	int front, rear;
}Queue;
typedef Queue* SqQueue;

初始化队列

void InitQueue(SqQueue& q)
{
	q = new Queue;
	q->front = q->rear = 0;
}

销毁队列

void DestroyQueue(SqQueue& q)
{
	delete q;
}

判断队列是否为空

若队列为空,返回真,否则返回假;
bool QueueEmpty(SqQueue q)
{
	return(q->front == q->rear);
}

进队列

bool enQueue(SqQueue& q, ElemType e)
{
	if ((q->rear + 1) % MaxSize == q->front)//队满
		return false;
	q->rear = (q->rear + 1) % MaxSize;
	q->data[q->rear] = e;
	return true;
}

出队列

bool deQueue(SqQueue& q, ElemType& e)
{
	if (q->front == q->rear)//队空
		return false;
	e = q->data[q->front];
	q->front = (q->front + 1) % MaxSize;
	return true;
}

1.2.4 队列c++模板

#include <queue>///头文件

q.push(x);//x插入队列的末端
q.pop();//弹出队列的第一个元素,物理删除,不返回该元素
q.front();//取队头元素
q.back();//取队尾元素
q.empty();//队列为空时,返回true
q.size();//访问队列中的元素个数

1.2.5 链队列

定义结构体

//单链表中数据节点类型QNode定义如下:
typedef struct qnode
{
	ElemType data;//数据元素
	struct qnode* next;
}QNode;
//链队中头尾指针类型LinkQueue定义如下:
typedef struct
{
	QNode* front;
	QNode* rear;
}LinkQueue;

初始化链队

构造一个空队,即创建一个链队结点,其front和rear域均置为NULL

Status InitQueue(LinkQueue& Q)
{
	Q.front = Q.rear = new QNode;
	if (!Q.front) exit(OVERFLOW);
	Q.front->next = NULL;
	return OK;
}

判断队列是否为空

若队列为空,返回真,否则返回假;

Status QueueEmpty(LinkQueue Q)
{
	return(Q.front == Q.rear);
}

求链队列的队头元素

Status GetHead(LinkQueue Q, ElemType& e)
{
	if (Q.front == Q.rear)//空队列
		return ERROR;
	e = Q.front->next->data;
	return OK;
}

链队列入队

Status EnQueue(LinkQueue& Q, ElemType e)
{
	p = new QNode;
	if (!p)exit(OVERFLOW);
	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
	return OK;
}

链队列出队

Status DeQueue(LinkQueue& Q, ElemType& e)
{
	if (Q.front == Q.rear)//空队列
		return ERROR;
	p = Q.front->next;
	e = p->data;
	Q.front->next = p->next;
	if (Q.rear == p) Q.rear = Q.front;//最后一个元素被删,改队尾
	delete p;
	return OK;
}

2.PTA实验作业

2.1 符号配对

#include<stdio.h>
int main()
{
	char s[200];
	int top = -1;
	char c, st[1000];//存储数据 
	char ch[1000];//用来和栈内符号匹配 
	ch[')'] = '(';
	ch[']'] = '[';
	ch['}'] = '{';
	int k = 0, flag = 0;
	do
	{//获取输入数据放入st数组 
		scanf("%c", &c);
		if (flag == 0 && c == '\n')
		{ 
			break;
		}
		st[k++] = c;
		if (c == '.')
		{
			flag = 0;
		}
		else
		{
			flag = 1;
		}

	} while (1);
	st[k] = '\0';
	for (int i = 0; i < k; i++)
	{
		if (st[i] == '/')
		{
			if (i < k&&st[i + 1] == '*')
			{
				s[++top] = '<';//用<代替/*	入栈
				i++;
			}

		}
		else if (st[i] == '(' || st[i] == '[' || st[i] == '{')
		{
			s[++top] = st[i];//入栈
		}
		else if (st[i] == '*')
		{
			if (i < k&&st[i + 1] == '/')
			{
				if (s[top] == '<')
				{ //出栈
					i++;
					top--;
				}
				else
				{
					printf("NO\n");
					if (top == -1)
					{ //缺失左括号
						printf("?-*/\n");
					}
					else
					{ //栈内符号缺失右括号
						c = s[top];
						printf("%c-?\n", c);
					}
					return 0;
				}
			}
		}
		else if (st[i] == ')' || st[i] == ']' || st[i] == '}')
		{
			if (s[top] == ch[st[i]])
			{ //出栈  用到了ch1数组 
				top--;
			}
			else
			{
				printf("NO\n");
				if (top == -1)
				{ //缺失左括号
					printf("?-%c\n", st[i]);
				}
				else
				{ //栈内符号缺失右括号
					c = s[top];
					if (c == '<')
					{
						printf("/*-?\n");
					}
					else
					{
						printf("%c-?\n", c);
					}
				}
				return 0;
			}
		}

	}

	if (top == -1)
	{//当栈为空时,打印yes 
		printf("YES\n");
	}
	else
	{//剩下的定是缺失右符号的 
		printf("NO\n");
		c = s[top];
		if (c == '<')
		{
			printf("/*-?\n");
		}
		else
		{
			printf("%c-?\n", c);
		}
	}

	return 0;
}

2.1.2 知识点总结

. 判断字符为左括号,右括号,以及符号匹配的条件
. 字符栈的应用

2.2 银行业务队列简单模拟

#include<stdio.h>
#include<queue>
#include<iostream>
using namespace std;
int main()
{
    int N; cin >> N;
    queue<int>que[2];

    for (int i = 0; i < N; i++)
    {
        int temp;cin >> temp;
        if (temp % 2 == 1)                //分成奇偶队列
            que[1].push(temp);
        else que[0].push(temp);
    }
    int flag = 0;                            //用来控制第一个输出的编号前面不带空格
    for (int time = 1;; time++)
    {
        if (que[0].empty() && que[1].empty())
            break;
        
        if (!que[1].empty())
        {
            if (flag==0)
            cout << que[1].front();
            else cout << " " << que[1].front();
            que[1].pop();flag = 1;
        }
        if (time % 2 == 0 && !que[0].empty())
        {
            if (flag==0)cout << que[0].front();
            else cout << " " << que[0].front();
            que[0].pop(); flag = 1;
        }
    }
    cout << endl;
    return 0;
}

2.2.2 知识点总结

. queue模板的应用

posted on 2021-04-10 22:15  长安&  阅读(380)  评论(0编辑  收藏  举报

导航