DS博客作业02--栈和队列

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

0.PTA得分截图

1.本周学习总结(0-5分)

1.1 栈

画一个栈的图形,介绍如下内容。

栈--又被称为后进先出的线性表(LIFO表,Last In First Out)

1.1.1顺序栈的结构、操作函数

声明顺序栈的类型SqStack

typedef struct{
  	ElemType data[MaxSize];
  	int top;
  }Stack;
typedef Stack *SqStack;

操作函数

(1)初始化栈initStack(&s)
void InitStack(&s)
{
    s=new Stack;
    s->top=-1;
}
(2)销毁栈ClearStack(&s)
void DestroyStack(SqStack &s)
{
    delete s;
}
(3)判断栈是否为空StackEmpty(s)
bool StackEmpty(s)
{
    return(s->top==-1)
}
(4)进栈Push(&s,e)
bool Push (SqStack &s,ElemtType e)
{
    if(s->top==MaxSize-1)       //顺序栈务必考虑栈满
    return false;
    s->top++;                   //栈顶指针增1
    s->Data[s->top]=e;
    return true;
}
(5)出栈Pop(&s,&e)
bool Pop(SqStack &s,ElemType &e)
{
    if(s->top==-1)    //栈为空,栈下益出
    return false';
    e=s->Data[s->top];//取栈顶指针元素
    s->top--;         //栈顶指针减一
    return true;
}
(6)取栈顶元素GetTop(s)
//在栈不为空的条件下,将栈顶元素赋给e
bool GetTop(SqStack *s,ElemType &e)
{
  if (s->top==-1)   //栈为空的情况
  return false;
  e=s->data[s->top];
  return true;
}
(7)建立栈CreateStack
Stack CreateStack(int MaxSize)
{
    Stack s=(Stack)malloc(sizeof(struct SNode));
    s->Data=(ElementType *)malloc (MaxSize*sizeof(ElementType));
    s->top=0;
    s->MaxSize = MaxSize;
    return s;
}

链栈的结构、操作函数

链栈的四要素

链栈中的数据节点的类型LiStack定义

typedef int ElemType;
typedef struct Linknode
{
    ElemType data;
    struct Linknode *next;
}LinkNode, *LiStack;

链栈的基本运算算法

(1)初始化栈initStack(&s)
void InitStack(LiStack &s)
{
    s=new LiNode;
    s->next=NULL;
}
(2)销毁栈ClearStack(&s)
void DestroyStack(LiStack &s)
{
    LiStack node;
    whilw(s!=NULL)
    {
      node=s;
      s=s->next;
      delete node;
     }
}
(3)判断栈是否为空StackEmpty(s)
bool StackEmpty(LiStack s)
{
    return (s->next==NULL);
}
(4)进栈Push(&s,e)
//该运算新建一个节点,用于存放元素e,将其插入到头节点之后作为新的首结点
void Push (LiStack &s)
{
    LiStack p;
    p=new LiNode;
    p->data=e;
    p->next=s->next;
    s->next=p;
}
(5)出栈Pop(&s,&e)
//在栈不为空的条件下,将头节点后继数据节点的数据域赋给e

bool 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;
    return true;
}
(6)取栈顶元素GetTop(s,e)
//在栈不为空的条件下,将头节点后继数据节点的数据域赋给e
bool GetTop(LiStack s,ElemType &e)
{
    if(s->next==NULL)         //栈空的情况
    return false;
    e=s->next->data;
    return true;
}

1.2 栈的应用

栈作为一种存放临时数据的容器。如果后存入的元素先处理,则采用栈。

表达式

1.2.1中缀表达式

(1)算术表达式中,运算符位于两个操作数中间的表达式称为中缀表达式,例如1+2*3。

(2)中缀表达式的运算一般遵循“先乘除,后加减,从左到右计算,先括号内,后括号外”的规则。

(3)中缀表达式依赖运算符优先级,还需处理括号。

1.2.2后缀表达式

(1)后缀表达式又称为逆波兰表达式,即为在算术表达式中运算符在操作数的后面,如1+2*3的后缀表达式为1 2 3 * +。

(2)后缀表达式在书写过程中已经考虑了运算符的优先级,没有括号,只有操作数和运算符,而且放在前面的运算符越优先执行。

1.2.3前缀表达式

如果运算符在操作数的前面,称为前缀表达式,如1+2*3的前缀表达式为+ 1 * 2 3。

1.2.4将算术表达式转换成后缀表达式

将一个中缀表达式转换成后缀表达式时,需要从左到有扫描算术表达式,将遇到的操作数直接存放到后缀表达式中,将遇到的每一个运算符或者左括号都暂时保存到运算符栈,而且先执行的运算符先出栈。

1.2.5后缀表达式求值

后缀表达式求值过程是从左到右扫描后缀表达式postexp,若读取到的是一个操作数,将它进操作数栈,若读取的是一个运算符op,从操作数栈中连续出栈两个操作数,当整个后缀表达式扫描结束时,操作数栈中的栈顶元素就是表达式的计算结果。

1.3 队列

画一个队列的图形,介绍如下内容。

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

顺序队结构

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

操作函数

1. 初始化队列
void InitQueue(SqQueue &q)
{
    q=new Queue;
    q->front=q->rear=-1;
}
2.销毁队列
void DestroyQueue(SqQueue &q)
{
    delete q;
}
3.判断队列是否为空
bool QueueEmpty(SqQueue q)
{    
    return (q->front==q->rear);
}
4.进队列
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;
}
5.出队列
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;
}

环形队列的结构、操作函数

环形队列 结构体
typedef struct
{
 ElemType data[MaxSize];
 int front,rear;
} Queue;
typedef Queue *SqQueue;

操作函数

进环形队列
bool enQueue(SqQueue &q,ElemType e)
{
    if((q->rear+1)%MaxSize==0)   //队满溢出
    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;
    q->front=(q->front+1)%MaxSize;
    e=q->data[q->front];
    return true;
}

链队列的结构、操作函数

结构
typedf struct qnode
{
 ElemType data;
 struct qnode *next;
} QNode;
typedef struct Queue *LiQueue;
LiQueue front,rear;
入队
Q.rear->next=node;
Q.rear=node;
出队
node =Q.font->next;
Q.font->next=node->next;
delete node;

队列应用--配对舞伴

int QueueLen(SqQueue Q)
{
    int len = 0;
    int i = Q->front;
    int j = Q->rear;
    while(i != j)
    {
        i = (i+1)%MAXQSIZE;
        len++;
    }
    return len;
}//队列长度

int EnQueue(SqQueue &Q, Person e)
{
    if((Q->rear+1)%MAXQSIZE == Q->front)
        return ERROR;

    Q->rear = (Q->rear+1)%MAXQSIZE;
    Q->data[((Q->rear))] = e;
    return OK;
}//加入队列
int QueueEmpty(SqQueue &Q)
{
    return (Q->front == Q->rear);
}//队列是否为空

int DeQueue(SqQueue &Q, Person &e)
{
    if(QueueEmpty(Q))
        return -1;
    Q->front = (Q->front+1)%MAXQSIZE;
    e = Q->data[Q->front];

    return OK;
}
//出队列
void DancePartner(Person dancer[], int num)
{
    int i;
    for(i = 0 ; i < num; ++i)
    {
        if(dancer[i].sex == 'F')
            EnQueue(Fdancers,dancer[i]);  //女生入队
        else
            EnQueue(Mdancers,dancer[i]);  //男生入队
    }
    Person e;
    while((!QueueEmpty(Fdancers)) && (!QueueEmpty(Mdancers)))
    {
      
        DeQueue(Fdancers,e);
        cout<<e.name<<"  ";
        DeQueue(Mdancers,e);
        cout<<e.name<<endl;
    }

} //配对舞伴

2.PTA实验作业

2.1 符号配对

2.1.1 解题思路及伪代码

解题思路

1.对于左符号直接进行入栈操作
2.读入右符号,判断情况:
/用>来代替;
/
用<来代替;
如果栈空,则缺少对应的左括号。
如果栈顶元素不匹配,缺少与栈顶元素对应的右括号
读入结束,如果栈空,则完全匹配,如果有剩余,则缺少右括号。

#include<stdio.h>
#define maxsize 1003
#include<stdlib.h>
typedef struct node *stack;
struct node
{
	char ch[maxsize];
	int top;
};
stack creat()
{
	stack s;
	s=(stack)malloc(sizeof(struct node));
	s->top=-1;
	return s;	
} 
void push(stack s,char cht)
{
	s->top++;
	s->ch[s->top]=cht;	
} 

int main()
{
	int i,k=0,j;
	static char ch1[1000],ch2[1000],ch[10000];
	struct node *s1=NULL;
	s1=creat();
	ch1['(']=')';
	ch1['{']='}';
	ch1['[']=']';
	
	ch1['<']='>';
	for(i=0;;i++)
	{
		gets(ch);
		if(ch[0]=='.'&&ch[1]=='\0') break;
		for(j=0;ch[j]!='\0';j++)
		{
			if(ch[j]=='('||ch[j]==')'||ch[j]=='['||ch[j]==']'||ch[j]=='{'||ch[j]=='}')
			{
				ch2[k++]=ch[j];
			}
			else if(ch[j]=='/'&&ch[j+1]=='*')
			{
				ch2[k++]='<';
				j++;
			}
			else if(ch[j]=='*'&&ch[j+1]=='/')
			{
				ch2[k++]='>';
				j++;
			}
		}
	}
	int flag=1;
	for(i=0;i<k;i++)
	{
		if(ch2[i]=='('||ch2[i]=='['||ch2[i]=='{'||ch2[i]=='<')
		{
			push(s1,ch2[i]);
		}
		else if(ch2[i]==')'||ch2[i]==']'||ch2[i]=='}'||ch2[i]=='>')
		{
			if(s1->top!=-1&&ch1[s1->ch[s1->top]]==ch2[i])
			{
				s1->top--;
			}
			else 
			{
				printf("NO\n");
				
				if(s1->top==-1)
				{
					if(ch2[i]==')') printf("?-)");
					else if(ch2[i]=='}') printf("?-}");
					else if(ch2[i]==']') printf("?-]");
					else if(ch2[i]=='>') printf("?-*/"); 
				}
				
				else if(ch1[s1->ch[s1->top]]!=ch2[i])
				{
					if(s1->ch[s1->top]=='(') printf("(-?");
					else if(s1->ch[s1->top]=='[') printf("[-?");
					else if(s1->ch[s1->top]=='{') printf("{-?");
					else if(s1->ch[s1->top]=='<') printf("/*-?");
				}
				flag=0;
				break;
			}
		}
	}
    
	if(flag==1&&s1->top==-1) printf("YES");
        else if(flag==1&&s1->top!=-1)
    {
        printf("NO\n");
        if(s1->ch[s1->top]=='(') printf("(-?");
		else if(s1->ch[s1->top]=='[') printf("[-?");
					else if(s1->ch[s1->top]=='{') printf("{-?");
					else if(s1->ch[s1->top]=='<') printf("/*-?");
    }
	return 0;
}
#include<stdio.h>
#define maxsize 1003
#include<stdlib.h>
创建队列

定义ch1[1000], ch2[1000]存放符号, ch[10000]判断符号的匹配
int flag=1
 for j from 0 to ch[j]!='\0'
   if ch[j]== 除/* */ 以外的符号
    then
     ch2[k++]=ch [j]
      j++
    else if ch[j]=='/' && ch [j+1]=='*'
      ch2[k++]='<'
      j++
    else if ch[j]=='*' && ch [j+1]=='/'
     ch2[k++]='>'
     j++

 end for

for i from 0 to k
  if   ch2[i] 为 左符号
   then  
    直接放入 栈中
  else if ch2[i]为右符号
   then
   if  栈中存在符号,且ch2中符号与栈符号对应
    then
     出栈
   else
    输出NO 
     判断缺少的符号 
     输出 ?-缺失符号
      flag =0 
       break
      end for

if flag==1 且栈空
   then 
    输出 yes
else if 栈不为空
  then
   输出NO
   判断栈中剩余符号,
   输出?-缺失符号

2.1.2 总结解题所用的知识点

使用malloc函数进行空间的动态申请
判断字符为左符号,右符号,对其进行相应的入出栈操作
使用替换的方式使判断更简单

2.2 银行业务队列简单模拟

2.2.1 解题思路及伪代码

解题思路

偶数顾客一次两个,奇数顾客一次输出一个。
那么则创建两个队列,一个存放A窗口的客户,另一个存放B窗口的客户,并分别统计两个窗口的人数。

  #include<iostream>
  #include<string>
  #include<queue>
  using namespace std;
 定义数组a[]存放顾客数字
 定义队列qa  //存放奇数
 定义队列qb  //存放偶数
 输入 N     
 for i from 0 to N-1 
   输入 a[i]
   do if (a[i]为奇数)
   进入qa
    else
    进入qb
    end for
 
 int x,y  存放队列头
if(qa队列长度大于qb队列长度的两倍)

  while(qa队列或qb队列不为空队列情况)

   if(qa不为空队列)
    then
     x = qa.front();
     输出x
     出队

     if(qa长度不为0)
      then
      输出 " "

     if(qa长度不为0)
      then
       输出 " "
    
    if(qb不为空队列)
     then
      y = qb.front();
      输出y
       出队
       输出" "
     
   
 else  //qa队列长度不大于qb队列长度两倍时

   while (qa队列或qb队列不为空队列情况)
     if(qa不为空队列)
      then
      x = qa.front();
      输出x
      出队
      输出" "
     
     x = qa.front();
      输出x
      出队
      输出" "
  
     if(qb不为空队列)
      then
       y = qb.front();
       输出y
       出队

     if(qb长度不为0)
          输出 " "

    
       return 0;

2.2.2 总结解题所用的知识点

c++库函数

出队列函数pop(),

判断队列是否为空队列函数empty()

使用队列头部进行判断

3.阅读代码----无法吃午餐的学生数量

来自于leetcode--队列

https://leetcode-cn.com/problems/number-of-students-unable-to-eat-lunch/

3.1 题目及解题代码

题目如下

解题代码

逛了一圈,发现大佬们的解法各不相同,这里选择了几种方法比较阅读

int countStudents(int* students, int studentsSize, int* sandwiches, int sandwichesSize){
    int arr[2];
    memset(arr, 0, sizeof(arr));
    for (int i=0; i<studentsSize; ++i) {
        ++arr[students[i]];
    }
    for (int i=0; i<sandwichesSize; ++i) {
        if (arr[sandwiches[i]] == 0) break;
        --arr[sandwiches[i]];
    }
    return arr[0] + arr[1];
}

作者:dong-feng-32
链接:https://leetcode-cn.com/problems/number-of-students-unable-to-eat-lunch/solution/c-zhi-xu-yao-pan-ding-xi-huan-zhan-ding-iffdu/
来源:力扣(LeetCode)
#include<stdlib.h>
#define MAX 102		
struct Queue
{
        int a[MAX];
        int front;		
        int rear;	
};
//队列的基本操作
void Init_Queue(struct Queue *q)
{
        q->front=q->rear=0;			
}

int Queue_Length(struct Queue *q)
{
        return (q->rear-q->front+MAX)%MAX;	
}

int Is_Empty(struct Queue *q)
{
        if(q->front == q->rear)		
                return 1;
        else
                return 0;
}

int Is_Full(struct Queue *q)
{
        if(q->front == (q->rear+1)%MAX)		
                return 1;
        else
                return 0;
}

int En_Queue(struct Queue *q,int x)
{
        if(Is_Full(q))				
            return 0;
        q->a[q->rear] = x;			
        q->rear = (q->rear+1)%MAX;
        return 1;
}

int De_Queue(struct Queue *q)
{
        if(Is_Empty(q))			
                return 0;		
        q->front = (q->front+1)%MAX;
        return 1;
}
//将队头元素移至队尾
int De_Queue1(struct Queue *q)
{
        if(Is_Empty(q))			
                return 0;		
        q->a[q->rear] = (q->a[q->front]);
        q->rear = (q->rear+1)%MAX;
        q->front = (q->front+1)%MAX;
        return 1;
}
//循环结束条件的判断
int judge(struct Queue *q,int *sandwiches,int j){
	int n,i;
	int count=0;
	n = Queue_Length(q);
    int n1 = q->front;
	for(i = 0;i < n;i++){
		if(q->a[n1] != sandwiches[j]){
			count++;
			n1 = (n1+1)%MAX;
		}
	}
	if(count == n)
	return 1;
	else 
	return 0;
}

int countStudents(int* students, int studentsSize, int* sandwiches, int sandwichesSize){
    int i,j=0;
    struct Queue *q = (struct Queue *)malloc(sizeof(struct Queue ));
    Init_Queue(q);
    for(i = 0;i < studentsSize;i++){
        En_Queue(q,students[i]);
    }

作者:fzst
链接:https://leetcode-cn.com/problems/number-of-students-unable-to-eat-lunch/solution/cyu-yan-shi-xian-4ms59mb-by-fzst-en7d/
来源:力扣(LeetCode)

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

第一版作者的解题思路很简单:若喜欢栈顶的甜点的学生存在,那么不管他们在队伍的哪个位置,必定会遍历到他。否则,一定无法继续拿掉栈顶甜点。

直接遍历,通过循环结构实现,不喜欢圆形和不喜欢方形三明治的被统计出。
这种解法的时间复杂度为o(n),相当简单。

void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 [1] 。
memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.

第二版作者用了比较标准且详细的队列解决,但相比第一版作者的方法代码量较大。不过两者思路个人感觉其实差距不大。

先将学生的喜好依次入队,然后将队列中的元素依次和三明治数组的头元素比较,如果相等,则让此学生出队,三明治数组元素后移一位。如果不相等,则将学生队列中的队头移至队尾,重复比较。
当学生队列中的每一个都不喜欢三明治数组里的头元素则循环结束,所以可定义一个用来判断循环能否结束的函数来控制循环次数。最后如果学生队列为空,则返回0,反之,则返回学生队列中的元素个数。
使用了多个函数封装处理,时间复杂度是o(n);

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

题目的解题条件明显,需要考虑的元素数量其实不多,就是两种三明治与人的对应,均是从栈顶考虑

难点在于不喜欢栈顶三明治的人回重新回到队列尾部,会进行多轮筛选。

posted @ 2021-04-05 21:14  落叶满长安  阅读(72)  评论(0编辑  收藏  举报