七、【栈和队列】栈

本文围绕栈这一数据结构展开,介绍了栈的基本概念,指出其是受限的线性结构,具有“后入先出”特性。还阐述了栈的顺序表示(包括顺序栈、共享栈)和链式表示及各自实现方式,如顺序栈有静态和动态之分,共享栈可更好利用空间,链栈便于多栈共享且无栈满上溢情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

栈 Stack

在第一节中我们提到,数据结构的逻辑结构分为线性结构和非线性结构,其中线性结构又可分为一般线性结构、受限线性结构和推广结构。栈和队列从数据结构上看和线性表很类似,但是他们在普通线性表的基础上有额外的要求限制,因此说他们是受限的线性结构。这一节我们主要来了解什么是栈。
在这里插入图片描述


1 栈的基本概念

1.1 栈的定义

是只允许在表尾进行插入或删除操作的线性表。从定义上来看,栈依然属于线性表,只是它的操作仅限在表的一端执行。允许变化的一端通常称为栈顶,一般用表尾构成;而始终固定的一端被称为栈底,由表头构成。
在这里插入图片描述
由于栈只能在表尾端进行插入或删除操作,因此,最后插入的元素位于栈顶,在删除时会被第一个弹出栈表,就像一叠盘子,最后放置的盘子位于最顶端,也会被最先拿走。例如上图中的元素3是最后一个入栈的,也是第一个出栈的,栈的这种特性被称为“后入先出”(Last In First Out,LIFO)。


1.2 栈的基本操作

由于栈只有表尾一端可以操作,所以栈的基本操作大部分都是围绕表尾进行的。栈的主要操作如下:

主要操作操作结果
InitStack(&S)初始化一个空栈 S。
DestroyStack(&S)销毁栈,并释放栈 S 占用的存储空间。
ClearStack(&S)将 S 清空为空栈。
StackEmpty(S)判断栈 S 是否为空,若为空则返回 true,否则返回 false。
StackLength(S)返回栈 S 的元素个数,即栈的长度。
GetTop(S, &x)读取栈顶元素,若栈非空,则用 x 返回栈顶元素。
Push(&S, x)入栈,若栈 S 未满,则将 x 插入,使之成为新栈顶。
Pop(&S, &x)出栈,若栈 S 非空,则弹出栈顶元素,并用 x 返回。


2 栈的顺序表示和实现

栈是一种线性表,属于逻辑结构。同其他的线性表一样,栈也有两种不同的存储方式:顺序存储和链式存储。


2.1 顺序栈

顺序栈是采用顺序存储的栈,利用一组地址连续的存储单元来存放自栈顶到栈底的数据元素,同时设置一个指针指向栈顶元素。
在这里插入图片描述
静态顺序栈使用静态数组来实现(如上图所示),使用一个整型变量 top 来记录栈顶元素的位置,即元素在数组的下标。由于数组的第一位元素下标为0,因此一般将 top 指针指向 -1 来表示空栈。当有元素时, top 指针指向栈顶元素。
在这里插入图片描述
动态顺序栈使用动态数组来实现(如上图所示),和静态顺序栈最大的区别就是栈顶指针的表示不同。动态顺序栈一般有两个指针 base 和 top,一个指向栈底,一个指向栈顶。当栈表为空时,base 和 top 指向同一个位置;当栈表不为空时,top 始终指向栈顶元素的下一位(逻辑上的下一位,在图上表示则为栈顶元素的上方)。此时,栈的有效长度可以用 top-base 来获取。


2.2 顺序栈的实现

2.2.1 顺序栈类型定义
/********** 动态顺序栈的类型定义 **********/
#define INITSIZE 10
#define INCREMENT 10

typedef int ElemType;
typedef struct {
    ElemType *base; // 栈底指针 
    ElemType *top;  // 栈顶指针
    int capacity;   // 栈当前的最大容量
} SqStack;

2.2.2 主要操作的实现

栈的主要操作都是围绕表尾进行的,所以重点是处理栈顶指针 top 。详情见附录。


2.3 共享栈

共享栈是一种特殊的顺序栈,利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数组,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中部延伸,如下图所示:
在这里插入图片描述
和静态顺序栈一样,如果栈表不为空,栈顶指针指向栈顶元素;如果栈表为空,他们的栈顶指针分别等于 -1 和 MaxSize。当插入新元素时,都需要先移动栈顶指针,再赋值。当两个栈顶指针相邻时(B.top - A.top = 1),即代表共享栈已满。

共享栈的优点是可以更好地利用存储空间,只有在整个存储空间都被占满时才发生上溢(超出最大范围)。


2.3.1 共享栈类型定义
/********** 共享栈类型定义 **********/
#define MAXSIZE 10
typedef int ElemType;
typedef struct {
    ElemType data[MAXSIZE];
    int top1;                
    int top2;
} SharedStack;

2.3.2 主要操作的实现

共享栈的插入和删除操作需要先选择待操作的栈是哪一个,再开始执行操作。其余部分和顺序栈的操作一致,详情见附录。



3 栈的链式表示和实现

采用链式存储的栈称为链栈。链栈的优点是便于多个栈共享存储空间,且不存在栈满上溢的情况。

由于栈的操作仅在表的一端完成,正好符合单链表可以在表头快速执行插入和删除操作的特点,所以一般将栈顶指针指向单链表的表头。此时,链栈就是一个只能在表头进行操作的单链表。
在这里插入图片描述
链栈的实现基本和单链表一致,所以这里不再赘述。实现详情见附录。



相关章节

第一节 【绪论】数据结构的基本概念
第二节 【绪论】算法和算法评价
第三节 【线性表】线性表概述
第四节 【线性表】线性表的顺序表示和实现
第五节 【线性表】线性表的链式表示和实现
第六节 【线性表】双向链表、循环链表和静态链表
第七节 【栈和队列】栈
第八节 【栈和队列】栈的应用
第九节 【栈和队列】栈和递归
第十节 【栈和队列】队列



附录

顺序栈的实现

静态顺序栈的实现

/*
 * File name: StaticSqStack.h
 * -----------------------
 * 静态顺序栈的实现。
 */

#ifndef _STATIC_SEQUENTIAL_STACK_h_
#define _STATIC_SEQUENTIAL_STACK_h_

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;


/********** 静态顺序栈的类型定义 **********/
#define MAXSIZE 10
typedef int Elemtype;
typedef struct {
    Elemtype data[MAXSIZE];
    int top;                // 栈顶指针,指向-1表示空栈
} SqStack;


/********** 静态顺序栈的实现 **********/
/*
 * Function: 初始化操作
 * ----------------------------
 * 初始化一个空栈S
 */
void InitStack(SqStack &S){
    S.top = -1; // 指针归零
}

/*
 * Function: 销毁栈操作
 * ----------------------------
 * 静态数组的销毁不需要手动执行,因此无需销毁操作。
 */
void DestoryStack(SqStack &S){}

/*
 * Function: 清空栈操作
 * ----------------------------
 * 将S清为空栈。
 */
void ClearStack(SqStack &S){
    S.top = -1; // 指针归零
}

/*
 * Function: 判空操作
 * ----------------------------
 * 判断栈S是否为空,若为空则返回true,否则返回false。
 */
bool StackEmpty(SqStack S){
    return S.top==-1;
}

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

/*
 * Function: 读取栈顶元素操作
 * ----------------------------
 * 读取栈顶元素,若栈非空,则用e返回栈顶元素。
 */
bool GetTop(SqStack S, Elemtype &e){
    if (StackEmpty(S)){
        return false;
    }
    e = S.data[S.top];
    return true;
}

/*
 * Function: 入栈操作
 * ----------------------------
 * 若栈S未满,则将e插入,使之成为新栈顶。
 */
bool Push(SqStack &S, Elemtype e){
    if (StackLength(S)<MAXSIZE){        // 如果还有空间,则继续插入操作
        S.data[++S.top] = e;
        return true;
    } else {
        printf("Out of space!\n");      
        return false;
    }
}

/*
 * Function: 出栈操作
 * ----------------------------
 * 若栈S非空,则弹出栈顶元素,并用e返回。
 */
bool Pop(SqStack &S, Elemtype &e){
    if (StackEmpty(S)){
        return false;
    } else {
        e = S.data[S.top--];
        return true;
    }
}

/*
 * Function: 输出操作
 * ----------------------------
 * 按从表头到表尾的顺序输出
 */
void Print(SqStack S){
    for (int i=0;i<=S.top;i++){
        printf("%d ", S.data[i]);
    }
    printf("\n");
}

#endif // _STATIC_SEQUENTIAL_STACK_h_

动态顺序栈的实现

/*
 * File name: DynamicSqStack.h
 * -----------------------
 * 动态顺序栈的实现
 */

#ifndef _DYNAMIC_SEQUENTIAL_STACK_h_
#define _DYNAMIC_SEQUENTIAL_STACK_h_

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;


/********** 动态顺序栈的类型定义 **********/
#define INITSIZE 10
#define INCREMENT 10

typedef int ElemType;
typedef struct {
    ElemType *base; // 栈底指针 
    ElemType *top;  // 栈顶指针
    int capacity;   // 栈当前的最大容量
} SqStack;


/********** 主要操作的实现 **********/
/*
 * Function: 初始化操作
 * ----------------------------
 * 初始化一个空栈S
 */
void InitStack(SqStack &S){
    S.capacity = INITSIZE;              // 设定初始最大容量
    S.base = new ElemType[S.capacity];  // 创建空栈表
    S.top = S.base;                     // 将栈顶指针归零
}

/*
 * Function: 销毁栈操作
 * ----------------------------
 * 销毁栈,并释放栈S占用的存储空间。
 */
void DestoryStack(SqStack &S){
    S.top=S.base;
    delete[] S.base;
    S.capacity = 0;
}

/*
 * Function: 清空栈操作
 * ----------------------------
 * 将S清为空栈。
 */
void ClearStack(SqStack &S){
    S.top = S.base;
}

/*
 * Function: 判空操作
 * ----------------------------
 * 判断栈S是否为空,若为空则返回true,否则返回false。
 */
bool StackEmpty(SqStack S){
    return S.top==S.base;
}

/*
 * Function: 求栈长操作
 * ----------------------------
 * 返回栈S的元素个数,即栈的长度。
 */
int StackLength(SqStack S){
    return S.top-S.base;
}

/*
 * Function: 读取栈顶元素操作
 * ----------------------------
 * 读取栈顶元素,若栈非空,则用e返回栈顶元素。
 */
bool GetTop(SqStack S, ElemType &e){
    if (StackEmpty(S)){
        return false;
    }
    e = *(S.top-1);     // 指针向前移动一位,指向栈顶元素
    return true;
}

/*
 * Function: 入栈操作
 * ----------------------------
 * 若栈S未满,则将e插入,使之成为新栈顶。
 */
bool Push(SqStack &S, ElemType e){
    if (StackLength(S)>=S.capacity){        // 如果没有空间,则首先进行扩容操作
        // 重新分配base指向内存的大小
        S.base = (ElemType *) realloc (S.base, (S.capacity+INCREMENT) * sizeof(ElemType));
        S.top = S.base + S.capacity;
        S.capacity += INCREMENT;   
    }
    *S.top++ = e;       // ++优先级高于*,所以可分解为:*S.top = e; S.top = S.top+1;         
    return true;
}

/*
 * Function: 出栈操作
 * ----------------------------
 * 若栈S非空,则弹出栈顶元素,并用e返回。
 */
bool Pop(SqStack &S, ElemType &e){
    if (StackEmpty(S)){
        return false;
    } else {
        e = *--S.top;
        return true;
    }
}

/*
 * Function: 输出操作
 * ----------------------------
 * 按从表头到表尾的顺序输出
 */
void Print(SqStack S){
    for (int i=0;i<StackLength(S);i++){
        printf("%d ", S.base[i]);
    }
    printf("\n");
}


#endif // _DYNAMIC_SEQUENTIAL_STACK_h_

顺序栈检测程序

/*
 * File name: SenquentialStackTest.cpp
 * -----------------------
 * 检测静态顺序栈和动态顺序栈
 */

// 引用静态顺序栈
// #include "StaticSqStack.h"       

// 引用动态顺序栈
#include "DynamicSqStack.h"

int main(){
    SqStack S;
    InitStack(S);

    int n;
    ElemType e;

    char helpInfo[] =
        "*****************************\n"
        "Sequential Stack check: \n"
        "\t-2-Quit\n"
        "\t1-Push\n"
        "\t2-Pop\n"
        "\t3-Empty check\n"
        "\t4-Get Length\n"
        "\t5-Get top\n"
        "\t6-Clear\n"
        "\t7-Print\n"
        "*****************************\n";

    
    while (n!=-2){
        printf(helpInfo);
        scanf("%d", &n);
        switch(n){
            case 1:
                printf("Enter the new value: ");
                scanf("%d", &e);
                Push(S, e);
                break;
            case 2:
                if (Pop(S, e)){
                    printf("The last value %d is popped.\n", e);
                } else {
                    printf("The Stack is empty.\n");
                }
                break;
            case 3:
                if (StackEmpty(S)){
                    printf("The Stack is empty.\n");
                } else {
                    printf("The Stack is not empty.\n");
                }
                break;
            case 4:
                printf("The length of Stack is: %d\n", StackLength(S));
                break;
            case 5:
                if (GetTop(S, e)){
                    printf("The top value is: %d\n", e);
                } else {
                    printf("The Stack is empty.\n");
                }
                break;
            case 6:
                ClearStack(S);
                printf("All cleared.\n");
                break;
            case 7:
                printf("Stack is: ");
                Print(S);
                break;
        }
    }
	DestoryStack(S);
	
    return 0;
}

共享栈的实现

共享栈的实现

/*
 * Filename: SharedStack.h
 * -----------------------
 * 实现共享栈。
 */

#ifndef _SHARED_STACK_h_
#define _SHARED_STACK_h_

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

/********** 共享栈类型定义 **********/
#define MAXSIZE 10
typedef int ElemType;
typedef struct {
    ElemType data[MAXSIZE];
    int top1;                
    int top2;
} SharedStack;


/********** 共享栈的实现 **********/
/*
 * Function: 初始化操作
 * ----------------------------
 * 初始化一个空的共享栈S
 */
void InitStack(SharedStack &S){
    S.top1 = -1;        // 指针归零
    S.top2 = MAXSIZE;
}

/*
 * Function: 清空栈操作
 * ----------------------------
 * 将S清为空栈。
 */
void ClearStack(SharedStack &S){
    InitStack(S);       // 指针归零
}

/*
 * Function: 判满操作
 * ----------------------------
 * 判断栈S是否已满,若已满则返回true,否则返回false。
 */
bool StackFull(SharedStack S){
    return S.top2-S.top1==1;
}

/*
 * Function: 求栈长操作
 * ----------------------------
 * 返回栈S的元素个数,即总的栈长。
 */
int StackLength(SharedStack S){
    return (S.top1+1 + MAXSIZE-S.top2);
}

/*
 * Function: 入栈操作
 * ----------------------------
 * 若栈S未满,则选择一个链表将e插入,使之成为新栈顶。
 * 使用变量n(0或1)来指示选择插入的栈。
 */
bool Push(SharedStack &S, ElemType e, int n){
    if (!StackFull(S)){        // 如果还有空间,则继续插入操作
        if (n){                // 如果n为1,插入反向增长的栈
            S.data[--S.top2] = e;
        } else {
            S.data[++S.top1] = e;
        }
        return true;
    } else {
        printf("Out of space!\n");      
        return false;
    }
}

/*
 * Function: 出栈操作
 * ----------------------------
 * 若栈S非空,则选择一个栈弹出栈顶元素,并用e返回。
 * 使用变量n(0或1)来指示选择插入的栈。
 */
bool Pop(SharedStack &S, ElemType &e, int n){
    if (n){                     // 如果n为1,选择反向增长的栈
        if (S.top2==MAXSIZE){   // 如果该栈为空,返回false
            return false;
        } else {
            e = S.data[S.top2++];
            return true;
        }
    } else {                    // 如果n为0,选择正向增长的栈
        if (S.top1==-1){
            return false;
        } else {
            e = S.data[S.top1--];
            return true;
        }
    }
}

/*
 * Function: 输出操作
 * ----------------------------
 * 将两个表的元素分别按栈内的顺序输出。
 */
void Print(SharedStack S){
    for (int i=0;i<=S.top1;i++){
        printf("%d ->", S.data[i]);
    }
    printf("\n");
    for (int j=MAXSIZE-1;j>=S.top2;j--){
        printf("%d ->", S.data[j]);
    }
    printf("\n");
}


#endif // _SHARED_STACK_h_

共享栈检测程序

/*
 * Filename: SharedStack.cpp
 * -----------------------
 * 检测共享栈的各个方法。
 */

#include "SharedStack.h"

int main(){
    SharedStack S;
    InitStack(S);

    int n;
    ElemType e;

    char helpInfo[] =
        "*****************************\n"
        "Sequential Stack check: \n"
        "\t-2-Quit\n"
        "\t1-Push\n"
        "\t2-Pop\n"
        "\t3-Full check\n"
        "\t4-Get Length\n"
        "\t5-Clear\n"
        "\t6-Print\n"
        "*****************************\n";

    
    while (n!=-2){
        printf(helpInfo);
        scanf("%d", &n);
        switch(n){
            case 1:
                printf("Enter the new value: ");
                scanf("%d", &e);
                printf("Choose a stack<0/1>: ");
                scanf("%d", &n);
                Push(S, e, n);
                break;
            case 2:
                printf("Choose a stack<0/1>: ");
                scanf("%d", &n);
                if (Pop(S, e, n)){
                    printf("The last value %d is popped.\n", e);
                } else {
                    printf("The Stack is empty.\n");
                }
                break;
            case 3:
                if (StackFull(S)){
                    printf("The Stack is full.\n");
                } else {
                    printf("The Stack is not full.\n");
                }
                break;
            case 4:
                printf("The length of Stack is: %d\n", StackLength(S));
                break;
            case 5:
                ClearStack(S);
                printf("All cleared.\n");
                break;
            case 6:
                printf("Stack is: \n");
                Print(S);
                break;
        }
    }
    return 0;
}

链栈的实现

链栈的实现

/*
 * Filename: LinkedStack.h
 * -----------------------
 * 使用单链表来实现链栈
 */

#ifndef _SINGLE_LINKED_LIST_h_
#define _SINGLE_LINKED_LIST_h_

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;


/********** 链栈的结点的类型定义 **********/
typedef int ElemType;
typedef struct LNode{
    ElemType data;              // 数据域
    struct LNode *next;         // 指针域
} LNode, *LinkedStack;


/********** 主要操作的实现 **********/
/*
 * Function: 初始化操作
 * ----------------------------
 * 初始化一个空栈
 */
void InitStack(LinkedStack &S){
    S = new LNode;
    S->next = NULL;
}

/*
 * Function: 清空栈操作
 * ----------------------------
 * 将S清为空栈。
 */
void ClearStack(LinkedStack &S){
    LinkedStack q;              // 指向待删除结点
    while (S->next!=NULL){      // 不断删除第一个结点
        q = S->next;
        S->next = q->next;
        delete q;
    }
}

/*
 * Function: 销毁栈操作
 * ----------------------------
 * 销毁栈,并释放栈 S 占用的存储空间。
 */
void DestroyStack(LinkedStack &S){
    ClearStack(S);
    delete S;
}



/*
 * Function: 判空操作
 * ----------------------------
 * 判断栈S是否为空,若为空则返回true,否则返回false。
 */
bool StackEmpty(LinkedStack S){
    return !S->next;
}

/*
 * Function: 求栈长操作
 * ----------------------------
 * 返回栈S的元素个数,即栈的长度。
 */
int StackLength(LinkedStack S){
    int count=0;
    LinkedStack tmp=S;
    while (tmp->next!=NULL){
        tmp = tmp->next;
        count++;
    }
    return count;
}

/*
 * Function: 读取栈顶元素操作
 * ----------------------------
 * 读取栈顶元素,若栈非空,则用e返回栈顶元素。
 */
bool GetTop(LinkedStack S, ElemType &e){
    if (StackEmpty(S)){
        return false;
    }
    e = S->next->data;
    return true;
}

/*
 * Function: 入栈操作
 * ----------------------------
 * 使用头插法将e插入,使之成为新栈顶。
 */
void Push(LinkedStack &S, ElemType e){
    LNode *n = new LNode;
    n->data = e;
    n->next = S->next;
    S->next = n;
}

/*
 * Function: 出栈操作
 * ----------------------------
 * 若栈S非空,则弹出栈顶元素,并用e返回。
 */
bool Pop(LinkedStack &S, ElemType &e){
    LinkedStack q;

    if (StackEmpty(S)){
        return false;
    } else {
        q = S->next;
        e = q->data;
        S->next = q->next;
        delete q;
        return true;
    }
}

/*
 * Function: 输出操作
 * ----------------------------
 * 由于单链表的特性,更方便按从栈尾到栈头的顺序输出。
 * 后续可以用递归法来正向输出。
 */
void Print(LinkedStack S){
    LinkedStack tmp=S;
    while (tmp->next!=NULL){
        tmp = tmp->next;
        printf("%d <- ", tmp->data);
    }
    printf("base\n");
}


#endif // _SINGLE_LINKED_LIST_h_

链栈的检测程序

/*
 * Filename: LinkedStackTest.cpp
 * -----------------------
 * 用来检测链栈各方法。
 */

#include "LinkedStack.h"

int main(){
    LinkedStack S;
    InitStack(S);

    int n;
    ElemType e;

    char helpInfo[] =
        "*****************************\n"
        "Sequential Stack check: \n"
        "\t-2-Quit\n"
        "\t1-Push\n"
        "\t2-Pop\n"
        "\t3-Empty check\n"
        "\t4-Get Length\n"
        "\t5-Get top\n"
        "\t6-Clear\n"
        "\t7-Print\n"
        "*****************************\n";

    
    while (n!=-2){
        printf(helpInfo);
        scanf("%d", &n);
        switch(n){
            case 1:
                printf("Enter the new value: ");
                scanf("%d", &e);
                Push(S, e);
                break;
            case 2:
                if (Pop(S, e)){
                    printf("The last value %d is popped.\n", e);
                } else {
                    printf("The Stack is empty.\n");
                }
                break;
            case 3:
                if (StackEmpty(S)){
                    printf("The Stack is empty.\n");
                } else {
                    printf("The Stack is not empty.\n");
                }
                break;
            case 4:
                printf("The length of Stack is: %d\n", StackLength(S));
                break;
            case 5:
                if (GetTop(S, e)){
                    printf("The top value is: %d\n", e);
                } else {
                    printf("The Stack is empty.\n");
                }
                break;
            case 6:
                ClearStack(S);
                printf("All cleared.\n");
                break;
            case 7:
                printf("Stack is: ");
                Print(S);
                break;
        }
    }
	DestoryStack(S);
	
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值