数据结构——堆栈、队列

一、后缀表达式

  • 中缀表达式:运算符号位于两个运算符之间。如:a+b*c-d/e
  • 后缀表达式:运算符号位于两个运算符之后。如:abc*+de/-

要计算后缀表达式:应从右向左“扫描”,逐个处理运算符和运算符号。可使用堆栈储存运算数,在需要时“倒序”输出。

二、堆栈

堆栈(Stack):具有一定操作约束的线性表,只在一端(栈顶,Top)做插入、删除。

  • 插入数据:入栈(Push)
  • 删除数据:出栈(Pop)
  • 后入先出:Last In First Out(LIFO)

数据对象集:一个由0个或多个元素的有穷线性表;
操作集:长度为MaxSize的堆栈S和堆栈元素item。

  1. 栈的顺序存储实现
    栈的顺序存储结构通常是由一个一维数组和一个记录栈顶元素位置的变量组成。如下:
#define MaxSize	//储存数据元素的最大个数
typedef struct SNode *Stack;//结构指针
struct SNode
{
	ElementType Data[MaxSize];
	int Top;
} 

(1)入栈

void Push(Stack PtrS,ElementType item)
{
	if(PtrS->Top == MaxSize-1)
	{
		printf("堆栈满");
		return;
	} 
	else
	{
		ptrS->Data[++(PtrS->Top)] = item;
		//等于执行(PtrS->Top)++;
		//PtrS->Data[PtrS->Top] = item;
		return;
	}
}

(2)出栈

ElementType Pop(Stack PtrS)
{
	if(PtrS->Top == -1)
	{
		printf("堆栈空");
		return ERROR;	//ERROR是ElementType的特殊值,标志错误 
	}
	else
	{
		return (PtrS->Data[(PtrS->Top)--]);
	}
}

eg:用一个数组实现两个堆栈,要求最大的利用数组空间,使数组只要有空间入栈操作就可以成功。
我们就可以让这两个栈分别从数组的两头开始向中间生长;当两个栈的栈顶指针相遇时,表示这两个栈都满了。

#define MaxSize //存储数据元素的最大个数
struct DStack
{
	ElementType Data[MaxSize];
	int Top1;			//堆栈1的栈顶指针 
	int Top2;			//堆栈2的栈顶指针 
}S; 
S.Top1 = -1;
S.Top2 = MaxSize;
void Push(struct DStack *PtrS, ElementType item, int Tag)//Tag作为区分两个堆栈的标志,取值为1,2 
{
	if(PtrS->Top2 - PtrS->Top1 == 1)//堆栈满 
	{
		printf("堆栈满"); 
		return; 
	} 
	if(Tag == 1)		//对第一个栈操作 
	{
		PtrS->Data[++(PtrS->Top1)] = item;
	}
	else				//对第二个栈操作  
	{
		PtrS->Data[--(PrtS->Top2)] = item;
	}
}
ElementType Pop(struct DStack * PtrS, int Tag)
{
	if(Tag == 1)			//对第一个堆栈操作 
	{
		if(PtrS->Top1 == -1)
		{
			printf("堆栈1空");
			return;
		}
		else
		{
			return PtrS->Data[(PtrS->Top1)--];
		}
	}
	else				//对第二个堆栈操作  
	{
		if(PtrS->Top2 == MaxSize)
		{
			printf("堆栈2空");
		} 
		else
		{
			return PtrS->Data[(PtrS->Top2)++];
		}
	}
} 
  1. 堆栈的链式存储实现
    栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。栈顶的指针Top应该为链表的头。
typedef struct SNode *Stack;
struct SNode
{
	ElementType Data;
	struct SNode * Next;
};

(1)堆栈初始化(建立空栈)
(2)判断堆栈s是否为空

Stack CreateStack()//构建一个堆栈的头结点,返回指针 
{
	Stack S;
	S = (Stack)malloc(sizeof(struct SNode));
	S->Next = NULL;
	return S; 
}

int IsEmpty(Stack S)//判断堆栈S是否为空,若为空,函数返回整数1,否则返回0 
{
	return (S->Next == NULL); 
}

(3)将元素item压入堆栈S

void Push(ElementType item, Stack S)
{
	struct SNode *TmpCell;
	TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
	TmpCell->ElementType = item;
	TmpCell->Next = S->Next;
	S->Next = TmpCell;
}

(4)删除并返回堆栈S的栈顶元素

ElementType Pop(Stack S)
{
	struct SNode *FirstCell;
	ElementType TopElem;
	if(IsEmpty(S))
	{
		printf("堆栈空");
		return NULL;
	}
	else
	{
		FirstCell = S->Next;
		S->Next = FirstCell->Next;
		TopElem = FirstCell->Element;
		free(FirstCell);
		return TopElem;
	}
}

三、应用于中缀表达式求值

可将中缀表达式转换为后缀表达式:
从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。

  1. 运算数:直接输出;
  2. 左括号:压入堆栈;
  3. 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈、不输出);
  4. 运算符:
  • 若优先级大于栈顶运算符时,则把它压栈;
  • 若优先级小于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
  1. 若各对象处理完毕,则把堆栈中存留的运算符一并输出。

下面是一个将中缀表达式转换为后缀表达式的栗子:
数据结构——堆栈、队列
堆栈的其他应用:

  • 函数调用及递归实现
  • 深度优先搜索
  • 回溯算法

四、队列

队列(Queue):具有一定操作约束的线性表
插入和删除操作:只能在一端插入,在另一端删除。

  • 数据插入:入队列(ADDQ)
  • 数据删除:出队列(DeleteQ)
  • 先进先出:FIFO

数据对象集:一个有0个或多个元素的有穷线性表。
操作集:长度为MaxSize的队列Q,队列元素item。

  1. 队列的顺序存储实现
    队列的顺序存储结构通常是由一个一维数组和一个记录队列头元素的位置变量front以及一个记录队列尾元素位置的rear组成。
#define MaxSize		//储存数据元素的最大个数
struct QNode
{
	ElementType Data[MaxSize];
	int rear;
	int front;
};
typedef struct QNode *Queue;

在循环队列中只存放MaxSize-1个元素(此时认为队列已满)
(1)入队列

void AddQ(Queue PtrQ, ElementType item)
{
	if((PtrQ->rear + 1) % MaxSize == PtrQ->front)
	{
		printf("队列满");
		return;
	}
	PtrQ->rear = (PtrQ->rear + 1) % MaxSize;
	ptrQ->Data[PtrQ->rear] = item;
}

(2)出队列

ElemnetType DeleteQ(Queue PtrQ)
{
	if(PtrQ->front == PtrQ->rear)
	{
		printf("队列空");
		return ERROR;
	}
	else
	{
		PtrQ->front = (PtrQ->front + 1) % MaxSize;
		return PtrQ->Data[PtrQ->front];
	}
}
  1. 队列的链式存储实现
    队列的链式存储结构也可以用一个单链表实现。插入和删除操作分别在链表的两头进行;队列指针front应该指向链表的头部,rear应该指向链表的尾部。
struct Node
{
	ElementType Data;
	struct Node * Next;
};
struct QNode		//链队列结构 
{
	struct Node *front;	//指向队头结点 
	struct Node *rear;	//指向队尾结点 
};
typedef struct QNode *Queue;
Queue PtrQ; 

(1)不带头结点的链式队列的出队操作

ElementType DeleteQ(Queue PtrQ)
{
	struct Node *FrontCell;
	ElementType FrontElem;
	
	if(PtrQ->front == NULL)
	{
		printf("队列空");
		return ERROR;
	}
	FrontCell = PtrQ->front;
	if(PtrQ->front == PtrQ->rear)		//若队列中只有一个元素 
	{
		PtrQ->front = PtrQ->rear = NULL;//删除后队列置为空 
	}
	else
	{
		PtrQ->front = PtrQ->front->Next;	
	} 
	FrontElem = FrontCell->Data;
	free(FrontCell);				//释放被删除结点空间 
	return FrontElem;
}
上一篇:(C语言)堆栈顺序存储源码(包含测试)


下一篇:Qt新建项目No valid kits found解决思路