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

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

0.PTA得分截图

1.本周学习总结

1.1 栈

★栈中的数据元素遵守”后进先出(先进后出),时进时出”的原则,
栈顶与栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底(只能在栈顶进行插入和删除操作哦!)

顺序栈的结构、操作函数


顺序栈结构定义

#define MAXSIZE 10
struct StackNode {
    int data[MAXSIZE];
    int top;
};

顺序栈操作函数

// 创建空栈
StackNode* createStack() {
    StackNode* stack = (StackNode*)malloc(sizeof(StackNode));
    if (stack == NULL) {
        cout << "Memory allocate failed." << endl;
        return NULL;
    }
    for (int i = 0; i < MAXSIZE; i++) {
        stack->data[i] = 0;
    }
    stack->top = -1;
    return stack;
}
// 入栈 
void Push(StackNode* stack, int item) {
    if (stack == NULL) {
        cout << "The stack is not created." << endl;
        return;
    }
    if (stack->top == MAXSIZE - 1) {
        cout << "The stack is full." << endl;
        return;
    }
    else {
        stack->data[++(stack->top)] = item;
        return;
    }
}
//出栈,并返回出栈数据
int Pop(StackNode* stack) {
    if (stack == NULL) {
        cout << "The stack is not created." << endl;
        return 0;
    }
    if (stack->top == -1) {
        cout << "The stack is empty." << endl;
        return 0;
    }
    else {
        return (stack->data[(stack->top)--]);
    }
}
 // 获取栈元素个数
int getStackLength(StackNode* stack) {
    if (stack == NULL) {
        cout << "The stack is not created." << endl;
        return -1;
    }
    return (stack->top + 1);
}

链栈的结构、操作函数

★与顺序栈相比,链栈的结点空间可以动态申请,因此,不存在栈满上溢的情况。

链栈的基本操作包括:

  • 链栈的初始化操作。链栈的初始化操作就是把链栈初始化为空,设栈顶指针为top,初始化时,不带头结点top==NULL;带头结点top->next ==NULL;
  • 判断链栈是否为空。判断链栈是否为空,就是判断链栈的头结点指针域是否为空,即:top->next NULL;带头结点,不带头结点的链栈栈空条件为:topNULL;
  • 进栈操作。进栈操作就是将数据元素data插入到链栈的栈顶,就是将新结点插入到链表的第一个结点之前,将新结点插入到链表中分为3个步骤p->data = data; p->next = top->next; top->next = p;p为指向新结点的指针;
  • 出栈操作。出栈操作就是将栈的第一个结点删除,并将结点元素赋值给data,在元素出栈前要判断栈是是否为空;
  • 取栈顶元素。取栈顶元素就是把栈顶的元素取出,并返回。在去栈顶元素之前,同样要判断栈是否为空;
  • 求链栈的长度。求链表的长度就是返回链栈中的元素个数,从栈顶指针开始,通过指针域找到下一个结点,并使用变量计数,直到栈底为止;
  • 销毁链栈操作。链栈的空间是动态申请的,在程序结束时要把这些结点空间通过free函数释放;
  • 打印栈中元素。打印栈中元素即:将栈中元素输出。

链栈结构定义

typedef int DataType;

typedef struct Node
{
    DataType data;
    struct Node* next;
}LStackNode,*LinkStack;

链栈操作函数

//链栈的初始化

void InitStack(LinkStack* top)
{
    if ((*top = (LinkStack)malloc(sizeof(LStackNode))) == NULL)//为头结点开辟一个存储空间
    {
        exit(-1);
    }
    (*top)->next = NULL; //将链栈的头结点指针域置为空
}

//判断链栈是否为空
int StackEmpty(LinkStack top)
{
    if (top->next==NULL)       
    {
        return 1;
    }
    return 0;
}

//进栈操作

void PushStack(LinkStack top, DataType data)
{
    LStackNode*  p;
    p = (LStackNode*)(malloc(sizeof(LStackNode))); 
    if (p == NULL)
    {
        printf("内存分配失败!\n");
    }
    else
    {
        p->data = data;
        p->next = top->next;
        top->next = p;
    }
}

//出栈操作

void PopStack(LinkStack top,DataType* data)
{
    LStackNode* p;
    p = top->next;
    if (p==NULL)
    {
        printf("栈为空!\n");
    }
    else
    {
        top->next = p->next;
        *data = p->data;
        free(p);   //释放p指向的结点
    }
}

//取栈顶元素

int GetTop(LinkStack top, DataType *data)
{
    LStackNode* p;
    p = top->next;
    if (StackEmpty(top))
    {
        printf("栈为空!\n");
    }
    else
    {
        *data = p->data;
    }
    return *data;
}

//求表长操作

int StackLength(LinkStack top)
{
    int count = 0;
    LStackNode *p;
    p = top;
    while (p->next != NULL)
    {
        count++;
        p = p->next;
    }
    return count;
}

//销毁链栈
void DestoryStack(LinkStack top)
{
    LStackNode *p;
    LStackNode *q;
    p = top;
    while (!p)
    {
        q = p;
        p = p->next;
        free(q);
    }

}

//打印栈中元素
void StackPrint(LinkStack top)
{
    LStackNode* p;
    if (StackEmpty(top))
    {
        printf("栈为空!\n");
    }
    printf("栈中元素为:\n");
    p = top;
    while (p->next != NULL)
    {
        p = p->next;
        printf("%-3d", p->data);

    }

    printf("\n");
}

1.2 栈的应用

表达式

表达式求和:


#include <iostream>
#include <stack>
#include <stdio.h>
#include <string.h>
 
using namespace std;
 
//符号数组
char symbol[7] = {'+', '-', '*', '/', '(', ')', '#'};
//栈内元素的优先级
int in[7] = {3, 3, 5, 5, 1, 6, 0};
//栈外元素的优先级
int out[7] = {2, 2, 4, 4, 6, 1, 0};
 
///通过符号字符获取它的数组下标
int get(char c)
{
    switch(c)
    {
    case '+':
        return 0;
    case '-':
        return 1;
    case '*':
        return 2;
    case  '/':
        return 3;
    case '(':
        return 4;
    case ')':
        return 5;
    case '#':
        return 6;
    default:
        return 6;
    }
}
 
///比较栈内运算符c1和栈外运算符c2的优先级
char precede(char c1, char c2)
{
    int i1 = get(c1);
    int i2 = get(c2);
 
    if(in[i1] > out[i2])
    {
        return '>';
    }
    else if(in[i1] < out[i2])
    {
        return '<';
    }
    else
    {
        return '=';
    }
}
 
 
///计算基本表达式的值
int figure(int a, int theta, int b)
{
    switch(theta)
    {
    case 0:
        return a + b;
    case 1:
        return a - b;
    case 2:
        return a * b;
    default:
        return a / b;
    }
}
 
///计算表达式的值
int EvaluateExpression(const char *exp)
{
    stack<int> data; //数据栈
    stack<int> oper; //符号栈
 
    oper.push(get('#'));
    int sum = 0;
    int flag = 1; //表示正负号 1,表示正 0,表示负
    int a, theta, b;
 
    if(!('-' == *exp || '(' == *exp || isdigit(*exp)))
    {
        cout << "表达式出错1" << endl;
        return -1;
    }
 
    if('-' == *exp)
    {
        flag = 0;
        exp++;//指向下一个字符
    }
 
    int index = oper.top();
    while(*exp || symbol[index] != '#')
    {
        if(isdigit(*exp))
        {
            sum = 0;
            if(flag)
            {
                while(isdigit(*exp))
                {
                    sum = sum * 10 + *exp - '0';
                    exp++;
                }
            }
            else
            {
                while(isdigit(*exp))
                {
                    sum = sum * 10 - *exp - '0';
                    exp++;
                }
            }
            data.push(sum);
            flag = 1;
        }
        else
        {
            switch(precede(symbol[oper.top()], *exp))
            {
            case '>' :
                b = data.top();
                data.pop();
                a = data.top();
                data.pop();
                theta = oper.top();
                oper.pop();
                data.push(figure(a, theta, b));
                break;
            case '<' :
                oper.push(get(*exp));
                if(*exp)
                {
                    exp++;
                }
                break;
            case '=' :
                oper.pop();
                if(*exp)
                {
                    exp++;
                }
                break;
            }
        }
        index = oper.top();
    }
    return data.top();
}
 
int main()
{
    char s[105];
    ///只能算整形,浮点会错
    printf("请务必保证输入的表达式正确的,时间有限,本人未加表达式查重功能\n例如:(2+4)/2+(3-1)\n");
    scanf("%s",s);
    cout << EvaluateExpression(s) << endl;
    return 0;
}

1.3 队列

队列是一种先进先出的线性表,是一种常用的数据结构。
它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作

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

// 定义队列
typedef struct {
	int data[MaxSize];  //定义数组,存放队列元素
	int front, rear;  // 队头和对尾指针(存放数组下标)
}SeqQueue;
//初始化
void InitSeqQueue(SeqQueue& Q) {
	Q.front = Q.rear = 0;
}
//入队
void EnSeqQueue(SeqQueue& Q, int x) {
	if (Q.rear == MaxSize)
	{
		cout << "队列已满,不能入队";
	}
	else {
		// 先插入值到队尾,再将rear+1
		Q.data[Q.rear] = x;
		Q.rear++;
	}
}
//出队
void DeSeqQueue(SeqQueue& Q, int& x) {
	if (Q.front == Q.rear) {
		cout << "队列已空,不能出队";
	}
	else {
		// 先取出队头元素,再将front+1
		x = Q.data[Q.front];
		Q.front++;
	}
}

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

//初始化
typedef int Elemtype;
typedef struct
{
	Elemtype data[MAXN];
	int front, rear;
}Cseque;
void Init(Cseque &q)
{
	q.front = q.rear = 0;
}
int EnQueue(Cseque &q,Elemtype x)/* 进队操作, 返回1:队满 */
{
	if((q.rear+1)%MAXN==q.front)
	{
		cout << "队满" << endl; return 0;
	}
	else
	{
		q.rear = (q.rear + 1) % MAXN;
		q.data[q.rear] = x; 
		return 1;
	}
}
int DeQueue(Cseque &q, Elemtype *x)/* 出队操作 返回1:队空 */
{
	if(q.front==q.rear)
	{
		cout << "队空" << endl; return 0;
	}
	else
	{
		q.front = (q.front + 1) % MAXN;
		*x = q.data[q.front ];
		return 1;
	}
}
void OutputQueue(Cseque &q)	/* 输出队列中元素 */
{
	if (q.front==q.rear)  cout << "队空" << endl;
	int i = q.front + 1;
	while (i<=q.rear)
	{
		cout << q.data[i] << "    ";
		i++;
	}
	cout << endl;
}

链队列的结构、操作函数

typedef int Elemtype;
//数据结点
typedef struct node
{		/* 定义队列结构 */
	int data;		/* 队列元素类型为int */
	struct node *next;
}QNode;
//链队结点
typedef struct qptr
{
	QNode *front;
	QNode *rear;
}LQueue;
void EnQueue(LQueue *q,Elemtype x)	/* 进队操作 */
{
	QNode *p;
	p =(QNode*)malloc(sizeof(QNode));
	p->data = x;  p->next = NULL;
	q->rear->next = p;
	q->rear = p;

}
int EmptyQue(LQueue *q)//队列是否为空
{
	if (q->front == q->rear) return 0;
	else return 1;
}
int DeQueue(LQueue *q, Elemtype *x)	/* 出队操作 1:对空 */
{
	QNode *p;
	if(!EmptyQue(q))
	{
		cout << "队空" << endl; return 0;
	}
	else
	{
		p = q->front->next;
		q->front->next = p->next;
		*x = p->data; free(p);
		if (q->front->next == NULL)
			q->rear = q->front;     //队中只有一个元素的情况下,出队后修改队尾指针
		return 1;
	}
}

void OutputQueue(LQueue *q)		/* 输出队列中元素 */
{
	QNode *p;
	p = q->front->next;
	if (!EmptyQue(q))   cout << "队空";
	while (p)
	{
		cout << p->data<< "     ";
		p = p->next;
	}
	cout << endl;
}

void  GetBack(LQueue *q)//获取对尾元素
{
	cout <<"队尾元素为:"<< q->rear->data;
	cout << endl;
}
void  GetHead(LQueue *q)//获取对头元素
{
	cout << "队头元素为:" << q->front->next->data;
	cout << endl;
}

队列应用

舞伴问题

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->data[Q->rear] = e;
    //cout << e.name<<e.sex<<endl;
    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 符号配对

2.1.1 解题思路及伪代码

基本思路就是一个一个字符处理,发现一个要处理的左符号(/,[,(,{)就入栈,发现一个需要处理的右符号(/,],),})就与栈顶对比,如果是对应的左符号,那么把栈顶弹出,否则说明这个右符号不能配对,输出NO等信息并退出。如果一直处理到程序结尾都没有退出就是YES。

定义字符串 c 存放待匹配符号
定义字符栈 s 做匹配处理
定义 i,k 控制循环
将字符串输入并存入a,逐个判断a中的字符是否为
while(1){
        if 输入的是结束标志
            then 结束循环
        输入字符串a
        for(i=0;i<a.size();i++) 
            if a[i]是符号
                then 存入b
            else if a[i]是/*
                then 将<存入b且i+1
            else if a[i]是*/
                将>存入b且i+1
        end
定义 n=k;
for i=0 to nif b[i]等于[、{、(或<
        then 将b[i]入栈
    else if 栈不为空
        if 栈顶元素与此时的b[i]匹配
            then 将栈顶出栈
        else
            将b[i]入栈
    else
        将b[i]入栈
end for

if 栈空
    then输出YES
else{
    输出NO
    重置k=0
    while 栈不空
        将栈中元素逐个放入c中
        将c数组中的元素逐个比较
    for i=0 to k-1
for j=i+1 to kif 匹配
                then c[i]c[j]都置为0
    for i=0 to kif c[i]等于(、[、{
            then输出c[i]-?
            结束循环
        else if c[i]等于'<'
            then输出/*-?
            结束循环
        else if c[i]等于'>'
            then输出?-*/
            结束循环
        else if c[i]等于)、]、}
            then输出?-c[i]
            结束循环
end  

2.1.2 总结解题所用的知识点

字符栈的运用

2.2 银行业务队列简单模拟


2.2.1 解题思路及伪代码

for i=0 to n-1
    if 客户编号是奇数
       then 入A队
       else 入B队
end
for i=0 to n-1
      if A队非空
         then 取出2个A队元素并输出
      if B队非空
          then 取出1个B队元素并输出
end

2.2.2 总结解题所用的知识点

queue用法

3.阅读代码

3.1 题目及解题代码


class MyStack {
public:
    queue<int> queue1;
    queue<int> queue2;

    /** Initialize your data structure here. */
    MyStack() {

    }

    /** Push element x onto stack. */
    void push(int x) {
        queue2.push(x);
        while (!queue1.empty()) {
            queue2.push(queue1.front());
            queue1.pop();
        }
        swap(queue1, queue2);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int r = queue1.front();
        queue1.pop();
        return r;
    }
    
    /** Get the top element. */
    int top() {
        int r = queue1.front();
        return r;
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return queue1.empty();
    }
};

3.2 该题的设计思路

为了满足栈的特性,即最后入栈的元素最先出栈,在使用队列实现栈时,应满足队列前端的元素是最后入栈的元素。可以使用两个队列实现栈的操作,其中queue1
用于存储栈内的元素queue2作为入栈操作的辅助队列。
入栈操作时,首先将元素入队到queue2,然后将queue1的全部元素依次出队并入队到queue2,此时queue2的前端的元素即为新入栈的元素,再将queue1和queue2
互换,则queue1的元素即为栈内的元素,queue1的前端和后端分别对应栈顶和栈底。
由于每次入栈操作都确保queue1的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除queue1的前端元素并返回即可,获得栈顶元素操作只需要获得queue1的前端元素并返回即可(不移除元素)。
由于queue1用于存储栈内的元素,判断栈是否为空时,只需要判断queue1是否为空即可。

  • 时间复杂度:入栈操作 O(n),其余操作都是 O(1)。

入栈操作需要将queue1 中的 n 个元素出队,并入队 n+1 个元素到queue2 ,共有 2n+1次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是O(n)。
出栈操作对应将queue1的前端元素出队,时间复杂度是 O(1)。
获得栈顶元素操作对应获得queue1的前端元素,时间复杂度是O(1)。
判断栈是否为空操作只需要判断queue1是否为空,时间复杂度是 O(1)。

  • 空间复杂度:O(n),其中n是栈内的元素。需要使用两个队列存储栈内的元素。

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

1.题目较灵活,一个队列或者两个队列的方法都能做
2.使用两个队列的方法较复杂容易出错
3.题目涉及到栈和队列两种数据结构,需掌握好两种结构才能更好做题。
栈是一种后进先出的数据结构,元素从顶端入栈,然后从顶端出栈。
队列是一种先进先出的数据结构,元素从后端入队,然后从前端出队。

posted @ 2021-04-05 22:56  唐yuy  阅读(67)  评论(1编辑  收藏  举报
levels of contents