数据结构-栈


栈 stack 

    1)栈 是限定 在表尾(栈顶)进行插入和删除操作的线性表 

        栈是一种思想 "先进后出 LIFO"     "后进先出"

            栈顶 top    进行插入和删除操作的地方(表尾)
            栈底 bottom                       (表头)


    2)要对栈进行的基本操作: 


        InitStack()     初始化一个栈
        Push()          入栈
        Pop()           出栈
        StackLength()   栈的长度(元素个数)
        GetTop()        获取栈顶元素的值,但是不出栈
        IsEmpty()       判断是否为空栈
        ClearStack()    清空栈
        DestroyStack()  销毁栈


    3)栈的实现

        3.1)顺序栈 
            顺序结构的存储方式
            用一组地址连续的空间 依次存放栈的每一个元素    "数组"

            #define MAX_LEN  20         //栈的最大的容量
            typedef int ElemType;       //数据的类型 

            typedef struct SqStack          //顺序栈
            {
                ElemType *data;     //data指向一块地址连续的空间,来存储栈的每一个元素 
                                        //指针实现 : data = (ElemType *)malloc( sizeof(ElemType) * MAX_LEN ); 
                                        //数组实现 : ElemType data[MAX_LEN];

                int top;            //栈顶, 指定栈顶元素的下标
                                    //top == -1  表示栈中没有元素, 空栈 
            } SqStack ;


                练习: 
                    1)初始化一个顺序栈, 将栈的地址返回 

                            SqStack.c   / SqStack.h    

//初始化一个顺序栈
SqStack * InitStack()
{
    //创建一个顺序栈,并初始化
    SqStack * s = (SqStack *)malloc( sizeof(SqStack) );
    s->data = (ElemType *)malloc( sizeof(ElemType)*MAX_LEN );
    s->top = -1;

    return s;
} 

                    2)销毁一个顺序栈 

//销毁一个顺序栈 
void DestroyStack( SqStack * s )
{
	if( s == NULL )
	{
		return ;
	}

	if( s->data )
	{
		free( s->data );
	}
	s->top = -1;

	free(s);
}   

                    3)清空一个栈 

//清空一个栈 
void ClearStack( SqStack * s )
{ 
	if( s )
	{
		//清空之后,只能入栈,会把原来的数据覆盖掉
		s->top = -1;
	}
}


                    4)判断是否为空栈 
                        返回值: 
                            1   为空
                            0   不为空

/*
int IsEmpty( SqStack * s )
{
    //栈为空: 栈不存在 || 栈里面没有元素
	if( s == NULL || s->top == -1 )
	{
		return 1;
	}

	return 0;
}
*/
bool IsEmpty( SqStack * s )
{
    //栈为空: 栈不存在 || 栈里面没有元素
	if( s == NULL || s->top == -1 )
	{
		return true;
	}

	return false;
}

                    5)获取栈的长度 (栈中元素的个数)

//获取栈的长度 (栈中元素的个数)
int StackLength( SqStack * s )
{
	if( s == NULL )
	{
		return 0;
	}

    return s->top + 1;
}

                    6)入栈 
                        返回值: 
                            1   入栈成功
                            0   入栈失败

int Push( SqStack * s, ElemType d )
{
    //不能入栈的情况: 栈不存在 || 栈满了
    if( s == NULL || s->data == NULL || s->top == MAX_LEN - 1 )
    {
		//printf("Push error! \n");
		return 0;
    }

    //入栈 
    s->data[ ++(s->top) ] = d;
    return 1;
}

                    7)出栈 
                        返回值: 
                            1   出栈成功 
                            0   出栈失败 

int Pop( SqStack * s, ElemType *d )
{
    //不能出栈的情况: 栈不存在 || 栈为空
    if( s == NULL || s->data == NULL || s->top == -1 )
    {
		//printf("Pop error! \n");
		return 0;
    }

    //出栈 
    *d = s->data[ (s->top)-- ];
    return 1;
}

                    8)获取栈顶元素的值,但是不出栈
                        返回值: 
                            1   出栈成功 
                            0   出栈失败

int GetTop( SqStack * s, ElemType *d )   
{
	//不能出栈的情况: 栈不存在 || 栈为空
    if( s == NULL || s->data == NULL || s->top == -1 )
    {
		//printf("GetTop error! \n");
		return 0;
    }

    //获取栈顶元素的值,但是不出栈 (top下标不变) 
    *d = s->data[ s->top ];
    return 1;
}   

    
        3.2) 链式栈 
                用链表来实现  "带头结点的双向链表"

                //数据元素的类型 
                typedef int ElemType;       //数据元素中数据的类型
                typedef struct node 
                {
                    ElemType data;          //数据域 --> 存储数据 
                    struct node * next;     //指针域 --> 保存逻辑上的下一个
                    struct node * prev;                 //保存逻辑上的上一个
                } DNode;

                //栈 头结点的类型 
                typedef struct LinkStack 
                {
                    struct node * top;          //指向栈顶元素 last
                    struct node * bottom;       //指向栈底元素 first
                    int num;                    //记录栈中元素的个数 
                    //...
                } LinkStack;

               

                     基本操作: 同上 

            
                    练习: 
                        1)初始化一个栈, 将栈的地址返回 

                            LinkStack.c   / LinkStack.h  

//初始化一个栈
LinkStack * InitStack()
{
    //创建一个栈(头结点), 并初始化 
    LinkStack * s = (LinkStack *)malloc( sizeof(LinkStack) );
    s->top = NULL;
    s->bottom = NULL;
    s->num = 0;

    return s;
}

                        2)清空一个栈 

//清空一个栈 
void ClearStack( LinkStack * s )
{
    //栈不存在 || 空栈 
    if( s == NULL || s->top == NULL )
    {
    	return ;
    }

    //遍历 --> 释放所有的数据节点的空间 
	DNode * p = s->top;		//遍历指针
	while( p )
	{
		s->top = s->top->prev;
		if( s->top )
		{
			s->top->next = NULL;
		}
		p->prev = NULL;

		free( p );

		p = s->top;
	}

    s->num = 0;
    s->bottom = NULL;
}

                        3)销毁一个栈

//销毁一个栈
void DestroyStack( LinkStack * s )
{
    //栈不存在
    if( s == NULL )
    {
		return ;
    }

    //释放所有的数据节点的空间
	ClearStack( s );

    //再 释放栈头结点
    free( s );
}

                        4)判断是否为空栈 
                            返回值: 
                                1 为空
                                0 不为空

int IsEmpty( LinkStack * s )
{
    //栈不存在 || 栈里面没有元素 
    if( s == NULL  ||  s->top == NULL )
    {
		return 1;
    }
    
	return 0;
}

                        5)获取栈的长度 (栈中元素的个数)

//获取栈的长度 (栈中元素的个数)
int StackLength( LinkStack * s )
{
	if( s == NULL )
	{
		return 0;
	}

	return s->num;
}

                        6)入栈 
                            返回值: 
                                1 入栈成功 
                                0 入栈失败

int Push( LinkStack * s , ElemType d )
{
    //栈不存在
    if( s == NULL )
    {
		return 0;
    }

    //为入栈的元素 创建一个新的节点空间, 并初始化
    DNode * pnew = (DNode *)malloc( sizeof(DNode) );
    pnew->data = d;
    pnew->next = NULL;
    pnew->prev = NULL;

    //入栈, 只能在栈顶top进行插入和删除操作 --> 尾插法 
    if( s->top == NULL ) 	//从无到有
    {
    	s->top = pnew;
    	s->bottom = pnew;
    }
    else 	//从少到多
    {
    	//尾插法
    	s->top->next = pnew;
    	pnew->prev = s->top;
    	s->top = pnew;
    }
    s->num ++;

	return 1;
}

                        7)出栈 
                            返回值: 
                                1 出栈成功
                                0 出栈失败 

int Pop( LinkStack * s , ElemType *d )
{
    //栈不存在 || 栈为空
    if( s == NULL || s->top == NULL )
    {
		return 0;
    }

    //出栈 
    //先获取栈顶元素top的值 
	*d = s->top->data;
    
    //然后在释放该栈顶元素top (删除尾节点)
    DNode *p = s->top;

    s->top = s->top->prev;
    if( s->top )
    {
		s->top->next = NULL;
    }
    p->prev = NULL;
    free( p );
    s->num --;

    if( s->num == 0 )
    {
		s->bottom = NULL;
    }

	return 1;
}

                        8)获取栈顶元素的值,但是不出栈 
                            返回值: 
                                1 出栈成功
                                0 出栈失败

int GetTop( LinkStack * s , ElemType *d )
{
    //栈不存在 || 栈为空
    if( s == NULL || s->top == NULL )
    {
		return 0;
    }

    //获取栈顶元素的值 
	*d = s->top->data;

	return 1;
}


        练习: 
            1)简答题 :
                有5个元素,其入栈顺序为: A,B,C,D,E 
                在各种可能出栈的次序中, 
                    第一个出栈为C,且第二个出栈为D的情况有哪些? 

                    CDEBA
                    CDBEA 
                    CDBAE    

            2)编程题 
                从键盘上获取一串数学表达式(数字+运算符+括号)
                请判断其括号是否匹配正确
                    返回值: 
                        1   匹配正确
                        0   匹配错误

                    例子: 
                        9+3-2*(3-1)+5       OK 
                        {(1+2*3)-[3]()}     OK 
                        (4-2*[6){}          error 
                        (1*3+[2-3)+4]       error 

                   
                        栈的思想 
                            遍历字符串 
                                如果是左边的括号, 就入栈 (入栈其对应的右边括号)
                                如果是右边的括号, 就出栈 进行对比

int judge_match_brake( char *str )
{
	int flag = 1;	//标志位,1匹配正确,0匹配失败

    //初始化一个顺序栈
    SqStack * s = InitStack();

    //遍历字符串 
    int i;
    for( i=0; i<=strlen(str); i++ )
    {
		//如果是左边的括号, 就入栈 (入栈其对应的右边括号)
		if( str[i] == '(' )
		{
			Push( s, ')' );
		}
		else if( str[i] == '{' )
		{
			Push( s, '}' );
		}
		else if( str[i] == '[' )
		{
			Push( s, ']' );
		}

		//如果是右边的括号, 就拿它与出栈元素 进行对比
		if( str[i] == ')' || str[i] == '}' || str[i] == ']' )
		{
			if( ! IsEmpty( s ) )	//栈不为空
			{
				ElemType temp = 0;
				Pop( s, &temp );	//出栈

				if( temp != str[i] )
				{
					flag = 0;
					break;
				}
			}
			else		//栈为空:左边的括号少了,右边的括号多了
			{
				flag = 0;
				break;
			}
		}
    }

    //比较完之后, 栈不为空: 左边的括号多了
    if( ! IsEmpty( s ) )
    {
		flag = 0;
    }

	//销毁栈
	DestroyStack( s );

    return flag;
}

int main()
{
    char buf[128] = {0};
    scanf("%s", buf );

    int flag = judge_match_brake( buf );
    if( flag )
    {
		printf("Right!\n");
    }
    else 
    {
		printf("Error!\n");
    }
}

        3) 实现 简易计算器的功能 + - * / 
            输入一串数学表达式, 计数这个表达式的结果 
                输入: 1+22*4/2+10-5+20
                输出: 70 

                提示:
                    运算符栈
                    操作数栈

//判断字符 是否为运算符 +-*/
int is_operator_char( char ch )
{
	if( ch == '+' || ch == '-' || ch == '*' || ch == '/' )
	{
		return 1;
	}
	return 0;
}

//运算符的优先级的比较 (ch1是待入栈, ch2是栈顶)
int compare_operator( char ch1, char ch2 )
{
	if( ( ch1 == '*' || ch1 == '/' ) && ( ch2 == '+' || ch2 == '-' ) )
	{
		return 1;
	}
	return 0;
}

//计算 
int calculate( SqStack *sd, SqStack *so )
{
	//1个运算符 + 2个操作数
	int a = 0, b = 0;
	Pop( sd, &b );
	Pop( sd, &a );

	int op = 0;
	Pop( so, &op );

	switch( op )
	{
		case '+' : return a+b;
		case '-' : return a-b;
		case '*' : return a*b;
		case '/' : return a/b;
		default : 
			return 0;
	}
}

int expression( char * str )
{
	if( str == NULL )
	{
		return 0;
	}

    //初始化两个栈(运算符栈+操作数栈) int
	SqStack *sd = InitStack();		//操作数栈
	SqStack *so = InitStack();		//运算符栈

    //解析字符串
    while( *str )
    {
        //如果是数字字符,则合成操作数,然后入栈 
        int d = 0;
        while( *str >= '0' && *str <= '9' )
        {
			d = d*10 + (*str - '0');
			str++;
        }
        Push( sd, d );		//操作数就直接入栈
        
        if( *str == '\0' )
        {
			break;
        }

        //如果是运算符 
        if( is_operator_char( *str ) )
        {
        	while(1) 
        	{
	            //栈为空 || 带入栈的运算符的优先级高于栈顶元素的优先级 --> 入栈
				int top = 0;
				GetTop( so, &top );	//获取栈顶运算符
				
				if( IsEmpty( so ) || compare_operator( *str, top ) )
				{
					Push( so, *str );
					break;
				}
				else 
				{
					 //否则 就出栈(1个运算符+2个操作数) 计算, 再将结果继续入栈
					 int sum = calculate( sd, so );

					 Push( sd, sum );
				}
			}
		}

		str++;
    }

	//将剩余的全部出栈 进行计算
	while( !IsEmpty( so ) ) 
	{
		int sum = calculate( sd, so );
		Push( sd, sum );
	}

	//最后将结果出栈
	int ret = 0;
	Pop( sd, &ret );

    //销毁栈
    DestroyStack( sd );
    DestroyStack( so );

    return ret;
}

int main()
{
	char buf[128] = {0};
	scanf("%s", buf );

	int sum = expression( buf );
	printf("%s = %d\n", buf, sum );
}

    =====================

        中缀表达式 
            就是我们平时所用的标准四则运算表达式, 比如 9+3-2*(1+4)-4/2
            所有的运算符都是在两个操作数的中间

        后缀表达式 (逆波兰式) 
            所有的运算符都是在要运算的操作数的后面出现
            例子: 
                1)
                    9+(3-1)*3+10/2
                    ==> 
                        9 3 1 - 3 * + 10 2 / +        (后缀表达式) 

                2) 
                    2*(3+5)+7/1-4
                    ==> 
                        2 3 5 + * 7 1 / + 4 -           (后缀表达式)
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值