数据结构-第三章 栈、队列和数组-笔记

目录

自定义的栈和队列的实现

栈的顺序存储

队列的顺序存储

队列的链式存储

手写代码版的简易栈

手写代码版的简易队列 

栈在括号匹配中的应用

栈在表达式求值中的应用

3.3 栈和队列的应用-课后习题

2、车厢调度

3、利用栈实现该递归函数的非递归计算


自定义的栈和队列的实现

栈的顺序存储

#define maxsize 10
struct sta
{
    int data[maxsize];
    int top;    //top指向栈顶元素
};
//栈的初始化
void InitStack(sta& s)
{
    s.top = -1;
}
//判断栈是否为空
bool StackEmpty(sta& s)
{
    if (s.top == -1) return true;
    return false;
}
//进栈
bool Push(sta& s, int x)
{
    if (s.top == maxsize - 1) return false;
    s.top++;
    s.data[s.top] = x;
    return true;
}
//出栈
bool pop(sta& s, int& x)
{
    if (s.top == -1) return false;
    x = s.data[s.top];
    s.top--;
    return true;
}
//读栈顶元素
bool GetTop(sta& s, int& x)
{
    if (s.top == -1) return false;
    x = s.data[s.top];
    return true;
}

队列的顺序存储

#define maxsize 10
struct que
{
    int data[maxsize];
    int front;  //front指向队首元素
    int rear;   //rear指向队尾元素的下一个位置
};
//队列的初始化
void InitQueue(que& q)
{
    q.front = 0;   
    q.rear = 0;
}
//判断队列是否为空
bool QueueEmpty(que& q)
{
    if (q.front == q.rear) return true;
    return false;
}
//进队
bool EnQueue(que& q, int x)
{
    //判断是否队满
    if ((q.rear + 1) % maxsize == q.front) return false;
    q.data[q.rear] = x;
    q.rear = (q.rear + 1) % maxsize;
    return true;
}
//出队
bool DeQueue(que& q, int& x)
{
    //判断是否队空
    if (q.rear == q.front) return false;
    x = q.data[q.front];
    q.front = (q.front + 1) % maxsize;
    return true;
}
//读队首元素元素
bool GetHead(que& q, int& x)
{
    //判断是否队空
    if (q.rear == q.front) return false;
    x = q.data[q.front];
    return true;
}

这种情况下front指向队首元素,rear指向队尾元素的下一个位置,队列中必须牺牲一个单元来区分队空和队满两种情况。

该种情况下队列长度为:(q.rear + maxsize - q.front) % maxsize

为了解决牺牲一个队列单元的问题,可以在类型中使用一个数据成员size来表示队列中的元素个数;或者也可以在类型中增加一个数据成员tag,为0表示最近的一次操作是删除,为1表示最近的一次操作是插入,这样当q.front == q.rear时,如果tag为0则表示队空,为1则表示队满。

队列的链式存储

#define maxsize 10
//链式队列的结点
struct quenode
{
    int data;
    quenode* next;
};
//带头结点的链式队列
struct quelink
{
    quenode* front,* rear;
};

//队列的初始化
void InitQueue(quelink* q)
{
    q->front = q->rear = new quenode;
    q->front->next = nullptr;
}
//判断队列是否为空
bool QueueEmpty(quelink* q)
{
    //也可以用q->front == nullptr来判断队空
    if (q->front == q->rear) return true;
    return false;
}
//进队
void EnQueue(quelink* q, int x)
{
    //链式队列不用判断是否队满
    quenode* cur = new quenode;
    cur->data = x;
    cur->next = nullptr;
    q->rear->next = cur;
    q->rear = cur;
}
//出队
bool DeQueue(quelink* q, int& x)
{
    //判断是否队空
    if (q->front == q->rear) return false;
    quenode* temp = q->front->next;
    x = temp->data;
    q->front->next = temp->next;
    //如果队列中只有一个结点,令尾节点重新指向头结点
    if (temp == q->rear)
    {
        q->rear = q->front;
    }
    delete temp;
    return true;
}
//读队首元素元素
bool GetHead(quelink* q, int& x)
{
    //判断是否队空
    if (q->front == q->rear) return false;
    quenode* temp = q->front->next;
    x = temp->data;
    return true;
}

手写代码版的简易栈

#define maxsize 100
int main()
{
    int sta[maxsize];   //栈
    int top = -1;       //栈顶指针

    //入栈
    sta[++top] = 1;
    //出栈
    top--;
    //读栈顶元素
    int x = sta[top];
    //读栈顶元素并出栈
    int x = sta[top--];
    //判断是否栈空
    if (top == -1);

    return 0;
}

手写代码版的简易队列 

#define maxsize 100
int main()
{
    int que[maxsize];           //队
    int front = 0, rear = 0;    //队首指针和队尾指针

    //入队
    que[rear++] = 1;
    rear %= maxsize;
    //出队
    front = (front + 1) % maxsize;
    //读队首元素
    int x = que[front];
    //读队首元素并出队
    int x = que[front++];
    front %= maxsize;
    //判断是否队空
    if (front == rear);
    //计算队列的长度
    int len = (rear + maxsize - front) % maxsize;

    return 0;
}

栈在括号匹配中的应用

bool bracketcheck(string str)
{
	sta s;
	InitStack(s);
	for (int i = 0; i < str.size(); i++)
	{
		if (str[i] == '(' || str[i] == '[' || str[i] == '{')
		{
			Push(s, str[i]);
		}
		else
		{
			//如果栈为空,说明当前右括号没有匹配的左括号
			if (StackEmpty(s)) return false;
			char x;
			pop(s, x);	//弹出栈顶元素
			//判断括号是否匹配
			if (str[i] == ')' && x != '(') return false;
			else if (str[i] == ']' && x != '[') return false;
			else if (str[i] == '}' && x != '{') return false;
		}
	}
	//最后判断栈是否为空,若不为空说明有左括号未匹配
	return StackEmpty(s);
}

栈在表达式求值中的应用

【注】

②:根据这条性质的说明,栈中的括号只可能含有左括号“(”。

另外,在括号中遇到多个运算符时,也要参照③中的规则。

③:若栈中含有优先级高于等于当前运算符的其他运算符,那么这些运算符应该先运算完才对。

比如若当前遇到了+,然后栈中含有一个×,那么根据运算符的优先级,应该先算×,后算+,所以先将×出栈。

又比如,当前遇到了+,然后栈中含有一个-,虽然他们的优先级相等,但是根据“左优先”原则,也应该先算-,后算+,所以先将-出栈。

再比如,当前遇到了×,然后栈中含有一个+,那么根据运算符的优先级就不用将+弹出栈了,因为应该先算×,后算+。

实际上,中缀转后缀,以及计算后缀表达式,这两步可以结合起来同时做:

实现代码如下:

//计算运算符优先级
int priority(char op)
{
	if (op == '*' || op == '/') return 2;
	else if (op == '+' || op == '-') return 1;
}
//简单的计算
int caculate(int left, int right, char op)
{
	int temp = 0;
	if (op == '+') temp = left + right;
	else if (op == '-') temp = left - right;
	else if (op == '*') temp = left * right;
	else if (op == '/') temp = left / right;
	return temp;
}
//中缀转后缀并计算
int turn_caculate(string str)
{
	stack<char> op_stack;	//符号栈
	stack<int> num_stack;	//操作数栈
	for (int i = 0; i < str.size(); i++)
	{
		if (str[i] == ' ') continue;
		int temp = 0;
		//如果接下来遇到的是操作数,直接入操作数栈
		if ('0' <= str[i] && str[i] <= '9')
		{
			for (int j = i; j < str.size(); j++)
			{
				if ('0' <= str[j] && str[j] <= '9')
				{
					temp = temp * 10 + (str[j] - '0');
					i++;
				}
				else
				{
					i--;
					break;
				}
			}
			num_stack.push(temp);
		}
		//如果遇到的是符号
		else
		{
			switch (str[i])
			{
			//如果是左括号,直接入符号栈
			case '(':
				if (str[i] == '(') op_stack.push(str[i]);
				break;
			//如果是右括号,依次弹出符号栈内运算符直到左括号为止
			case ')':
				while (op_stack.top() != '(')
				{
					//边弹符号边计算
					char op = op_stack.top();
					op_stack.pop();
					int right = num_stack.top();
					num_stack.pop();
					int left = num_stack.top();
					num_stack.pop();
					temp = caculate(left, right, op);
					num_stack.push(temp);
				}
				//最后弹出符号栈中的左括号
				op_stack.pop();
				break;
			//如果是运算符
			default:
				//依次弹出符号栈中,优先级高于或等于当前运算符的所有运算符,直到符号栈空或遇到左括号
				while (!op_stack.empty() && op_stack.top() !=  '(' && priority(op_stack.top()) >= priority(str[i]))
				{
					//同样边弹运算符边计算
					char op = op_stack.top();
					op_stack.pop();
					int right = num_stack.top();
					num_stack.pop();
					int left = num_stack.top();
					num_stack.pop();
					temp = caculate(left, right, op);
					num_stack.push(temp);
				}
				//最后将当前运算符入栈
				op_stack.push(str[i]);
				break;
			}
		}
	}
	//最后将符号栈中剩余的运算符依次弹出并计算
	while (!op_stack.empty())
	{
		int temp = 0;
		char op = op_stack.top();
		op_stack.pop();
		int right = num_stack.top();
		num_stack.pop();
		int left = num_stack.top();
		num_stack.pop();
		temp = caculate(left, right, op);
		num_stack.push(temp);
	}
	return num_stack.top();
}

这里偷了懒,栈直接用的容器,没有设置除0判断啥的。

3.3 栈和队列的应用-课后习题

2、车厢调度

string mobilize(string train)
{
    sta s;
    InitStack(s);
    string ans;
    for (int i = 0; i < train.size(); i++)
    {
        if (train[i] == 'H')
        {
            Push(s, train[i]);
        }
        else
        {
            ans.push_back(train[i]);
        }
    }
    while (!StackEmpty(s))
    {
        char x;
        pop(s, x);
        ans.push_back(x);
    }
    return ans;
}

3、利用栈实现该递归函数的非递归计算

这题说实话,这个函数没怎么看懂,这个下标n其实就是一个参数,函数实际上长这样:

举个例子,比如计算P(3, x)的话:

                  

书上所给的答案,就是利用滚动数组的思想,用fv1和fv2来保存每次所需要的两个P()函数值,所以这题为啥要用栈呢。。我这里的循环是从2到n,按书上的答案来的话,栈的作用实际上就是让循环从n到2,我没太明白这题用栈的意义。

double func(int n, double x)
{
	double fv1 = 1;
	double fv2 = 2 * x;
	for (double i = 2; i <= n; i++)
	{
		double temp = 2 * x * fv2 - 2 * (i - 1) * fv1;
		fv1 = fv2;
		fv2 = temp;
	}
	if (n == 0) return 1;
	return fv2;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值