记录数据结构的学习005

(此文为王道数据结构学习笔记,只供个人后续复习使用,若有错误,还望指出我改正,谢谢)

队列的链式实现:

引入队头队尾指针

定义与初始化一个链式列表

typedef struct{
	ElemType data;
	struct LinkNode *next;
}LinkNode;//链式队列的单个结点的结构体

typedef struct{
	LinkNode *front, *rear;//头指针,尾指针
}LinkQueue;//链式队列

void InitQueue(LinkQueue &Q) {
	Q.front = Q.rear = (LinkNode *)malloc(sizeof(LinkNode));//初始时头指针尾指针都指向头节点
	Q.front->next = NULL;
}//链式队列初始化

判断队列是否为空,当头尾指针都指向同一结点(即头结点)时,队列即为空。

bool IsEmpty(LinkQueue Q) {
	if (Q.front = Q.rear) //如果头尾指针指向同一个结点,则队列为空
		return true;
	else
		return false;
}

新结点入队

只能从队尾进入

void EnQueue(LinkQueue &Q, ElemType x) {
	LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));//创建新结点
	s->data = x;//给新结点赋值
	s->next = NULL;//新结点的下一个结点为空
	Q.rear->next = s;//尾指针所指结点(即现在的尾结点)的next指向新结点
	Q.rear = s;//尾指针指向新结点,新结点成为新尾结点
}

结点出队:

只能从队头出队

void DeQueue(LinkNode &Q, ElemType &s) {
	if (Q.front = Q.rear)//空队列
		return false;
	LinkNode *p = Q.front->next;//定义一个临时指针p指向头结点的下一个(即1号结点,队头结点)
	x = p->data;//x返回该结点的值
	Q.front->next = p->next;//令头结点的next指向该结点的next(即2号结点),完成出队
	if (Q.rear == p)//如果队尾指针指向的队头结点
		Q.rear = Q.front;//出队后队列即空
	free(p);//释放临时指针
	return true;
}

双端队列(真题出现过)

两端都可以插入删除

输入受限制的双端的队列:只能从一端入队,而两端都可以出队;

输出受限制的双端的队列:只能从一端出队,而两端都可以入队;

比栈的输出序列更多

栈的应用

1.括号匹配问题(即代码中左右括号需匹配)

每检索到一个右括号,即向左找最近一个没有被匹配过的左括号。

即左括号入栈,每遇到一个右括号,一个左括号出栈。

算法思路程序流程图:

 

算法实现:

#define MaxSize 100
typedef struct {
	char data[MaxSize];
	int top;
}SqStack;

void InitStack(SqStack &S) {
	S.top = -1;
}//初始化括号栈

bool Push(SqStack &S, char e) {
	if (S.top == MaxSize)
		return false;
	else {
		S.data[++S.top] = e;
		return true;
	}
}//左括号入栈

bool Pop(SqStack &S, char &e) {
	if (S.top == -1)
		return false;
	else {
		e = S.data[S.top--];
		return true;
	}

}

bool StackEmpty(SqStack S) {
	if (S.top == -1)
		return true;
	else
		return false;
}

bool bracketCheck(char str[], int length) {
	SqStack S;
	InitStack(S);
	for (int i = 0; i < length; i++) { //扫描完所有字符
		if (str[i] == '(' || str[i] == '[' || str[i] == '{') { //判断是否为左括号
			Push(S, str[i]);//是左括号即入栈
		}
		else if (str[i] == ')' || str[i] == ']' || str[i] == '}') { //扫描到右括号
			if (StackEmpty(S)) {
				return false; //扫描到右边
			}

			char topElem;
			Pop(S, topElem);
			if (str[i] = ')'&& topElem != '(')
				return false;
			if (str[i] = ']'&& topElem != '[')
				return false;
			if (str[i] = '}'&& topElem != '{')
				return false;
			//扫描到的右括号与栈顶左括号元素不匹配
		}
	}
	return StackEmpty(S);//扫描完毕后,如果栈为空,则全部成功,如果栈不为空,则表示失败
}
int main() {
	char str[100];
	bool s;
	int length;
	printf("请输入一段100以内的字符串,以“@”作为结束。\n");
	for (int i = 0; i < 100; i++) {
		scanf_s("%c", &str[i]);
			if (str[i] == '@')
				break;
	}
	length = sizeof(str);
	s = bracketCheck(str, length);
	if(s==true)
		printf("匹配成功!");
	else
		printf("匹配失败!");
	

}

2.表达式求值

三种算数表达式:

中缀表达式:即数学上的算数表达式,运算符在两个操作数中间   

a + b

a + b - c * d

后缀表达式:(逆波兰表达式),运算符在两个操作数后面   

a b +

a b + c d * -  

前缀表达式:(波兰表达式),运算符在两个操作数前面   

+ a b

- + a b * c d

计算后缀表达式的思路:后缀表达式是运算符在两个操作数后面,所以让操作数一一进栈,然后如果扫描到运算符,即让栈顶的两个操作数出栈,进行运算后(注意左右顺序,再将运算结果入栈作为新的操作数,重复上述操作,直至扫描完毕。

算法流程图:

算法实现:

char Reverse_Polish_notation(char str[], int length) {
	SqStack S;
	char a, b, s;
	InitStack(S);
	for (int i = 0; i < length; i++) { //扫描完所有字符
		if (str[i] == '+'&&str[i] == '-'&&str[i] == '*'&&str[i] == '/')
			Push(S, str[i]);
		else {
			Pop(S, a);
			Pop(S, b);
			if (str[i] == '+')
				s = b + a;
			if (str[i] == '-')
				s = b - a;
			if (str[i] == '*')
				s = b * a;
			if (str[i] == '/')
				s = b / a;
			Push(S, s);
		}
	}
	return Pop(S, s);
}

中缀表达式转化为后缀表达式:

操作数的相对顺序不会改变,但运算符的相对顺序会改变。

思路:

初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。

从左到右扫描处理各个字符,直到末尾,中途可能会遇到以下三种情况:

1.遇到操作数。直接加入后缀表达式。

2.遇到界限符号。遇到左括号直接入栈;遇到右括号则依次弹出栈内运算符并加入后缀表达式,直到弹出左括号为止(左括号不加入后缀表达式)

3.遇到运算符。依次弹出栈中高于或等于当前运算符的所有运算符,并且加入后缀表达式,直到碰到左括号或者栈空。之后再把当前运算符入栈。

按照上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

算法流程图:

栈在递归中的应用

斐波那契数列

斐波那契数列定义为

Fib(n)=Fib(n-1) + Fib(n-2),Fib(1) = 1,Fib(0) = 0;其中n>1

可以通过调用自身形成函数栈完成Fib(n)计算的操作 

int Fib(int n) {
	if (n == 0)
		return 0;
	else if (n == 1)
		return 1;
	else
		return Fib(n - 1) + Fib(n - 2);
}

函数栈内部会一一进栈,完成运算后一一出栈反馈结果给上一个函数继续完成出栈,直至出结果

队列在遍历中的应用:

用队列可以实现树的的层次遍历:

1.根结点入队;

2.若队列为空(所有结点都处理完),则结束遍历;否则重复3操作;

3.将队列中第一个结点出队,访问之。如果它有左孩子,将之入队;如果它有右孩子,将之入队。返回2。

如此可以用队列实现树的层序遍历

队列在操作系统中的应用:

多个进程争抢有限系统资源时FCFS(First Come First Service);

队列可以实现缓冲区功能。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值