栈的顺序与链式存储结构及实现(C语言)

目录

  • 1 栈的定义和特点
  • 2 栈的表示和操作的实现
    • 2.1 顺序栈的表示和实现
      • 2.1.1 栈的顺序存储表示
      • 2.1.2 顺序栈的定义与基本操作函数源码
    • 2.2 链栈的表示和实现
      • 2.2.1 栈的链式存储表示
      • 2.2.2 链栈的定义与基本操作函数源码
  • 3 分析与总结
    • 3.1 分析
      • 3.1.1 对比一下顺序栈与链栈
      • 3.1.2 对比顺序栈与顺序表、链栈与链表
    • 3.2 总结

1 栈的定义和特点

是限定仅在表尾进行插入和删除操作的线性表。因此,对栈来说,表尾端有特殊含义,称为栈顶。不含元素的空表称为空栈。栈的修改是按后进先出的原则进行的,又被称为后进先出的线性表/

2 栈的表示和操作的实现

2.1 顺序栈的表示和实现

2.1.1 栈的顺序存储表示

顺序栈是指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。通常习惯的做法是以top=-1,即指针指向数组之外表示空栈。

2.1.2 顺序栈的定义与基本操作函数源码

//
// Created by SongBy on 2022/03/25.
//

#include 
#include 

#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define TRUE 1
#define FALSE 0
#define MAXSIZE 10/*顺序栈可能达到的最大长度,存储空间初始分配量*/

typedef int Status;
typedef int ElemType;/*ElemType是栈内的元素类型,根据实际情况而定,此处假设为int*/

typedef struct {
    ElemType *data;
    int top;/*用于栈顶指针*/
}SqStack;

/*操作结果:构造一个空栈S*/
Status InitStack(SqStack *S){
    S->data = (ElemType *) malloc(sizeof(ElemType) * MAXSIZE);/*申请连续的MAXSIZE长度的ElemType类型数组,并让data指向这个空间*/
    if (!S->data)/*判断空间是否申请成功*/
        exit(OVERFLOW);
    S->top = -1;/*空栈无元素,栈顶指针指向栈外*/
    return OK;
}

/*初始条件:栈S已存在*/
/*操作结果:栈S被销毁*/
Status DestroyStack(SqStack *S){
    free(S->data);/*释放data指向的空间*/
    S->data = NULL;
    S->top = -1;/*重置栈顶指针*/
    return OK;
}

/*初始条件:栈S已存在*/
/*操作结果:栈S清为空栈*/
Status ClearStack(SqStack *S){
    S->top = -1;/*重置栈顶指针*/
    return OK;
}

/*初始条件:栈S已存在*/
/*操作结果:若栈S为空栈,则返回true,否则返回false*/
Status StackEmpty(SqStack S){
    if(S.top!=-1)/*栈顶指针是否指向栈外*/
        return FALSE;
    return TRUE;
}

/*初始条件:栈S已存在*/
/*操作结果:返回S的元素个数,即栈的长度*/
int StackLength(SqStack S){
    return S.top+1;/*元素所在下标+1*/
}

/*初始条件:栈S已存在且非空*/
/*操作结果:返回S的栈顶元素,不修改栈顶指针*/
ElemType GetTop(SqStack S){
    if(S.top == -1)/*栈空*/
        return ERROR;
    return S.data[S.top];
}

/*初始条件:栈S已存在*/
/*操作结果:插入元素e为新的栈顶元素*/
Status Push(SqStack *S, ElemType e){
    if(S->top == MAXSIZE -1)/*栈满*/
        return ERROR;
    S->top++;/*栈顶指针加1*/
    S->data[S->top] = e;/*将新插入元素赋值给栈顶元素*/
    return OK;
}

/*初始条件:栈S已存在且非空*/
/*操作结果:删除S的栈顶元素,并用e返回其值*/
Status Pop(SqStack *S, ElemType *e){
    if(S->top == -1)/*栈空*/
        return ERROR;
    *e = S->data[S->top];/*将要删除的栈顶元素赋值给e*/
    S->top--;/*栈顶指针减1*/
    return OK;
}

/*初始条件:栈S已存在且非空*/
/*操作结果:从栈底到栈顶依次对S的每个数据元素进行访问*/
Status StackTraverse(SqStack S){
    for(int i = 0;i<=S.top;i++)/*从栈底遍历到栈顶*/
        printf("%d",S.data[i]);/*此处以输出代替访问*/
    return OK;
}

2.2 链栈的表示和实现

2.2.1 栈的链式存储表示

链栈是指采用链式存储结构实现的栈。通常链栈用单链表来表示。链栈的结点结构与单链表相同,在此用StackNode表示。

2.2.2 链栈的定义与基本操作函数源码

//
// Created by SongBy on 2022/03/25.
//

#include 
#include 

#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define TRUE 1
#define FALSE 0
#define MAXSIZE 10/*顺序栈可能达到的最大长度,存储空间初始分配量*/

typedef int Status;
typedef int ElemType;/*ElemType是栈内的元素类型,根据实际情况而定,此处假设为int*/

typedef struct StackNode{
    ElemType data;/*数据域*/
    struct StackNode *next;/*指针域*/
}StackNode,*LinkStackPtr;/*栈结点*/

typedef struct LinkStack{
    LinkStackPtr top;/*栈顶指针*/
    int count;/*栈内元素计数器*/
}LinkStack;/*链栈*/

/*操作结果:构造一个空栈S*/
Status InitStack(LinkStack *S){
    S->top = NULL;/*栈顶指向空*/
    S->count = 0;/*栈内元素置为0*/
    return OK;
}

/*初始条件:栈S已存在*/
/*操作结果:栈S被销毁*/
Status DestroyStack(LinkStack *S){
    LinkStackPtr p;
    while(S->top)/*若栈不为空则删除栈顶元素,栈指针后移*/
    {
        p = S->top;
        S->top = S->top->next;
        free(p);
    }
    S->count = 0;/*重置计数器*/
    return OK;
}

/*初始条件:栈S已存在*/
/*操作结果:栈S清为空栈*/
Status ClearStack(LinkStack *S){
    LinkStackPtr p;
    while(S->top)/*若栈不为空则栈顶元素出栈,栈指针后移*/
    {
        p = S->top;
        S->top = S->top->next;
        free(p);
    }
    S->count = 0;/*重置计数器*/
    return OK;
}

/*初始条件:栈S已存在*/
/*操作结果:若栈S为空栈,则返回true,否则返回false*/
Status StackEmpty(LinkStack S){
    if(S.count == 0)/*栈内无元素*/
        return TRUE;
    return FALSE;
}

/*初始条件:栈S已存在*/
/*操作结果:返回S的元素个数,即栈的长度*/
int StackLength(LinkStack S){
    return S.count;
}

/*初始条件:栈S已存在且非空*/
/*操作结果:返回S的栈顶元素,不修改栈顶指针*/
ElemType GetTop(LinkStack S){
    if(S.count == 0)/*栈空*/
        return ERROR;
    return S.top->data;/*返回栈顶元素*/
}

/*初始条件:栈S已存在*/
/*操作结果:插入元素e为新的栈顶元素*/
Status Push(LinkStack *S, ElemType e){
    LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
    s->data = e;
    s->next = S->top;/*把当前栈顶元素赋值给新结点的直接后继*/
    S->top = s;/*将新的结点s赋值给栈顶指针*/
    S->count++;
    return OK;
}

/*初始条件:栈S已存在且非空*/
/*操作结果:删除S的栈顶元素,并用e返回其值*/
Status Pop(LinkStack *S, ElemType *e){
    LinkStackPtr p;
    if(StackEmpty(*S))/*栈空*/
        return ERROR;
    *e = S->top->data;
    p = S->top;/*将栈顶结点赋值给p*/
    S->top = S->top->next;/*栈顶指针下移一位,指向后一结点*/
    free(p);/*释放结点p*/
    S->count--;
    return OK;
}

/*初始条件:栈S已存在且非空*/
/*操作结果:从栈底到栈顶依次对S的每个数据元素进行访问*/
Status StackTraverse(LinkStack S){
    /*思路:构造一个新栈,将旧栈元素按出栈顺序入新栈,再从新栈栈顶遍历访问,即可得到从旧栈底到栈顶访问的顺序*/
    LinkStack ST;/*构造新栈*/
    InitStack(&ST);
    LinkStackPtr p;

    p = S.top;
    while(p)/*旧栈元素出栈并入新栈*/
    {
        LinkStackPtr s = (LinkStackPtr) malloc(sizeof (StackNode));
        s->data = p->data;
        s->next = ST.top;
        ST.top = s;
        ST.count++;
        p = p->next;
    }

    p = ST.top;
    while(p)/*遍历新栈*/
    {
        printf("%d",p->data);
        p = p->next;
    }
    return OK;
}

3 分析与总结

3.1 分析

3.1.1 对比一下顺序栈与链栈

时间性能:
它们在时间复杂度是一样的,均为O(1)。

空间性能:

  • 顺序栈需要事先确定一个固定的长度,可能会存在内存空间浪费的问题,但它的优势是存取时定位很方便。
  • 链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对栈的长度无限制。

3.1.2 对比顺序栈与顺序表、链栈与链表

  • 顺序栈:由于顺序栈的插入和删除只在栈顶进行,因此顺序栈的基本操作比顺序表要简单的多。
  • 链栈:由于栈的主要操作是在栈顶插入和删除,显然以链表的头部作为栈顶是最方便的,而且没必要像单链表那样为了操作方便附加一个头结点。

3.2 总结

如果栈的使用过程中元素变化不可预料,有时很大有时很小,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。

你可能感兴趣的:(#,Stack,数据结构(C语言版),数据结构,c语言)