博客作业02--栈和队列

| 这个作业属于哪个班级 | 数据结构--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | 博客作业02--栈和队列 |
| 这个作业的目标 | 学习栈和队列的结构设计及运算操作 |
| 姓名 | 章审 |

0.PTA得分截图

1.本周学习总结

1.1栈

顺序栈的结构、操作函数

顺序栈的结构
栈中数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用顺序存储结构进行存储,即分配一块连续的存储空间来存放栈中元素,并用一个变量指向当前的栈顶元素以反映栈中元素的变化。采用顺序 视频讲解存储结构的栈称为顺序栈。

typedef struct
{
  ElemTyqe data[MaxSize];
  int top;
}

顺序栈的操作函数
1.初始化栈initStack(&s)
该运算创建一个空栈,由s指向它。实际上就是分配一个顺序栈空间,并将栈顶指针设置为-1。算法如下:

void InitStack(SqStack *&s)
{
  s=(SqStack *)malloc(sizeof(SqStack));
  s->top=-1;
}

2.销毁栈DestroyStack(&s)
该运算释放顺序栈s占用的储存空间。算法如下:

void DestroyStack(SqStack *&s)
{
  free(s);
}

3.判断栈是否为空StackEmpty(s)
该运算实际上用于判断条件s->top==-1是否成立。算法如下:

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

4.进栈Push(&S,e)
该运算新建一个结点,用于存放元素e(由p指向它),然后将其插入到头结点之后作为新的首结点。算法如下:

void Push(LinkStNode *&s,ElemType e)
{
  LinkStNode *p;
  p=(LinkStNode*)malloc(sizeof(LinkStNode));
  p->data=e;
  p->next=s->next;
  s->next=p;
}

5.出栈Pop(&s,&e)
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e,然后将其删除。

bool Pop(LinkStNode*&s,ElemType &e)
{
  LinkStNode *p;
  if(s->next==NULL)
    return false;
  p=s->next;
  e=p->data;
  s->next=p->next;
  free(p);
  return true;
}

6.取栈顶元素GetTop(s, &e)
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e。算法如下:

bool GetTop(LinkStNode *s, ElemType&e)
{
  if(s->next==NULL)
    return false;
  e=s->next->data;
  return true;
}

链栈的结构、操作函数

链栈的结构
栈中数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用链式存储结构。采用链式存储结构的栈称为链栈(linked stack),链表有多种,这里采用带头结点的单链表来实现链栈。
链栈中节点类型LinkStNode的声明如下:

typedef struct linknode
{
  Elem Type data;
  struct linknode *next;
} LinkStNode;

链栈的操作函数
1.初始化栈
该运算创建一个空链栈s。实际上是创建链栈的头结点,并将其next域置为NULL。算法如下:

void InitStack(LinkStNode*&s)
{
  s=(LinkStNode*)malloc(sizeof(LinkStNode));
  s->next=NULL;
}

2.销毁栈
该运算释放链栈s占用的全部结点空间,和单链表的销毁算法完全相同。算法如下:

viod DestroyStack(LinkStNode*&s)
{
  LinkStNode * pre=s,*p=s->next;
  while(p!=NULL)
  {
    free(pre);
    pre=p;
    p=pre->next;
  }
  free(pre);
}

3.判断栈是否为空
该运算判断s->next=NULL的条件是否为成立。算法如下:

bool StackEpty(LinkStNode *s)
{
  return(s->next==NULL);
}

4.进栈Push(&s,e)
该运算新建一个结点,用于存放元素e(由p指向它),然后将其插入到头结点之后作为新的首结点。算法如下:

void Push(LinkStNode *&s,ElemType e)
{
  LinkStNode *p;
  p=(LinkStNode*)malloc(sizeof(LinkStNode));
  p->data=e;
  p->next=s->next;
  s->next=p;
}

5.出栈Pop(&s,&e)
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e,然后将其删除。算法如下:

bool Pop(LinkStNode*&s,ElemType&e)
{
  LinkStNode*p;
  if(s->next==NULL)
    return false;
  p=s->next;
  e=p->data;
  s->next=p->next;
  free(p):
  return true;
}

6.取栈顶元素
该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e。算法如下:

bool GetTop(Link*s,ElemType&e)
{
  if(s->next==NULL)
    return false;
  e=s->nest->data;
  return true;
}

1.2栈的应用

表达式的转换
代码如下

#include <iostream>
#include <stack>
#include <string>
using namespace std;
int main()
{
	stack<char>st;
	char s[50];
	int i = 0, n = 0;
	char c[22];
	char e;
	cin >> c;
	while (c[i]!='\0')//进行遍历,开始转换成后缀表达式
	{
		switch (c[i])
		{
		case'('://判定为左括号
			st.push('(');//左括号进栈
			i++;//继续扫描其他字符
			break;
		case')'://判定为右括号
			e = st.top();//出栈元素e
			st.pop();
			while (e != '(')//不为‘(’时循环
			{
				s[n++] = e;//将e放入s中;
				s[n++] = ' ';
				e = st.top();//继续出栈元素e
				st.pop();
			}
			i++;//继续扫描其他字符
			break;
		case'+'://判定为加号或减号
		case'-':
			while (!st.empty())//栈不空循环
			{
				e = st.top();//取栈顶元素e
				if (e != '(')//e不是‘(’
				{
					s[n++] = e;//将e存放到s中
					s[n++] = ' ';
					e = st.top();//出栈元素e
					st.pop();
				}
				else//e是‘(’退出循环
					break;
			}
			st.push(c[i]);//将‘+’或‘-’进栈
			i++;//继续扫描其他字符
			break;
		case'*'://判定为‘*’或‘/’号
		case'/':
			while (!st.empty())栈不空循环
			{
				e = st.top();//取栈顶元素e
				if (e == '*' || e == '/')//将栈顶‘*’或‘/’运算符出栈并存放到s中
				{
					s[n++] = e;将e存放到s中
					s[n++] = ' ';
					e = st.top();//出栈元素e
					st.pop();
				}
				else//e为非‘*’或‘/’运算符时退出循环
					break;
			}
			st.push(c[i]);//将‘*’或‘/’进栈
			i++;//继续扫描其他字符
			break;
		default://处理数字字符
			while (c[i] >= '0' && c[i] <= '9')//判定为数字字符
			{
				s[n++] = c[i];
				i++;
			}
			s[n++] = ' ';
		}
	}
	while (!st.empty())//此时c扫描完毕,栈不空时循环
	{
		e = st.top();77出栈元素e
		st.pop();
		s[n++] = e;将e放到s中
		s[n++] = ' ';

	}
	s[--n] = '\0';//给s表达式添加结束标识
	cout << s;
}

只是简单的加减乘除,没有考虑到负数和小数。

1.3队列

顺序队列的结构、操作函数

顺序队列的结构
队列中数据元素的逻辑关系呈线性关系,所以队列可以像线性表一样采用顺序存储结构进行存储,即分配一块连续的存储空间来存放队列中的元素,并用两个整型变量来反映队列中元素的变化,它们分别存储队首元素和队尾元素的下标位置,分别称为队首指针(队头指针)和队尾指针。采用顺序存储结构的队列称为顺序队(sequential queue)

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

顺序队列的操作函数
1.初始化队列
构造一个空队列q,将front和rear指针均设置成初始状态,即一1值。算法如下:

void InitQueue(SqQueue*&q)
{
  q=(SqQueue*)malloc(sizeof(SqQueue));
  q->front=q->rear=-1;
}

2.销毁队列
释放队列q占用的存储空间。算法如下:

void DestroyQueue(SqQueue *&q)
{
  free(q);
}

3.判断队列是否为空
若队列q为空,返回真;否则返回假。算法如下:

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

4.进队列
在队列q不满的条件下先将队尾指针rear增1,然后将元素e插人到该位置。算法如下:

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

5.出队列
在队列q不空的条件下先将队头指针front增1,并将该位置的元素值赋给e。算法如下:

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

环形队列的结构、操作函数
环形队列的结构
在环形队列q中设置队空条件是q->rear=q->front;队满条件是(q->rear 1) MaxSize=q-> front。而进队操作和出队操作改为分别将队尾rear和队头指针front循环进1.

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

环形队列的操作函数
1.初始化队列
构造一个空队列q,将front和rear指针均设置成初始状态,即0值。算法如下:

void InitQueue(SqQueue*&q)
{
  q=(SqQueue*)malloc(sizeof(SqQueue));
  q->front=q->rear=0;
}

2.销毁队列
释放队列q占用的存储空间。算法如下:

void DestroyQueue(SqQueue *&q)
{
  free(q);
}

若队列q为空,返回真;否则返回假。算法如下:

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

3.判断队列是否为空
若队列q为空,返回真;否则返回假。算法如下:

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

4.进队列
在队列q不满的条件下先将队尾指针rear增1,然后将元素e插人到该位置。算法如下:

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;
}

5.出队列
在队列q不空的条件下先将队头指针front增1,并将该位置的元素值赋给e。算法如下:

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

链队列的结构、操作函数
链队列的结构

1.初始化队列
构造一个空队,即创建一个链队节点,其front和rear域均置为NULL.算法如下:

void InitQueue(LinkQuNode*&q)
{
  q=(LinkQuNode*)malloc(sizeof(LinkQuNode));
  q->front=q->rear=NULL;
}

2.销毁队列
释放链队占用的全部存储空间,包括链队结点和所有数据结点的存储空间。算法如下:

void DestroyQueue(LinkQuNode)
{
  DataNode *pre=q->front,*p;
  if(pre!=NULL)
  {
    p=pre->next;
    while(p!=NULL)
    {
      free(pre);
      pre-p;
      p=p->next;
    }
    free(pre);
  }
  free(q)
}

3.判断队列是否为空
若队列为空,返回真;否则返回假。算法如下:

bool QueueEmpty(LinkQuNode*q)
{
  return(q->rear==NULL);
}

4.进队列
创建一个新结点用于存放元素e(由p指向它)。若原队列为空,则将链队结点的两个域均咨向结点p,否则将结点p链接到单链表的末尾,并让链队结点的rear域指向它。算法如下:

void enQueue(LinkQuNode*&q,ElemType e)
{
  DataNode *p;
  p=(DataNode*)malloc(sizeof(DataNode));
  p->data=e;
  p->next=NULL;
  if(q->rear==NULL)
    q->front=q->rear=p;
  else
  {
    q->rear->next=p;
    q->rear=p;
  }
}

5.出队列
若原队列为空,则下溢出返回假;若原队列不空,则将首结点的data域值赋给e,并副除之,若原队列只有一个结点,则需将链队结点的两个域均置为NULL,表示队列已为空。

bool deQueue(LinkQuNode*&q,ElemType&e)
{
  DataNode *t;
  if(q->rear==NULL)
    return false;
  t=q->front;
  if(q->front==q->rear)
    q->front==q->rear==NULL;
  else
    q->front=q->front->next;
  e=t->data;
  free(t);
  return true;
}

队列应用
pta 6.3
代码如下

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->rear + 1) % MAXQSIZE;//队尾加一
    Q->data[Q->rear] = e;//将数据写入队尾
    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符号配对

https://gitee.com/z202021123020/zs777/commit/02627105f630054e8ba96e055995fe43a247b0c1

2.1.1解题思路及伪代码

#include <iostream>
#include <stack>
#include <string>
using namespace std;

int main()
{
	stack <char> str;//初始化
	int i, length, flag = 0;
	static char A[1000];
	char ch;
	i = 0;
	scanf("%c", &A[i]);
	while (!(i >= 1 && A[i] == '\n'&&A[i - 1] == '.'))//遍历整个输入来计算长度
	{
		i++;
		scanf("%c", &A[i]);
	}
	length = i - 1;
	i = 0;
	while (i < length)//开始一一判断
	{
		switch (A[i])//对左符号进行判断
		{
		case '/':
			if (i + 1 < length&&A[i + 1] == '*')//将整个/*入栈
			
		case'('://将‘(’入栈
			
		case'['://将‘【’入栈
			
		case '{'://将‘{’入栈
			
		case'*':
			if (i + 1 < length&&A[i + 1] == '/')//对*/进行判断
			{
				if (str.empty())//对?-*/判断 如果是flag=2

				ch=str.top;

				if (ch == '*')//如果匹配就出栈
				
				else//不匹配返回no
						
			break;
		case ')'://对‘)’进行判断
			if (str.empty())//对?-)判断
		
			ch = str.top();
			if (ch == '(')//如果匹配就出栈
		
			else//不匹配返回no
			
			break;
		case '}'://对‘}’进行判断
			if (str.empty())//对?-}判断
			
			ch = str.top();
			if (ch == '{')//如果匹配就出栈
		
			else//不匹配返回no
			
			break;
		case ']'://对‘】’进行判断
			if (str.empty())//对?-】判断
			
			ch = str.top();
			if (ch == '[')//如果匹配就出栈
	
			else//不匹配返回no
			
			break;
		default://其他则跳过
			break;
		}
		if (flag == 2)
		{
			printf("NO\n");
			printf("?-*/");
			break;
		}
		else if (flag == 1)
		{
			printf("NO\n");
			if (str.empty())
			{
					printf("?-%c", A[i]);
			}
			else
			{
				if (ch == '*')
				{
					printf("/*-?");
				}
				else
				{
					printf("%c-?", ch);
				}
			}
			break;
		}
		i++;
	}
	if (flag == 0 && !str.empty())
	{
		printf("NO\n");
		ch = str.top();
		if (ch == '*')
		{
			printf("/*-?");
		}
		else
		{
			printf("%c-?",ch);
		}
	}
	else if (flag == 0)
	{
		printf("YES\n");
	}
}

2.1.2 总结解题所用的知识点

学会运用stack容器,不用单独再自己建一个栈的函数;

2.2 银行业务队列简单模拟

https://gitee.com/z202021123020/zs777/blob/master/7-6 银行业务队列简单模拟

2.2.1 解题思路及伪代码

#include <iostream>
#include <queue>
using namespace std;
int main()
{
	int i, n, k;
	cin >> k;//输入人数
	queue<int> q1;
	queue<int> q2;//创建队列
	for (i = 0; i < k; i++)//输入编号
	{
		cin >> n;
		if (n % 2)//入奇数的栈
		
		else//入偶数的栈
		
	}
	for (i = 0; i < k; i++)
	{
		if (i == 0)//先对第一个数据输出处理空格
		
		if ((i + 1) % 3)//开始对a窗口处理
		{
			if (!q1.empty())//判断是否处理完毕,未处理完毕打出a窗口的编号
			
			else//处理完毕直接打出b窗口的所有编号
			
		}
		else//对b窗口处理
		{
			if (!q2.empty())//判断是否处理完毕,未处理完毕打出b窗口的编号
			
			else//处理完毕直接打出b窗口的所有编号
			
		}
	}
	return 0;
}

2.2.2 总结解题所用的知识点
学会运用queue容器,不用单独再自己建一个队列的函数;

3.阅读代码

3.1 题目及解题代码

class MyQueue {
private:
    stack<int> inStack, outStack;

    void in2out() {
        while (!inStack.empty()) {
            outStack.push(inStack.top());
            inStack.pop();
        }
    }

public:
    MyQueue() {}

    void push(int x) {
        inStack.push(x);
    }

    int pop() {
        if (outStack.empty()) {
            in2out();
        }
        int x = outStack.top();
        outStack.pop();
        return x;
    }

    int peek() {
        if (outStack.empty()) {
            in2out();
        }
        return outStack.top();
    }

    bool empty() {
        return inStack.empty() && outStack.empty();
    }
};

3.2 该题的设计思路及伪代码

将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop 和 peek 操作。
每次 pop 或 peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

复杂度分析
时间复杂度:push 和 empty 为 O(1),pop 和 peek 为均摊 O(1)。对于每个元素,至多入栈和出栈各两次,故均摊复杂度为O(1)。
空间复杂度:O(n)。其中 n 是操作总数。对于有 n 次 push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n)。

3.3 分析该题目解题优势及难点。

利用栈实现队列的功能。
难点:如何实现利用两个栈来实现数据的输入与输出。

posted @ 2021-04-05 20:46  51456  阅读(182)  评论(1编辑  收藏  举报