数据结构 -- 栈ADT

数据结构 -- 栈ADT

栈模型

栈(又叫LIFO(后进先出)表)是限制插入和删除只能在一个位置上进行的表, 该位置是表的末端,叫做顶(top)。对栈的基本操作有Push(进栈)和Pop(出栈), 前者相当于插入,后者是删除最后插入的元素。对空栈进行Pop一般视为栈ADT的错误,而当运行Push是空间用尽是一个实现错误,但不是ADT错误,所以普通的请空栈操作与判断栈是否为空的测试都是栈的基本操作,但是,栈基本做的也就是Push和Pop操作了。学习栈的结构最好结合图形来学,画画图,结合基本的数组,指针与链表操作,栈是挺容易的一个数据结构,最重要是学会使用。

栈的实现

栈的实现分链表实现和数组实现。链表实现主要是使用指针,数组实现避免的指针的使用,可能是更流行的策略。

一:栈的链表实现

栈的链表实现是使用单链表,通过在表顶端插入来实现Push,通过删除表顶端元素来实现Pop。Top操作只是考查表的顶端元素并返回它的值,有时候Top与Pop操作合二为一。下面我们来实现这些操作的例程。
1. 类型声明
我们先给出栈ADT链表的类型声明:

struct Node;
typedef struct Node *PtyToNode;
typedef PtyToNode Stack;
int IsEmpty(Stack S);	/*判断是否为空*/
Stack CreateStack();	/*创建栈*/
void MakeEmpty(Stack S);	/*让栈为空*/
void Push(ElementType X, Stack S);	/*入栈*/
ElementType Top(Stack S);	/*Top操作*/
void Pop(Stack S);	/*出栈*/
struct Node 
{ 
     ElementType Element; 
     PtyToNode Next;
};

2.判断栈是否为空

int IsEmpty(Stack S)
{
     return S->next == NULL;
}

3.创建一个空栈,建立一个头结点; MakeEmpty设置栈为空。

Stack CreateStack()
{
     Stack S;
     S = molloc(sizeof(struct Node));
     if(S == NULL)
         exit(1);
     S->next = NULL;
     MakeEmpty(S);
     return S;
}

void MakeEmpty(Stack S)
{
     if(S == NULL)
          exit(1);
     else
          while(!IsEmpty(S))
                Pop(S);
}

4. Push进栈
Push是作为向链表前端进行插入而实现的,记得要判断内存空间是否不足。

void Push(ElementType X, Stack S)
{
     PtyToNode TmpCell;
     TmpCell = malloc(sizeof(struct Node));
     if(TmpCell == NULL)
     {
          printf("Out of space!!!");
          exit(1);
     }
     else
     {
          TmpCell->Element = X;
          TmpCell->next = S->next;
          S->next = TmpCell;
     }
}

5.Top返回栈顶元素

ElementType Top(Stack S)
{
     if(!IsEmpty(S))
         return S->next->Element;
     else
     {
          printf("Empty stack");
          return 0;
      }
}
6.Pop弹出元素

void Pop(Stack S)
{
     PtyToNode FirstCell;
     if(IsEmpty(S)
     {
          printf("Empty stack");
          exit(1);
     }
      else
      {
           FirstCell = S->Next;
           S->Next = S->Next->Next;
           free(FirstCell);
      }
}

二。栈的数组实现

用一个数组实现栈是很简单的,但是需要提前声明一个数组的大小,存在着危害性,但声明一个数组足够大而不至于浪费太多的空间通常没什么困难,真的要节省空间就用链表实现。每一个栈有一个TopOfStack,空栈它是-1,进栈加1,然后置Stack[TopOfStack] = X; 出栈置返回值为Stack[TopOfStack],然后TopOfStack减1。下面来用数组实现:

1. 顺序栈的声明

struct StackRecord;
typedef struct StackRecord *Stack;

int IsEmpty(Stack S);
int IsFull(Stack S);
Stack CreateStack(int MaxElements);
void MakeEmpty(Stack S);
void Push(ElementType X, Stack S);
ElementType Top(Stack S);
void Pop(Stack S);
ElementType TopAndPop(Stack S);

#define EmptyTOS = -1
#define MinStackSize(5)

struct StackRecord
{
	int Capacity;
	int TopOfStack;
	ElementType *Array;
};

2.顺序栈的创建

Stack Create(int MaxElements)
{
	Stack S;
	if(MaxElements < MinStackSize)
	{
		printf("Stack size is too small");
		exit(1);
	}
	S = malloc(sizeof(struct StackRecord));
	if(S == NULL) 
	{
		printf("Out of space!!!");
		exit(1);
	}
	S->Array = malloc(sizeof(ElementType) * MaxElements);
	if(S->Array == null)
	{
		printf("Out of space!!!");
		exit(1);
	}
	S->Capacity = MaxElements;
	MakeEmpty(S);
	return S;
}

3.判断顺序栈是否为空

int IsEmpty(Stack S) 
{
	return S->TopOfStack == EmptyTOS;
}

4.创建一个空栈
void MakeEmpty(Stack S) 
{
	S->TopOfStack = EmptyTOS;
}

5.进栈

void Push(ElementType X, Stack S)
{
     if(IsFull(S))
     {
          printf("Full Stack");
          exit(1);
      }
       else
            S->Array[++S->TopOfStack] = X;
}





6.返回栈顶元素
ElementType Top(Stack S)
{
	if(!IsEmpty(S))
		return S->Array[S->TopOfStack];
	else 
	{
		printf("Empty stack");
		return 0;
	}
}

7.出栈
void Pop(Stack S)
{
	if(IsEmpty(S))
	{
		printf("Empty stack");
		exit(1);
	}
	else 
		S->TopOfStack--;
}


8.给出栈顶元素并从顺序栈弹出
ElementType TopAndPop(Stack S)
{
	if(!IsEmpty(S))
		return S->Array[S->TopOfStack--];
	else
	{
		printf("Empty stack");
		return 0;
	}
}

栈的应用

1。平衡符号

编译器会因为你的程序缺少一个符号(如遗漏了一个花括号)而引起编译器列出上百行的测断,而真正的错误却没有找到。于是我们要有一个工具,让每一个括号自动配对,这些实现就要用到栈。做一个空栈,读入字符直到文件尾,如果是开放符号,将其进栈;如果是封闭符号,则当栈空时报错,否则将栈元素弹出。如果弹出的符号是对应的开放符号,则报错。在文件尾,如果栈非空则报错。

2.中缀表达式到后缀表达式的转换

栈不仅可以用来计算后缀表达式的值,而且可以用栈来将中缀表达式到后缀表达式的转换
如: 中缀表达式:a + b * c + (d *e+ f) * g 转为:
      后缀表达式:a b c * + d e * f + g * +
算法:1.设置一个空栈  
   2.从做左到右读取中缀表达式,当读入一个操作数时,立即将它放到输出中
   3.当读入运算符,则将该运算符与当前的栈顶元素进行比较,当该运算符优先级大于栈顶运算符的优先级,则将该运算符放入栈中;否则,将栈的元素弹出直到栈顶元素优先级低于该运算符
           4.如果是左括号,直接进栈,如果是右括号,将栈的元素弹出直到遇到响应的左括号为止。
           5.重复上述步骤,直到处理完所有字符。

3.十进制转换为d进制

将用十进制值除以d,保留其余数,即让余数进栈,重复操作指导该十进制数值为0为止。最后将所有的余数反向输出就得到对应的d进制。
N = (N div d) * d + N mod d (其中div为整除运算,mod为取余运算)

4. 迷宫问题求解

从迷宫入口开始,沿某一方向进行探索,若有通路则继续前进,没有通路则原路返回,从另外方向在进行探索,知道所有可能的道路都被探索完为止。利用栈来保存探索的路径,从而回溯出迷宫的道路。

5. 函数调用

一个函数调用时,需要存储所用的重要信息,以抽象方式来存在“一张纸”上,并置于堆的顶部,当进行其他的函数调用,遵循同样的过程。当该函数要返回时,它查看堆顶部的那张“纸”并复原所有的寄存器,然后进行返回转移。这些工作均可由一个栈完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值