数据结构 - 顺序栈的实行(C语言)

数据结构-顺序栈的实现

1 顺序栈的定义

既然栈是线性表的特例,那么栈的顺序存储其实也是线性表顺序存储的简化,我们简称为顺序栈。线性表是用数组来实现的,对于栈这种只能一头插入删除的线性表来说,用数组哪一端来作为栈顶和栈底比较好?

对,没错,下标为0的一端作为栈底比较好,因为首元素都存在栈底,变化最小,所以让它作栈底。

我们定义一个top变量来指示栈顶元素在数组中的位置,这top就如同中学物理学过的游标卡尺的游标,它可以来回移动,意味着栈顶的top可以变大变小,但无论如何游标不能超出尺的长度。同理,若存储栈的长度为StackSize,则栈顶位置top必须小于StackSize。当栈存在一个元素时,top等于0,因此通常把空栈的判定条件定为top等于-1。

来看栈的结构定义:

typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */

/* 顺序栈结构 */
typedef struct
{
    SElemType data[MAXSIZE];
    int top; /* 用于栈顶指针 */
}SqStack;

若现在有一个栈,StackSize 是5,则栈普通情况、空栈和栈满的情况示意图如下图所示。

  • 栈空时:栈顶指针(top)= -1;
  • 栈满时:栈顶指针(top)=MAXSIZE-1;
  • 栈未满:就是占中存在元素,top指针还未达到MAXSIZE-1。

2 进栈操作

进栈的示意图如下:

实现进栈只需要两步:

  • 判断栈是否已经满了,如果满了当然就入不了栈。
  • 栈顶指针+1,在新栈顶位置存入要进栈的元素。

对于进栈操作push,其代码如下:

/* 插入元素e为新的栈顶元素 */
Status Push(SqStack *S,SElemType e)
{
        if(S->top == MAXSIZE -1) /* 栈满 */
        {
                return ERROR;
        }
        S->top++;               /* 栈顶指针增加一 */
        S->data[S->top]=e;  /* 将新插入元素赋值给栈顶空间 */
        return OK;
}

3 出栈操作

出栈的示意图如下:

实现出栈也只需要两步:

  • 判断栈是否为空,里面没有数据先出栈也没有。
  • 将栈顶元素出栈,栈顶指针-1。

出栈操作pop,代码如下:

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(SqStack *S,SElemType *e)
{
        if(S->top==-1)
                return ERROR;
        *e=S->data[S->top]; /* 将要删除的栈顶元素赋值给e */
        S->top--;               /* 栈顶指针减一 */
        return OK;
}

两者没有涉及到任何循环语句,因此时间复杂度均是O(1)。

4 两栈共享空间

如果我们有两个相同类型的栈,我们为它们各自开辟了数组空间, 极有可能是第一个栈已经满了,再进栈就溢出了,而另—个栈还有很多存储空间空闲。这又何必呢?我们完全可以用一个数组来存储两个栈,只不过需要点小技巧。

我们的做法如下图所示,数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为数组的末端,即下标为数组长度n-1处。这样,两个栈如果增加元素,就是两端点向中间延伸。

其实关键思路是:它们是在数组的两端,向中间靠拢。top1和top2是栈1和栈2的栈顶指针,可以想象,只要它们俩不见面,两个栈就可以一直使用。从这里也就可以分析出来,栈1为空时,就是top1等于-1时;而当top2等于n时,即是栈2为空时,那什么时候栈满呢?

想想极端的情况,若栈2是空栈,栈1的top1等于n-1时,就是栈1满了。反之,当栈1为空栈时,top2等于0时,为栈2满。但更多的情况就是两个栈见面之时,也就是两个指针之间相差1时,即top1 + 1 == top2为栈满。

两栈共享空间的结构的代码如下:

/* 两栈共享空间结构 */
typedef struct
{
    SElemType data[MAXSIZE];
    int top1; /* 栈1栈顶指针 */
    int top2; /* 栈2栈顶指针 */
}SqDoubleStack;

对于两栈共享空间的push方法,我们除了要插入亓素值参数外,还需要有一个判 断是栈1还是栈2的栈号参数stackNumber。插入元素的代码如下:

/* 插入元素e为新的栈顶元素 */
Status Push(SqDoubleStack *S,SElemType e,int stackNumber)
{
    if (S->top1+1==S->top2)    /* 栈已满,不能再push新元素了 */
        return ERROR;
    if (stackNumber==1)            /* 栈1有元素进栈 */
        S->data[++S->top1]=e; /* 若是栈1则先top1+1后给数组元素赋值。 */
    else if (stackNumber==2)    /* 栈2有元素进栈 */
        S->data[--S->top2]=e; /* 若是栈2则先top2-1后给数组元素赋值。 */
    return OK;
}

因为在开始已经判断了是否有栈满的情况,所以后面的top1+1或top2-1是不担心溢出问题的。

对于两栈共享空间的pop方法,参数就只是判断栈1栈2的参数stackNumber,代码如下:

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber)
{
    if (stackNumber==1)
    {
        if (S->top1==-1)
            return ERROR; /* 说明栈1已经是空栈,溢出 */
            *e=S->data[S->top1--]; /* 将栈1的栈顶元素出栈 */
    }
    else if (stackNumber==2)
    {
        if (S->top2==MAXSIZE)
            return ERROR; /* 说明栈2已经是空栈,溢出 */
        *e=S->data[S->top2++]; /* 将栈2的栈顶元素出栈 */
    }
    return OK;
}

事实上,使用这样的数据结构,通常都是当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况。就像买卖股票—样,你买入时,—定是有一个你不知道的人在做卖出操作。这样使用两栈共享空间存储方法才有比较大的意义,否则两个栈都在不停地增长,那很快就会因栈满而溢出了。

当然,这只是针对两个具有相同数据类型的栈的—个设计上的技巧,如果是不相同数据类型的栈,这种办法不但不能更好地处理问题,反而会使问题变得更复杂,大家要注意这个前提。

5 完整实现

#include "stdafx.h"
#include "stdlib.h"   

#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */

typedef int Status;
typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */

/* 顺序栈结构 */
typedef struct
{
    SElemType data[MAXSIZE];
    int top; /* 用于栈顶指针 */
}SqStack;

Status visit(SElemType c)
{
    printf("%d ", c);
    return TRUE;
}

/*  构造一个空栈S */
Status InitStack(SqStack *S)
{
    /* S.data=(SElemType *)malloc(MAXSIZE*sizeof(SElemType)); */
    S->top = -1;
    return TRUE;
}

/* 把S置为空栈 */
Status ClearStack(SqStack *S)
{
    S->top = -1;
    return TRUE;
}

/* 若栈S为空栈,则返回TRUE,否则返回FALSE */
Status StackEmpty(SqStack S)
{
    if (S.top == -1)
        return TRUE;
    else
        return FALSE;
}

/* 返回S的元素个数,即栈的长度 */
int StackLength(SqStack S)
{
    return S.top + 1;
}

/* 若栈不空,则用e返回S的栈顶元素,并返回TRUE;否则返回FALSE */
Status GetTop(SqStack S, SElemType *e)
{
    if (S.top == -1)
        return FALSE;
    else
        *e = S.data[S.top];
    return TRUE;
}

/* 插入元素e为新的栈顶元素 */
Status Push(SqStack *S, SElemType e)
{
    if (S->top == MAXSIZE - 1) /* 栈满 */
    {
        return FALSE;
    }
    S->top++;               /* 栈顶指针增加一 */
    S->data[S->top] = e;  /* 将新插入元素赋值给栈顶空间 */
    return TRUE;
}

/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回TRUE;否则返回FALSE */
Status Pop(SqStack *S, SElemType *e)
{
    if (S->top == -1)
        return FALSE;
    *e = S->data[S->top];   /* 将要删除的栈顶元素赋值给e */
    S->top--;               /* 栈顶指针减一 */
    return TRUE;
}

/* 从栈底到栈顶依次对栈中每个元素显示 */
Status StackTraverse(SqStack S)
{
    int i;
    i = 0;
    while (i <= S.top)
    {
        visit(S.data[i++]);
    }
    printf("\n");
    return TRUE;
}

int main()
{
    int j;
    SqStack s;
    int e;
    if (InitStack(&s) == TRUE)
        for (j = 1; j <= 10; j++)
            Push(&s, j);
    printf("栈中元素依次为:");
    StackTraverse(s);
    Pop(&s, &e);
    printf("弹出的栈顶元素 e=%d\n", e);
    printf("栈空否:%d(1:空 0:否)\n", StackEmpty(s));
    GetTop(s, &e);
    printf("栈顶元素 e=%d 栈的长度为%d\n", e, StackLength(s));
    ClearStack(&s);
    printf("清空栈后,栈空否:%d(1:空 0:否)\n", StackEmpty(s));

    return 0;
}

/*
输出结果:

栈中元素依次为:1 2 3 4 5 6 7 8 9 10
弹出的栈顶元素 e=10
栈空否:0(1:空 0:否)
栈顶元素 e=9 栈的长度为9
清空栈后,栈空否:1(1:空 0:否)
*/

原文地址:https://www.cnblogs.com/linuxAndMcu/p/10327771.html

时间: 2024-08-09 06:35:19

数据结构 - 顺序栈的实行(C语言)的相关文章

数据结构 - 链栈的实行(C语言)

数据结构-链栈的实现 1 链栈的定义 现在来看看栈的链式存储结构,简称为链栈. 想想看栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?由于单链表有头指针,而栈顶指针也是必须的,那干吗不让它俩合二为一呢,所以比较好的办法是把栈顶放在单链表的头部(如下图所示).另外,都已经有了栈顶在头部了,单链表中比较常用的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的. 对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是 top=NULL 的时候. 链栈的结构代码如下: /* 链栈

数据结构 - 顺序队列的实行(C语言)

数据结构-顺序队列的实现 1 顺序队列的定义 线性表有顺序存储和链式存储,队列作为一种特殊的线性表,也同样存在这两种存储方式.我们先来看队列的顺序存储结构. 队列的顺序储存结构:用数组存储队列,为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,当front=rear时,为空队列,结构如下图所示. 假设是长度为5的数组,初始状态,空队列如下图左所示,front与 rear指针均指向下标为0的位置.然后入队a

数据结构--顺序栈的实现

最近在看严蔚敏的数据结构,以下是参照 http://blog.csdn.net/WLxinliang/article/details/52894338 手写的顺序栈的实现代码: 1.头文件定义了常数项 //constant.h #include<iostream> #include<string.h> #include<stdlib.h> #include<malloc.h> using namespace std; #define TRUE 1 #defi

数据结构-顺序栈

1 #include <iostream> 2 #include <stdlib.h> 3 using namespace std; 4 5 #define maxSize 30 6 7 typedef struct 8 { 9 int data[maxSize]; 10 int top; 11 }SqStack; 12 13 void InitStack(SqStack &S) 14 { 15 S.top=-1; 16 } 17 18 int IsEmpty(SqStac

数据结构——顺序栈及其操作

1 #include<iostream> 2 using namespace std; 3 4 typedef int SElemType; 5 typedef int Status; 6 #define OK 1 7 #define ERROR 0 8 9 10 #define MAXSIZE 100 //栈的最大空间 11 12 13 //顺序栈的存储结构 14 typedef struct SqStack 15 { 16 SElemType *base; //栈底指针 17 SElemT

数据结构-顺序栈(进栈 出栈)

#include<stdio.h> #define MaxSize 10 typedef struct SqStack{ int data[MaxSize]; int top ; }SqStack; //初始化顺序栈 void initStack(SqStack &S){ S.top = -1; } //判断栈是否为空 /*栈理论上不存在为满的情况,取决于内存大小*/ int isEmpty(SqStack S){ if(S.top == -1){//top为1表示为空 return

数据结构:C_顺序栈的实现

数据结构顺序栈的实现(C语言版) 1.写在前面 栈是一种遵循元素先进(Push)后出(Pop)规则的线性表,它的实现可以用数组或者链表. ..... 2.代码分解 2.1对栈的结构定义: typedef struct { int *base; int *top; int stacksize; }SqStack; |说明: 1.base表示栈底指针,在判断出栈.初始化和重新分配空间的时候需要用到. 2.top表示栈顶指针,是栈最关键和核心的组成,入栈时top向上移动,出栈时top向下移动. 3.此

顺序循环队列的c语言实现

1. 循环队列的顺序存储结构 typedef struct { QElemType data[MAXSIZE]; int front; /* 头指针 */ int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */ }SqQueue; 2. 初始化一个空队列Q Status InitQueue(SqQueue *Q) { Q->front=0; Q->rear=0; return OK; } 3.将Q清为空队列 Status ClearQueue(SqQueue *Q) {

数据结构C语言—顺序栈案例—十进制转二进制

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "SeqStack.h" 4 5 void conversion(int); 6 SeqStack seq; 7 int main() 8 { 9 int n; 10 scanf("%d",&n); 11 conversion(n); 12 return 0; 13 } 14 15 void conversion(int N)