Data Structures (三) 栈和队列

一、栈

栈是一种限制的线性表,它插入和删除数据只能在栈的栈顶一段进行,另一端叫做栈底。它的数据有先进后出的特点。它的功能就是将数据从一种序列改变为另一种序列。

栈从它的数据存储形式来分,有两种。

1、用顺序存储形式-----顺序栈

2、链式存储形式-----链式栈

#pragma once

typedef size_t DataType;
//顺序栈的结构定义----采用动态顺序表
typedef struct ma_seq_Stack
{

	DataType * _stack;
	DataType _size;
	DataType _capacity;
}ma_seq_Stack;


//初始化栈
void StackInit(ma_seq_Stack * s, DataType capacity)
{
	assert(s && capacity > 0);
	s->_stack = (DataType*)malloc(sizeof(DataType)*capacity);
	assert(s->_stack);
	s->_size = 0;
	s->_capacity = capacity;
}

//进栈
void StackPush(ma_seq_Stack * s, DataType x)
{
	assert(s);
	if (s->_size == s->_capacity)
	{
		s->_capacity *= 2;
		s->_stack = (DataType*)realloc(s->_stack, sizeof(DataType)*s->_capacity * 2);
	}
	s->_stack[s->_size] = x;
	s->_size++;
}

//出栈
void StackPop(ma_seq_Stack * s)
{
	assert(s && s->_size);
	--s->_size;
}

//返回栈的size
DataType StackSize(ma_seq_Stack * s)
{
	assert(s);
	return s->_size;
}

//返回栈顶元素
DataType StackTop(ma_seq_Stack * s)
{
	assert(s && s->_size);
	return s->_stack[s->_size - 1];
}

//判断栈是否为空
bool StackEmpty(ma_seq_Stack * s)
{
	return s->_size;
}

void test_seq_Stack()
{
	ma_seq_Stack stack;
	StackInit(&stack, 5);
	StackPush(&stack, 1);
	StackPush(&stack, 2);
	StackPush(&stack, 3);
	StackPush(&stack, 4);
	StackPush(&stack, 5);
	StackPush(&stack, 6);

	while (StackEmpty(&stack))
	{
		cout << StackTop(&stack) << " ";
		StackPop(&stack);
	}
	cout << endl;
}

//链式栈   因为栈的删除和插入都在栈顶进行操作,所以栈顶Top始终指向栈顶前面的头结点。
typedef struct StackNode
{
	DataType _data;
	struct StackNode* _next;
}StackNode;

//创建节点
StackNode* CreartStackNode(DataType x)
{
	StackNode* node = (StackNode*)malloc(sizeof(StackNode));
	node->_data = x;
	node->_next = NULL;
	return node;
}

//进栈--进行类似于单链表的头插
void StackNodePush(StackNode** top, DataType x)
{
	if (top == NULL)
	{
		*top = CreartStackNode(x);
	}
	else
	{
		StackNode* newNode = CreartStackNode(x);
		newNode->_next = *top;
		*top = newNode;
	}
}

//出栈--进行类似于单链表的头删
void StackNodePop(StackNode** top)
{
	if (top == NULL)
	{
		return;
	}
	StackNode* cur = (*top);
	(*top) = (*top)->_next;
	free(cur);


}

DataType StackNodeTop(StackNode* top)
{
	return top->_data;
}

void testStackNode()
{
	StackNode* top = NULL;
	StackNodePush(&top, 1);
	StackNodePush(&top, 2);
	StackNodePush(&top, 3);
	StackNodePush(&top, 4);
	StackNodePush(&top, 5);

	while (top)
	{
		cout << StackNodeTop(top) << " ";
		StackNodePop(&top);
	}
	cout << endl;
}

二、队列

相当于生活中的排队一样,只允许在一段进行插入,在另一端进行删除的线性表具有先进先出的特性。允许插入的一端称为队尾,允许删除的一段称为队头。

队列也分为顺序队列和链队列

假设有n个元素的队列,需要创建一个d大于n的数组,并且把所有元素存储在数组的前n个单元,数组下标为0的为队头,入队就是在队尾加一个元素,出队就是删除下标为0的位置上的元素,那就是所有的元素得向前移动,因此时间复杂度为O(n),顺序队列的实现和顺序表完全相同。

可是想想,出队列时,没必要把数组全部移动,如果没有限制必须存放在数组的前n个单元,出队的性能会大大增加,也就是说,队头不一定存放在下标为0的位置,每出一次队列,队头的下标++一下。

为了避免只有一个元素时,对头和队尾重合,处理的很麻烦,所以引入两个指针,head指向队头元素,tail指向队尾元素的下一个位置。

但是把数组占满时,tail指向了数组的外边,这将产生很麻烦的问题。还有假设队列最后一个单元占了一个元素,但是队头前面还有空着的数组空间,此时也不能再进行入队,这就叫假溢出。

所以引入了循环队列的定义解决假溢出。

循环队列就是后面满了,再把tail设为下标为0 的地址。这种头尾相接的顺序存储称为循环队列。

可是问题又来了:刚才定义head==tail时为空队列,可是现在队列满了,也是head==tail。

办法一:设置一个flag,刚开始队列为空,head==tail,flag=0;再经历一次head==tail,设置flag=1;此时队列满;

办法二:当队列满时,要保留一个空间空着。用于tail可能比head大,也可能小,所以尽管他们相差一个位置就是满的情况,也可能是差了一圈。所以此时队列满的条件是(tail+1)%MAXSIZE==head。 计算队列长度的公式为(tail-head+MAXSIZE)%MAXSIZE。

队列的链式存储结构---链队列

相当于带头结点的单链表,只能尾插和头删。为了操作方便,队头指针指向头结点,对尾指针指向尾结点。

 

总结:

栈:只能进行尾插和尾删的线性表。

队列:只能进行头删和尾插的线性表。

在顺序存储方面都有一些弊端。

栈可以使用两栈共享空间解决,队列可以使用循环队列解决。

 

三、栈和队列的应用

1、逆波兰表达式

计算机通过把中缀表达式通过栈转为后缀表达式计算出结果的。

中缀转后缀规则:

1、初始化一个空栈。

2、从左到右依次判断,如果是操作数,就直接输出,如果是操作符,就进栈。

3、如果是“(”,没有配对,进栈。

4、如果有“)”配对,则出栈,一直到“(”出栈为止。

5、如果此时操作符的优先级大于栈顶,则进栈,否则栈顶元素出栈。

6、直到栈为空,则成功输出了后缀表达式。

 

计算机计算后缀表达式规则:

1、如果是操作数,则进栈。

2、如果有操作符,则将栈顶的元素作为右操作数,出栈,再讲栈顶作为左操作数,出栈,计算结果后,进栈。

3、栈中只有最后一个操作数,就是后缀表达式的值。

 

栈的其他功能:

1、两个栈实现一个队列

stack1只负责进栈,stack2只负责出栈,出栈时,吧stack1.pop(top),然后在push到stack2中在stack2中pop。

2、两个队列实现一个栈

和上述方法相似

3、两栈共享空间。

借助数组来存放两个栈。其中下标为0的是一个栈的栈底,另一个栈的栈底为数组的末端,两个栈增加元素就是从数组的两端向中间延伸。他们是向中间靠拢,如果两个栈的top不相遇,就可以一直使用。

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值