数据结构丿丶堆栈


堆栈存储及相关操作

 堆栈是具有一定操作约束的线性表,是一种特殊的线性表,只能在一端(即栈顶Top)做插入、删除,具有先入后出的特点。其中插入为入栈push,删除为出栈pop

  • 顺序存储
struct SNode {
    ElementType  Data[maxsize]; /* 存储元素的数组 */
   int Top;      /* 栈顶元素下标,empty时为-1 */
};
typedef struct SNode *Stack;

建栈

Stack CreateStack( )
{
    Stack S = (Stack)malloc(sizeof(struct SNode));
    S->Top = -1;
    return S;
}

入栈和判断栈是否已满

void Push( Stack S, ElementType X )
{
    if ( IsFull(S) )   printf("堆栈满");
    else
    {
        s->Top++;
        S->Data[S->Top] = X;
    }
}
bool IsFull( Stack S )
{
    return (S->Top == S->MaxSize-1);
}

出栈和判断栈是否为空

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

  • 链式存储

 栈的链式存储结构实际上只是一个单链表,叫做链栈,插入和删除操作只能在栈顶进行其中栈顶指针Top一端只能是链表的头结点这一头,否则删除操作的时候,找不到前一个结点

struct SNode {
   ElementType Data;
   struct SNode *Next;
};
typedef struct SNode *Stack;**

建栈

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

入栈(插入头结点之后)

void Push( Stack PtrS, ElementType X )
{ 
    Stack S;
    S = (Stack)malloc(sizeof(struct SNode));
    S->Data = X;
    S->Next = PtrS->Next;
    PtrS->Next = S;
}

出栈和判断栈是否为空

ElementType Pop( Stack PtrS )  
{ /* 删除并返回堆栈S的栈顶元素 */
    Stack S;
    ElementType X;
    if( IsEmpty(PtrS) )
    {
        printf("堆栈空"); 
        return ERROR;  /* ERROR是ElementType的特殊值,标志错误 */
    }
    else
   {
        S = PtrS->Next; 
        X = S->Data;
        PtrS->Next =S->Next;
        free(S);
        return X;
    }
}
bool IsEmpty ( Stack S )
{ /* 判断堆栈S是否为空,即判断头结点的指针指向是否为空*/
    return ( S->Next == NULL );
}

堆栈应用

  • 数制的转换

 将一个十进制数转换为八进制数时,在计算过程中,把 N 与 8 求余得到的八进制数的各位依次进栈,计算完毕后将栈中的八进制数依次出栈输出,输出结果就是待求得的八进制数
 具体实现的时候,可以用顺序存储也可以用链式存储表示

 算法步骤
①初始化一个空栈 S
②当十进制数 N 非零时,循环执行以下操作:
  - 把 N 与 8 求余得到的八进制数压入栈 S
  - N 更新为 N 与 8 的商
③当栈 S 不为空时,循环执行以下操作:
  - 弹出栈顶元素 e
  - 输出 e

void conversion( int N )
{/*对于一个任意的非负十进制数,转换输出与其值对应的八进制数*/
	InitStack(S);
	while(N)
	{
		Push(S, N % 8);    /*N与8的余数压栈*/
		N = N/8;
	}
	while(!IsEmpty(S));
	{
		e = Pop(S);         /*弹出栈顶元素e*/
		cout<<e;
	}
}
  • 括号匹配的检验

 算法步骤
①初始化一个空栈 S
②设置一个标记性变量 flag,用来标记匹配结果,以控制循环及返回结果,初值为1
③依次读入表达式字符 ch,若表达式没有读入完毕且 flag 非零,则循环执行以下步骤:
  - 若 ch 为左括号 ‘(’ 或 ‘[’,则将其压栈
  - 若为 ch 右括号 ‘)’,则根据当前栈顶元素值分情况考虑:若栈顶非空且栈顶元素是 ‘(’,则正确匹配,否则匹配错误,flag 置为0
  - 若为 ch 右括号 ‘]’,则根据当前栈顶元素值分情况考虑:若栈顶非空且栈顶元素是 ‘[’,则正确匹配,否则匹配错误,flag 置为0
④推出循环后,若栈空且 flag 值为1,则匹配成功,返回 true,否则返回 false

bool Matching()
{/*检验表达式中所含括号是否正确匹配,如果匹配,则返回true,否则返回false*/
	InitStack(S);
	int flag=1;          /*标记匹配结果*/
	scanf("%c",&ch);        /*读入第一个字符*/
	while(ch!='#'&&flag)      /*假设表达式以#结尾*/
	{
		switch(ch)
		{
			case '[':
			case '(':
				Push(S,ch);    /*左括号则压栈*/
				break;
			case ')':
				if(!IsEmpty(S)&&GetTop(S)=='(')
					Pop(S);
				else
					flag=0;    /*匹配错误*/
				break;
				case ']':
				if(!IsEmpty(S)&&GetTop(S)=='[')
					Pop(S);
				else
					flag=0;    /*匹配错误*/
				break;
		}
		cin>>ch;         /*继续读入下一个字符*/
	}
	if(IsEmpty(S)&&flag) return true;    /*Empty说明全部匹配完成*/
	else return false;
}
  • 表达式的求值

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


 后缀表达式:

  对于后缀表达式,可以比较容易处理。大致过程为:将后缀表达式从左到右扫描,遇到数字就将数字压如堆栈中,遇到运算符就将运算符前面的两个数字出栈,将运算符置于两数字之间运算,将运算结果入栈。最后栈中剩余的最后一个元素就是运算结果

 例如求:6 2 / 3 - 4 2 * +

 ①从左到右扫描,遇到6和2,分别将其压进堆栈中
 ②遇到除号,将6和2出栈,计算6/2=3,将3入栈;继续扫描,后面遇见一个3,继续入栈
 ③ 遇到减号,将两个3出栈,计算3-3=0,将0入栈;继续扫描,后面遇见4与2,将其入栈
 ④遇到乘号,将4和2出栈,计算4*2=8,将8入栈
 ⑤遇到加号,将0和8出栈,计算0+8=8,将8入栈,扫描之后发现已经没有字符,而堆栈中只有8这个元素,故结果为8

         

 中缀表达式

  对于中缀表达式,我们要做的就是将其变成后缀表达式。

 观察两种表达式的转换:2 + 9 / 3 - / 5 —> 2 9 3 / + 5 -
 ①运算数相对顺序不变
 ②运算符号顺序发生改变
  - 需要存储“等待中”得运算符号
  - 也要将当前运算符号与“等待中”的运算符号比较

 转换过程
  从头到尾读取中缀表达式的每个对象,对不同的对象按照不同的情况处理
 ①运算数:直接输出;
 ②左括号:压入堆栈;
 ③右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
 ④运算符:
  - 若优先级大于栈顶运算符时,则把它压入栈中;
  - 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶元素运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压入栈中。
 ⑤若各对象处理完毕,则将堆栈中存留的运算符一并输出。


/* main */

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100

void Read_Expression(char str[]);    /*读取键盘输入的中缀表达式*/
void Conversion(char Infix[], char Suffix[]);    /*将中缀表达式转换为后缀表达式*/
int Compare(char ch, StackofChar SPtr);    /*运算符的比较*/
float Calculate(char Suffix[]);     /*由后缀表达式求出表达式结果*/

void Push(StackofChar, char);
void PushFloat(StackOfFloat, float);      /* 出栈、入栈的基本操作 */
char Pop(StackofChar);
float PopFloat(StackOfFloat);

struct SNodeofChar {
    char Data[MAXSIZE];      /* 存放字符的堆栈(用于转换中存储运算符,进行优先级比较) */
    int Top;
};
typedef struct SNodeofChar* StackofChar;

struct SNodeOfFloat {
    float Data[MAXSIZE];     /* 存放浮点数的堆栈(用于转换后的求值运算,只需要运算数入栈) */
    int Top;
};
typedef struct SNodeOfFloat* StackOfFloat;

int main()
{
    char InfixStr[MAXSIZE] = {0}, SuffixStr[MAXSIZE] = {0};
    
    /*将在键盘上输入的中缀表达式保存在字符串InfixStr中*/
    Read(InfixStr);   
    
    /*将中缀表达式抓换成后缀表达式保存在字符串SuffixStr中*/
    Conversion(InfixStr,SuffixStr);
    
    printf("\n结果是:%f\n", Calculate(SuffixStr));

    return 0;
}


/* Read_Expression */

void Read_Expression(char str[])
{
    printf("请输入所需求值的中缀表达式(英文输入法):\n");
    gets(str);
}


/* Conversion*/

void Conversion(char Infix[], char Suffix[])
{
    int i=0,j=0,Tag=0;

    struct SNodeofChar SChar = {{'\0'},-1};    /* 建立一个空字符栈,并初始化 */
    StackofChar SPtr = &SChar;

    /* 扫描中缀表达式的所有字符,根据字符的值做出不同操作 */
    while (Infix[i] != '\0')
    {
    	/*当扫描到数字的时候,直接输出到字符串Suffix中*/        
        if (Infix[i] <= '9'&&Infix[i] >= '0')
        {
            Suffix[j] = Infix[i];         
            j++;
        }
        
        /*当扫描到空格的时候,直接输出到字符串Suffix中*/
        else if (Infix[i] == ' ')
        {
            Suffix[j] = Infix[i];         
            j++;
        }
        
        /*当扫描到加减乘除运算符,将其与堆栈顶元素比较,根据优先级决定进栈还是栈顶元素出栈输出*/
        else if(Infix[i] == '+' || Infix[i] == '-' || Infix[i] == '*' || Infix[i] == '/')
        {
            Suffix[j] =' ';
            j++;
             
            Tag = 0       /*扫描到的元素要循环与栈顶元素比较优先级,直至扫描到的元素入栈*/
            while (Tag == 0)
            {
                Tag = Compare(Infix[i], SPtr);
                
                if (Tag == 0)
                	Suffix[j++] = Pop(SPtr);
                else
                	Push(SPtr, Infix[i]);
            }
        }
        
        /*如果扫描到左括号,直接进栈*/
        else if(Infix[i]=='(')
        {
            Push(SPtr, Infix[i]);      
        }
        
        /*如果扫描到右括号,一直出栈输出直到栈顶元素是左括号,然后将栈顶的左括号出栈但不输出*/
        else if (Infix[i] == ')')
        {
            Suffix[j] = ' ';
            j++;
            
            while (SPtr->Data[SPtr->Top] != '(')
            {
                Suffix[j] = Pop(SPtr);
                j++;
            }
            Pop(SPtr);   /* 左括号不输出 */
        }
        
        i++;                 /* 进行下一个字符的扫描 */
    }
    
    /*扫描完之后将堆栈中剩余所有元素出栈*/
    while (SPtr->Top != -1)
    {
        Suffix[j] = Pop(SPtr);    
        j++;
    }
    Suffix[j]='\0';
}


/* Compare */

int Compare(char ch, StackofChar SPtr)
{
    /*如果堆栈空,将扫描到的运算符号进栈*/
    if (SPtr->Top == -1)
        return 1;
    
    /*当栈顶是左括号时,将扫描到的运算符号进栈*/
    if (SPtr->Data[SPtr->Top] == '(')
        return 1;
    
    /*当栈顶是乘号或者除号的情况*/
    else if (SPtr->Data[SPtr->Top] == '/' || SPtr->Data[SPtr->Top] == '*')
        return 0;       /* 一定为栈顶元素出栈,因为括号不调用Compare函数 */
        
    /*当栈顶元素是加号或者减号时*/
    else
    {
         if (ch == '*' || ch == '/')      
            return 1;     /*如果扫描到的是乘号或除号,将栈顶元素进栈*/
        else           
            return 0;       /*如果扫描到的是加号或者减号,将栈顶元素出栈*/
    }
}


/* Calculate */

float Calculate(char Suffix[])
{
    float num = 0, temp = 0, first = 0, last = 0;
    int i = 0;
    
    struct SNodeOfFloat SFloat = {{0},-1};    /* 建立一个空浮点数栈存储运算数 */
    StackOfFloat SPtr = &SFloat;    
    
    while (Suffix[i] != '\0')      /* 扫描后缀表达式,计算结果 */
    {
    
    	/* 当扫描到的是数字字符,将其转换成数字,并将其压入栈中 */
    	if (Suffix[i] <= '9' && Suffix[i] >= '0')
    	{
    		num = 0;
    		while (Suffix[i] <= '9' && Suffix[i] >= '0')
    		{
          		temp = (float)(Suffix[i] - '0');
          		num = num * 10 + temp;      /* 运算数可能为多位数(数字与数字间无空格) */
          		i++;
          	}
          	PushFloat(SPtr, num);
        }

        /* 当扫描到空格,则忽略 */
        else if (Suffix[i] == ' ')
        	i++;

        /*当扫描到加减乘除号,将栈顶两个元素弹出,并将其用扫描到的运算符号计算,将结果压入栈中*/
        else if(Suffix[i] == '+'||Suffix[i] == '-'||Suffix[i] == '*'||Suffix[i] == '/')
        {
        	first = PopFloat(SPtr);
        	last = PopFloat(SPtr);
        	if (Suffix[i] == '+')
        	{
        		PushFloat(SPtr, first + last);
        		i++;
        	}
        	else if (Suffix[i] == '-')
        	{
        		PushFloat(SPtr, first - last);
        		i++;
        	}
        	else if (Suffix[i] == '*')
        	{
        		PushFloat(SPtr, first*last);
        		i++;
        	}
        	else
        	{
        		PushFloat(SPtr, first / last);
        		i++;
        	}
        }
        
        return PopFloat(SPtr);      /* 栈中最后一个数字就是结果,将其返回 */
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值