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

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

0.PTA得分截图

栈和队列题目集总得分,请截图,截图中必须有自己名字。题目至少完成2/3(不包括选择题),否则本次作业最高分5分。

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

1.1 栈

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

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

    操作函数

    • 结构体

      struct SNode {
          ElementType* Data;  /* 存储元素的数组   */
          Position Top;     /* 栈顶指针 */
          int MaxSize;
      };
      
    • 入栈

    void PushStack(Stack S, ElementType x)
    {
        if (S->Top == S->MaxSize)
        {
            printf("Stack Full\n");
            return;
        }
        S->Top++;
        S->Data[S->Top] = x;
    }
    
    • 出栈
    ElementType PopStack(Stack S)
    {
        if (S->Top == -1)
        {
            printf("Stack Empty\n");
            return 0;
        }
        return S->Data[S->Top--];
    }
    
    • 栈空
    int StackEmpty(Stack S)
    {
        if (S->Top == -1)
            return 1;
        else
            return 0;
    }
    
  • 链栈的结构、操作函数

    操作函数

    • 结构体定义:
    typedef int Elmetype;
    struct SNode
    {
    	Elmetype data;
    	struct SNode* next;
    };
    typedef struct SNode* LinkStack;
    
    • 进栈:
    void Push(LinkStack& S, Elmetype X)
    {
    	LinkStack NewS = new SNode;
    	/*数据载入*/
    	NewS->data = X;
    	/*头插法插入新数据*/
    	NewS->next = S->next;
    	S->next = NewS;
    }
    
    • 出栈:
    bool Pop(LinkStack& S, Elmetype& e)
    {
    	/*首先要判断栈顶是否为空*/
    	if (S->next == NULL)
    	{
    		return false;
    	}
    	e = S->next->data;
    	LinkStack delSNode = S->next;
    	S->next = delSNode->next;
    	delete delSNode;
    	return true;
    }
    
    • 取栈顶元素:
    void DestoryLinkStack(LinkStack& S)
    {
    	LinkStack delS = S;
    	while (S != NULL)
    	{
    		S = S->next;
    		delete delS;
    		delS = S;
    	}
    }
    

1.2 栈的应用

  • 表达式求值
  • 表达式转换
  • 符号的配对(创建一个栈,在读入字符的过程中,如果是左括号,则直接入栈,等待相匹配的同类右括号;如果是右括号,且与当前栈顶左括号匹配,则将栈顶左括号出栈)
  • 迷宫问题

1.3 队列

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

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头.

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

    • 结构体定义
    typedef struct
    {
        ElemType data[MaxSize];
        int front, rear;
    }SqQueue;
    typedef SqQueue* Queue;
    
    • 初始化队列
    void InitQueue(Queue Q)
    {
        Q = (Queue)malloc(sizeof(SqQueue)*Max);
        Q->front = Q->rear = 0;
    }
    
    • 销毁队列
    void DestroyQueue(Queue Q)
    {
        free(Q);
    }
    
    • 判断队列是否为空
    bool QueueEmpty(Queue Q)
    {
        return (Q->front == Q->rear);
    }
    
    • 入队
    bool EnQueue(Queue Q,ElemType e)
    {
        if(Q->rear == MaxSize)
            return false;
        Q->data[Q->rear++] = e;
        return true;
    }
    
    • 出队
    bool DeQueue(Queue Q,ElemType &e)
    {
        if(Q->front == Q->rear)
            return false;
        e = Q->data[Q->front++];
        return true;
    }
    
  • 环形队列的结构、操作函数

    • 结构体定义
    typedef struct
    {
        ElemType data[MaxSize];
        int front, rear;
    }SqQueue;
    typedef SqQueue* Queue;
    
    • 初始化队列
    void InitQueue(Queue Q)
    {
        Q = (Queue)malloc(sizeof(SqQueue)*Max);
        Q->front = Q->rear = 0;
    }
    
    • 销毁队列
    void DestroyQueue(Queue Q)
    {
        free(Q);
    }
    
    • 判断队列是否为空
    bool QueueEmpty(Queue Q)
    {
        return (Q->front == Q->rear);
    }
    
    • 入队
    bool EnQueue(Queue Q,ElemType e)
    {
        if((Q->rear +1) % MaxSize == Q->front)
            return false;
        Q->data[Q->rear] = e;
        Q->rear = (Q->rear + 1) % MaxSize;
        return true;
    }
    
    • 出队
    bool DeQueue(Queue Q,ElemType &e)
    {
        if(Q->front == Q->rear)
            return false;
        e = Q->data[Q->front];
        Q->front = (Q->front + 1) % MaxSize;
        return true;
    }
    
  • 链队列的结构、操作函数

    • 结构体定义
    typedef struct QNode    /* 声明链式队列的结点 */
    {
        int data;
        struct QNode *next;
    }Node;
    typedef struct QueuePoint    /* 声明链式队列的首尾指针 */
    {
        Node *front;
        Node *rear;
    };
    typedef QueuePoint* Queue;
    
    • 初始化队列
    Queue InitQueue (Queue Q)    
    {                        
        Q = (Queue)malloc(sizeof(QueuePoint));
        Q->front = Q->rear = NULL;
    
        return Q;
    }
    
    • 销毁队列
    void DestroyQueue(Queue Q)
    {
        Node* pre = Q->front,* p;
        if(pre != NULL)
        {
            p = pre->next;
            while(p != NULL)
            {
                free(pre);
                pre = p;p = p->next;
            }
            free(pre);
        }
        free(Q);
    }
    
    • 判断队列是否为空
    bool QueueEmpty(Queue Q)
    {
        return (Q->rear == NULL);
    }
    
    • 入队
    void EnQueue(Queue Q,ElemType e)
    {
        Node* p;
        p = (Node*)malloc(sizeof(Node));
        p->data = e;
        p->next = NULL;
        if(Q->rear == NULL)
            Q->front = Q->rear = p;
        else
        {
            Q->rear->next = p;
            Q->rear = p;
        }
    }
    
    • 出队
    bool DeQueue(Queue Q,ElemType &e)
    {
        Node* p;
        if(Q->rear == NULL)
            return false;
        p = Q->front;
        if(Q->front == Q->rear)
            Q->front = Q->rear = NULL;
        else
            Q->front = Q->front->next;
        e = p->data;
        free(p);
        return true;
    }
    
  • 队列应用,要有具体代码操作。

    舞伴问题

    int QueueLen(SqQueue Q){//队列长度
    return (Q->rear -Q->front + MAXQSIZE ) % MAXQSIZE;
    
    }
    int EnQueue(SqQueue &Q, Person e){//入队
     Q->rear = (Q->rear + 1) %MAXQSIZE;
      	Q->data[Q->rear] = e;
      	return 0;
    }
    int QueueEmpty(SqQueue &Q){//判空
        if(Q->front==Q->rear){
            return 1;
        }else{
            return 0;
        }
    }
    int DeQueue(SqQueue &Q, Person &e){//出队
    //就很神奇,把出队那个存到e中
        Q->front=(Q->front+1)%MAXQSIZE;    
        e=Q->data[Q->front];
        return 0;
    }
    void DancePartner(Person dancer[], int num){
       /*
       *函数作用:
       *1.将Person里存的人分到Mdancers, Fdancers两个队列
       *2.Mdancers, Fdancers一比一配队出列
       */
       //Mdancers, Fdancers
       for(int i=0;i<num;i++){
        if(dancer[i].sex=='M'){
            EnQueue(Mdancers,dancer[i]);
        }else{
            EnQueue(Fdancers,dancer[i]);
        }
       }
       while(QueueEmpty(Mdancers)!=1&&QueueEmpty(Fdancers)!=1){
        Person x,y;
        DeQueue(Mdancers, x);
        DeQueue(Fdancers, y);
        cout<<y.name<<"  "<<x.name<<endl;
       }
    }
    
    

2.PTA实验作业(4分)

此处请放置下面2题代码所在码云地址(markdown插入代码所在的链接)。如何上传VS代码到码云

2.1 符号配对

#include<iostream>
#include<string>
#include<stack>
using namespace std;
int main() {
	string s;
	char b[7];
	b[1] = '('; b[2] = ')'; b[3] = '['; b[4] = ']'; b[5] = '{'; b[6] = '}';//用来存储括号,省掉三分之二的代码量。如果你代码超过100行了,那很有可能是因为用了6个if而不是2个。
	int find = 0;
	bool isasimple = false;
	stack<char> bracket;//特地百度了一下括号的英文单词,就用他做栈名字吧。
	while (1) {
		getline(cin, s);
		if (s==".") goto zoulou;//当结束时,直接“zoulou”(走咯)
		for (int i = 0; i < s.length(); i++) {//*/是两个字符比较特殊,单独讨论
			if (s[i] == '/' && 1 + i < s.length())
				if (s[i + 1] == '*') {
					bracket.push(s[i]);//我们将两个字符以/的形式保存,这样方便
					i++;//因为这时候是两个字符
					continue;
				}
			if (s[i] == '*' && 1 + i < s.length())
				if (s[i + 1] == '/')
				{
					if (bracket.empty()) {
						cout << "NO" << endl << "?-*/" << endl;
						return 0;
					}
					else {
						if (bracket.top() != '/') {
							cout << "NO" << endl << bracket.top() << "-?" << endl;
							return 0;
						}
						else {
							bracket.pop();
							i++;//弹栈是弹一个,但是让栈弹的却是两个字符
							continue;
						}
					}
				}
			isasimple = false;
			for (find = 1; find <= 6; find++) {
				if (b[find] == s[i]) {//找一下是不是剩余的六个字符
					isasimple = true; break;
				}
			}
			if (isasimple) {
				if (find % 2 == 1) {
					bracket.push(s[i]);
					continue;
				}
				else {
					if (bracket.empty()) {
						cout << "NO" << endl << "?-" << b[find] << endl;
						return 0;
					}
					else {
						if (bracket.top() == '/') {
							cout << "NO" << endl <<"/*-?" << endl;
							return 0;
						}
						else if (bracket.top() != b[find - 1]) {
							cout << "NO" << endl << bracket.top() << "-?" << endl;
							return 0;
						}
						else {
							bracket.pop();
							continue;
						}
					}
				}
			}
		}
	}
zoulou:
	if (bracket.empty()) {//走咯之后还没完,如果栈不是空的,也还会出错。
		cout << "YES" << endl;
	}
	else {
		cout << "NO" << endl << bracket.top() << "-?" << endl;
	}
	return 0;
}

2.1.1 解题思路及伪代码

  • 当左边一堆左括号时,右边出现一个右括号,此右括号与离他最近的一个左括号匹配。 就是我们一直以右括号为标准,当右括号出现时,我们就要找他左边的括号。 所以我们要保存左括号,由于这个性质,我们可以用栈保存左括号。
  • 思路:我们一直把左括号压入栈中,当找到有个右括号时,我们把它和栈顶元素比较。
    但是比较的时候,要看栈是否为空。非空的话我们可以比较。如果为空,那我们就保存这个右符号,同时设置一个flag变量。
    最终还要看一看栈里面是不是空的,如果是空的,且flag也没问题,那我们输出yes
    如果栈为空,但是flag标记了,那我们输出那个右符号
    如果栈不是空的,输出栈顶即可。

2.1.2 总结解题所用的知识点

  • 顺序栈的结构

  • 顺序栈的出栈入栈操作

  • 递归函数的调用

  • 标准输入输出流的运用

  • 使用了hash表的知识,用hash表来存储会用到的符号。

  • 使用了STL库的string类。

  • 学会了利用string类的特点对多行元素进行有指定结束标志的读取。

2.2 银行业务队列简单模拟

#include <stdio.h>
int main() 
{ 
	int a[1010], b[1010];   //数组a,b用来存放A,B窗口的顾客 
	int ca = 0,cb = 0;     //ca,cb分别代表窗口A、窗口B的人数 
	
	int n; 	//n为顾客总数 	
	scanf("%d", &n); 
	for (int i = 1; i <= n; i++) 
	{ 
		int temp; //顾客编号 
		scanf("%d", &temp);
		
		//编号为偶数的去A窗口 ,否则去B窗口 
		if (temp % 2)  
			a[++ca] = temp; 
		else 
		b[++cb] = temp; 
	} 
	
	int f = 0;//控制空格的输出 
	int x = 1, y = 1; //x,y为a[],b[]下标,从1开始遍历(模拟队列,先进先出)
	 
	//A优先输出,A出两个,B出一个 
	while (x <= ca || y <= cb) 
	{ 
		if (x <= ca) 
		{ 
			if (f++) 
				printf(" "); 
			printf("%d", a[x++]); 
		} 
		if (x <= ca) 
		{ 
			if (f++) 
				printf(" "); 
			printf("%d", a[x++]); 
		} 
		if (y <= cb) 
		{ 
			if (f++) 
				printf(" "); 
			printf("%d", b[y++]); 
		} 
	} 
	return 0; 
	}

2.2.1 解题思路及伪代码

emm,很尴尬没有用队列和栈QwQ

2.2.2 总结解题所用的知识点

3.阅读代码(0--1分)

1份优秀代码,理解代码功能,并讲出你所选代码优点及可以学习地方。主要找以下类型代码:

注意:不能选教师布置在PTA的题目。完成内容如下。

3.1 题目及解题代码

可截图,或复制代码,需要用代码符号渲染。

#include<iostream>
#include <stack>
char s[4] = { 'q','a','b','c' };
std::stack<int> a[4];
bool move(int before, int after) {
	if (a[before].empty())
		return false;
	if (!a[after].empty())
		if ((a[after].top() - a[before].top()) < 0)
			return false;
	a[after].push(a[before].top());
	a[before].pop();
	printf("%c -> %c\n", s[before], s[after]);//faster than cout
	return true;
}
int main() {
	int  N, count = 0;
	std::cin >> N;
	for (int i = 0; i < N; i++)
		a[1].push(N - i);
	if (N % 2 == 1) {
		s[2] = 'c'; s[3] = 'b';
	}
	while (++count) {
		move((count - 1) % 3 + 1, (count) % 3 + 1);
		if (!move((count - 1) % 3 + 1, (count + 1) % 3 + 1)&&!move((count + 1) % 3 + 1, (count - 1) % 3 + 1))
				break;
	}
}

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

链表题目,请用图形方式展示解决方法。同时分析该题的算法时间复杂度和空间复杂度

所有的汉诺塔移动可以总结为重复的两步,我们假设现在最小的圆盘在a柱子上,柱子为a,b,c

第一步:将最小圆盘移动到下一个柱子上,也就是b

第二步:对a柱子和c柱子进行顶上最小的元素进行判断,把小一点的那个圆盘移动到大一点的那个圆盘(有空则摞在空柱子上)。

重复上述两步就可以得到答案。

注意:这样得到的最后的答案不一定是摞在c上,如果N是偶数将摞在b上,所以如果N是偶数我们就令第二个柱子为c,第三个柱子为b,这样就一定最后是摞在c上的。

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

真的有毒

posted @ 2021-04-11 15:05  2011王博  阅读(66)  评论(0编辑  收藏  举报