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

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

0.PTA得分截图

1.本周学习总结

1.1栈

  • 顺序栈(存储的空间是顺序的)

栈只能在一端进行插入和删除,设置顺序栈的的结构体时,用一个top来表示用来删除和插入的栈顶。顺序栈就像一个特殊的数组,只对top表示下标的栈元素改动。

typedef struct {
	elementType data[Max];//存放数据
	int top;//用来插入删除
	int MaxSize;//堆栈最大容量
}SqStack;

top=-1;//栈空条件
top=MaxSize-1;//栈满条件
top++;st->data[st->top]=e;//进栈操作
e=st->data[st->top];top--;//退栈操作

  • 初始化栈
void InitStack(SqStack& s)
{
	s = new Stack;
	s->top = -1;
}
  • 销毁栈
void DestroyStack(SqStack& s)
{
	delete s;
}
  • 进栈
bool Push(SqStack& s, ElementType e)
{
	if (s->top == MaxSize - 1)//如果栈为满的时候
		return false;//返回错误
	s->top++;
	s->data[s->top] = e;
	return true;
}
  • 出栈
bool Pop(SqStack& s, ElementType& e)
{
	if (s->top == -1)//栈为空的时候,返回false
		return false;
	e = s->data[s->top];//取栈顶元素
	s->top--;//栈顶指针-1
	return true;
}
  • 取栈顶元素
bool GetTop(SqStack* s, ElementType& e)
{
	if (s->top == -1)//栈为空的时候,返回false
		return false;
	e = s->data[s->top];//取元素,并不移动指针
	return true;
}
  • 链栈(采用链式存储数据,空间分配不是顺序的)
    数据定义
typedef struct linkknode
{
	ElementType data;//数据域
	struct linknode* next;//指针域
}LiNode, * LiStack;

链栈的操作函数

  • 初始化栈
void InitStack(SqStack& s)
{
	s = new Stack;
	s->next = NULL;
}
  • 销毁栈

void DestroyStack(SqStack& s)
{
	LiStack node;
	while (s != NULL)
	{
		node = s;
		s = s->next;
		delete node;
	}
}
  • 判断栈是否为空
bool StackEmpty(LiStack s)
{
	return (s->next == NULL);
}
  • 进栈//头插法来插入
bool Push(SqStack& s, ElementType e)
{
	LiStack p;
	p = new LiNode;//申请新的节点
	p->data = e;//赋值给它
	p->next = s->next;//插入p节点
	s->next = p;
}
  • 出栈
bool Pop(SqStack& s, ElementType& e)
{
	LiStack p;
	if (s->next == NULL)//栈为空的时候,返回false
		return false;
	p = s->next;
	e = p->data;
	s->next = p->next;//删除p节点
	delete p;//释放p节点
	return true;
}
  • 取栈顶元素
bool GetTop(SqStack* s, ElementType& e)
{
	if (s->next == NULL)//栈为空的时候,返回false
		return false;
	e = s->next->data;//取元素,并不移动指针
	return true;
}

1.2栈的应用

之前做小学生口算系统时,做有优先级的运算时,不好做处理。现在学习了栈,可以运用栈的先进后出的特点来实现优先级运算。

中缀表达式 a+b*c+(d*e+f)*g
转为后缀   abc*+de*f+g*+

遍历中缀表达式
if 算子直接->postexp[]
else 运算符
switch (运算符)
{
case'(':进数组postexp[]

case')':Pop()出栈e
	while (直到出栈的e为')')
	{
		e->postexp;
		Pop(),出栈e
	}
case'+':
case'-':
	while (直到栈为空)
	{
		GetTop//取栈顶
			if (栈顶e! = '(')
			{
				出栈;
					进数组postexp;
			}
			else
				break;
	}

	进栈我们现在遍历到的运算符

case'*':
case'/':
	while (直到栈为空)
	{
		GetTop//取栈顶
			if (栈顶为'+' || '-')
				break;
			if (栈顶e! = '*'||'/')
			{
				出栈;
					进数组postexp;
			}
			else
				break;
	}
	进栈我们现在遍历到的运算符


default:
	while (*exp旧数组为数字)
	{
		进新数组;
			exp++;
	}
  }

while (栈不为空时)
{
	出栈;
	进新数组postexp;
}
postexp[i] = 0;//结束标志

后缀表达式求值
遍历后缀表达式
遇见数字入栈
遇见操作符时一次出栈操作数 b,a 做运算 a op b ,结果入栈
遍历完成后栈内的那个元素即是结果

1.3队列


队列和栈的不同就是队列两个端口都能用,不过一般队头出队,队尾进队。
这样队列的特点就是先进先出。队列的结构体设计,有两个指针,一个用来表示队头一个用来表示队尾。

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

front=rear;//队空的条件
rear=MaxSize-1;//队满条件
rear++;data[rear]=e;//e进队
front++;e=data[front];//e出队

  • 初始化队列
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; 
}

使用队列的时候,可能因为front前出队留下的空位,导致rear=MaxSize-1作为队满条件不满足,出现假溢出。
这时候我们逻辑上让这个队列前后连接起来,形成一个环状的顺序表,称为环形队列或循环队列。

环形队列结构的结构体设计和不同的队列没有什么不同

typedef struct
{
    ElemType data[MaxSize];
    int front, rear;//队头和队尾指针
}Queue;
  • 初始化环形队列
void InitQueue(SqQueue& q)
{
    q->new Queue;
    q->front = q->rear = 0;//设置在最初的一位数上
}
  • 销毁环形队列
void DestroyQueue(SqQueue& q)
{
    delete q;
}
  • 判断环形队列是否为空
void 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->rear == q->front)//队空
        return false;
    e = q->data[q->front];
    q->front = (q->front + 1) % MaxSize;
    return true;
}

链式队列存储

队列用链式存储时,设立两个指针来指向队头和队尾。但这两个指针里面没有存放数据。

  • 链队列结构体设计
typedef struct qnode {
    ElemType data;
    struct qnode* next;
}QNode;

//链队列中头指针为指针类型
typedef struct
{
    QNode* front;
    QNode* rear;
}LinkQueue;
  • 初始化链队
//这个是带头节点的链队
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符号配对

2.1.1伪代码

将数据传入字符型数组st

for (直到st的结尾)
{
	if (st为"/*")
	{
		进s[];//用<代替/*
	}
	else if (st == '(' || '[' || '{')
	{
		进s[];//进栈
	}
	else if (st为 * / )
	{
		if (栈顶为 < )
		{
			Pop()//出栈
		}
		else
		{
			printf("NO\n");
			if (top - 1)//栈内无元素
				printf("?-*/\n");//缺失左符号
			else
				printf("%c-?\n", c);//缺失右符号
			结束函数//return 0
		}
	}
	else if (st == ')' || ']' || '}')
	{
		if[s的栈顶是三个之中的一个配对符号]
		{
			出栈
		}
		else
			if (top == -1栈内无元素)
				printf("?-%c\n", st[i]);
			else
				printf("%c-?\n", c);//缺失右符号
		< 要单独处理

			结束函数
	}
}
	if(top == -1)//在经过上面的处理后,栈内为空
	{
	输出:YES;
    }
	else
	{
		输出:NO
			输出:栈内第一个 - ? ;
     }

2.1.1解题思路

遍历数组
if算子
进新的数组
else
判断与前一位的优先级
前一位高则出栈,进新数组直到比现在这位低的优先级,然后进栈本位
前一位没有比本位高,进栈本位

2.1.2 总结解题所用知识点

  • 1.char ch[1000];//用来和栈内符号匹配 比较灵活
    ch[')'] = '(';
    ch[']'] = '[';
    ch['}'] = '{';
    用空间来换时间,设立一个字符型数组,用它们的ASCII码做下标,之后我们
    就可以更灵活的来找匹配的符号。像:
    s[top] == ch[st[i]]
    我们要找')'的时候就能用ch[')']来表示我们要找的'('

  • 2.题目中有'/'和'/',这两个都是两个字符,我们判断出后可以使用'<'和'>'来代替入栈,这样后期符号匹配比较好找

2.2银行业务队列简单模拟

2.2.1伪代码

for (i < max; i++)
{
	奇数进 odd栈
		偶数进 even栈
}
while (i < max)
	{
	if (flag < 2)
	{
	if (odd不为空)
	{
	输出e
	flag++;
	}
	}
	else
	{
	even出栈
	输出even出栈的元素
	flag = 0;
	}
	if (i != max - 1)//还没到最后一位数
				cout << " ";//数字后输出空格
	if (odd为空,even不为空)
	(
	将odd剩下的按格式输出
		}
else if(even为空,odd不为空)
	{
		将even剩下的按格式输出
	}

2.2.1解题思路

分两个队列odd和even,分别为奇数偶数
每输出两个奇数,就输出一个偶数
if其中一个为空的时候
将剩下的输出

3.阅读代码

3.1题目及解题代码


3.2该题的设计思路

先按照身高排队

再根据前面人数再排
遍历(从高到矮)
如果k=0,放到第一个,
k有数字就判断已经进去的人,来判断本次放在哪

3.3难点

  • 有两个判断顺序的标准,先用一个标准拍好序,再根据第二个标准做第二次调整

3.3 O(n)

因为遍历一遍后还要再判断本次位置,O(n^2)
空间复杂度:O(n),即为排序需要使用的栈空间

posted @ 2021-04-05 22:50  兴亡  阅读(74)  评论(2编辑  收藏  举报