《数据结构(C语言版)第二版》第三章-栈和队列(3.1-3.4)

3.1 栈和队列的定义和特点

对于输入:先输入表头a1,最后输入表尾an.

栈:后进先出(Last In First Out, LIFO)线性表
表尾在栈顶(入、出),表头在栈底。
在这里插入图片描述
队列:先进先出(First In First Out,FIFO)线性表
表尾在队尾(入),表头在队头(出)。
在这里插入图片描述

3.3 栈的表示和操作的实现

3.3.1 顺序栈的表示和实现

数据结构—c语言实现顺序栈的入栈,出栈,清空,销毁等操作—Chris-Chang

/*
顺序栈
栈的抽象数据类型
ADT 栈(stack)
Data
    同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
    InitStack(*S):初始化操作,建立一个空栈S
    ClearStack(*S):将栈清空
    StackEmpty(S):若栈为空,返回true,否则返回false
    GetTop(*S,*e):若栈存在且非空,用e返回S的栈顶元素
    Push(*S,e):若栈存在,插入新元素e到栈S中并成为栈顶元素
    Pop(*S,*e):删除栈S中栈顶元素,并用e返回其值
    StackTraverse(*S):遍历栈中所有元素
    StackLength(S):返回栈S的元素个数
endADT
 */

#include <stdio.h>
#include <stdlib.h>

#define OVERFLOW        -1
#define STACK_INIT_SIZE 10
#define STACKINCREMENT   2
#define OK               1
#define ERROR            0
#define TRUE             1
#define FALSE            0

#define MAXSIZE          100    /*设定栈最大值*/

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

/*当栈存在一个元素时,top等于0,因此通常把空栈的条件定位top等于-1*/
/*栈的结构定义*/
typedef struct
{
    SElemType data[MAXSIZE];
    int top;  //数组data的下标
}SqStack;

/*算法3.1 初始化顺序栈*/
Status InitStack(SqStack* S)
{
    S->top = -1;
    return OK;
}

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

/*算法3.3 顺序栈的出栈,若栈不为空,则删除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;
}

/*算法3.4 若栈非空,返回栈顶元素*/
Status GetTop(SqStack* S, SElemType* e)
{
    if (S->top == -1)return ERROR;
    else *e = S->data[S->top];
    return OK;
}

/*遍历栈中所有元素*/
Status StackTraverse(SqStack* S)
{
    int n, i;
    n = S->top;
    for (i = n; i >= 0; i--) {
        printf("%d ",(S->data[i]));
    }
    printf("\n");
    return OK;
}

/*返回栈S的元素个数*/
int StackLength(SqStack* S)
{
    return S->top + 1;
}

/*清空栈*/
Status ClearStack(SqStack* S)
{
    S->top = -1;
    return OK;
}

/*判断栈是否为空,-1为空*/
Status StackEmpty(SqStack* S)
{
    if (S->top == -1)
        return TRUE;
    else
        return FALSE;
}


int main()
{
    SqStack s;
    int e;
    printf("InitStack初始化栈并将1--12压入栈\n");
    if (InitStack(&s))
        for (e = 1; e <= 12; e++)
        {
            Push(&s, e);
        }
    printf("StackTraverse栈中元素从栈顶依次为:\n");
    StackTraverse(&s);
    Pop(&s, &e);
    printf("Pop弹出的元素为:%d\n", e);
    Pop(&s, &e);
    printf("Pop又弹出的元素为:%d\n", e);
    printf("Push将刚刚弹出的元素%d再次压入\n", e);
    Push(&s, e);
    printf("StackEmpty判断栈是否为空:%d(1:是 0:不是)\n", StackEmpty(&s));
    GetTop(&s, &e);
    printf("GetTop当前栈顶元素为:%d\n", e);
    printf("StackLength:当前栈长度为%d \n", StackLength(&s));
    ClearStack(&s);
    printf("ClearStack栈清空后,StackEmpty栈是否为空%d(1:是 0:不是)\n", StackEmpty(&s));
    return 0;
}

在这里插入图片描述

顺序栈—基本操作的实现及简单应用(C语言)(一)——阿里云开发者社区

malloc.h头文件和malloc函数详解 小马哥丶

malloc.h和stdlib.h的区别

c语言windows.h头文件详解 viafcccy

【C++】为何引入“引用“? 指针和引用有何区别?

3.3.1.1 顺序栈的初始化

#include<stdio.h>
#include<stdlib.h>
//#include<malloc.h>  //去掉
#include<windows.h>  //exit函数

#define OK       1     //出现OK相当于出现了1 
#define ERROR    0     //出现ERROR相当于出现了0
#define INFEASIBLE    -1 //不可行的
#define OVERFLOW      -2  //overflow一般用于exit的参数中

typedef int Status;
typedef int SElemType;

#define STACK_INIT_SIZE    10     //栈的储空间的初始分配量 
#define STACKINCREMENT     5      //栈的存储空间的分配增量

typedef struct {
    SElemType* top;        //栈顶指针 
    SElemType* base;       //栈底指针,在构造栈时和销毁栈时均为NULL 
    int stacksize;         //当前已经分配的存储空间 
}SqStack;

Status InitStack(SqStack& S);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。、

int main()
{
    //SElemType a[] = { 10 };
    //SElemType b = 20;
    //SElemType* pa = a;
    //SElemType* pb = &b;
    //SqStack N = { N.base = pa,N.top = pb,N.stacksize = 10 };

    SqStack N = { N.base = NULL,N.top = NULL,N.stacksize = 0 };  //必须初始化为空,而不能是其它实际值

    InitStack(N);
    return 0;
}

//构造一个空的顺序栈 
Status InitStack(SqStack& S)
{
    S.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));      
    //开辟一块连续的空间 
    //此处的S.base为一元数组SElemType S.base[STACK_INIT_SIZE]的基地址(首个元素的地址)。参见2.4.1 线性表的顺序存储表示

    if (!S.base)                //如果存储空间分配失败 
        exit(OVERFLOW);

    S.top = S.base;            //当栈为空栈时栈底和栈顶指针位置相同
    S.stacksize = STACK_INIT_SIZE;      //更新栈的空间大小
    return OK;
}

在这里插入图片描述

3.3.1.2 顺序栈的入栈

#include<stdio.h>
#include<stdlib.h>
//#include<malloc.h>  //去掉
#include<windows.h>  //exit函数

#define OK       1     //出现OK相当于出现了1 
#define ERROR    0     //出现ERROR相当于出现了0
#define INFEASIBLE    -1 //不可行的
#define OVERFLOW      -2  //overflow一般用于exit的参数中

typedef int Status;
typedef int SElemType;

#define STACK_INIT_SIZE    10     //栈的储空间的初始分配量 
#define STACKINCREMENT     5      //栈的存储空间的分配增量

typedef struct {
    SElemType* top;        //栈顶指针 
    SElemType* base;       //栈底指针,在构造栈时和销毁栈时均为NULL 
    int stacksize;         //当前已经分配的存储空间 
}SqStack;

Status InitStack(SqStack& S);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status ValueStack(SqStack& S, int Num);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status Push(SqStack& S, SElemType e); //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status PrintStack(SqStack S);

int main()
{
    //SElemType a[] = { 10 };
    //SElemType b = 20;
    //SElemType* pa = a;
    //SElemType* pb = &b;
    //SqStack N = { N.base = pa,N.top = pb,N.stacksize = 10 };

    SqStack N = { N.base = NULL,N.top = NULL,N.stacksize = 0 };  //必须初始化为空,而不能是其它实际值

    InitStack(N);
    ValueStack(N, 13);
    PrintStack(N);

    Push(N, 999);
    PrintStack(N);
    return 0;
}


//构造一个空的顺序栈 
Status InitStack(SqStack& S)
{
    S.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));      
    //开辟一块连续的空间 
    //此处的S.base为一元数组SElemType S.base[STACK_INIT_SIZE]的基地址(首个元素的地址)。参见2.4.1 线性表的顺序存储表示

    if (!S.base)                //如果存储空间分配失败 
        exit(OVERFLOW);

    S.top = S.base;            //当栈为空栈时栈底和栈顶指针位置相同
    S.stacksize = STACK_INIT_SIZE;      //更新栈的空间大小
    return OK;
}

//批量元素(从栈顶)入栈
Status ValueStack(SqStack& S, int Num)
{
    SElemType* Newbase = NULL;
	int length = 0;
	
    if (!S.base) {               //如果栈不存在 
        printf("栈不存在,元素无法入栈\n");
        return ERROR;
    }

    //判断传来的顺序栈中是否为空栈
    if (S.base != S.top) {         //如果不是空栈
    
		length = S.top - S.base;
		
        if (Num >= S.stacksize) {         //如果要入栈的元素数量大于栈的空间大小 
            //Num包括了原来已存在在栈的元素的个数,但是重新输入时,不用再输入已存在的数值。
            Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

            if (!Newbase) {         //如果存储空间分配失败 
                printf("存储空间分配失败\n");
                exit(OVERFLOW);
            }

            S.base = Newbase;
            /* 更新基地址base,看Newbase所在的位置还有没有足够的连续内存空间。
             ①如果有的话,分配更多的空间,(即使没有赋值那一步)返回的地址Newbase和原来的S.base相同;
             ②如果没有的话,在更大的空间内查找,如果找到(S.stacksize + STACKINCREMENT) * sizeof(SElemType)大小的空间,
             将旧的内容拷贝到新的内存中,把旧的内存释放掉,则返回新地址Newbase,如果分配内存失败则返回NULL.
             通过赋值语句将S.base指针也指到该Newbase处。*/
             
			S.top = S.base+ length;
            S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小 
        }


        int temp[] = { 0 };                  //定义一个辅助数组,方括号和花括号都不能少 

        for (int i = 1; i <= Num; i++) {
            printf("请输入第%d个元素的值:", i);
            scanf_s("%d", &temp[i - 1]);       //将之后输入的值暂时存储在临时数组中 
        }

        for (int j = 1; j <= Num; j++) {
            *S.top = temp[j - 1];
            //不能是S.top = &(temp[j - 1]),因为上面已经为S.base分配了连续的存储空间,要为该存储空间中依次存入值。而非更改指针S.top到其它的连续存储空间
            S.top++;
        }

        printf("%d个元素入栈成功\n", Num);
    }
    else 
    {
        if (Num >= S.stacksize) {    
            Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

            if (!Newbase) {        
                printf("存储空间分配失败\n");
                exit(OVERFLOW);
            }

            S.base = Newbase;
            S.top = Newbase;
            S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小 
        }
        
        for (int i = 1; i <= Num; i++) {
            printf("请输入第%d个值:", i);
            scanf_s("%d", &S.base[i - 1]); 
      		//或者用scanf_s("%d", S.top); 
            S.top++;
        }
        printf("%d个元素已入栈\n", Num);
    }

    //这条语句的目的是为了检测栈顶指针的位置是否在栈顶元素的下一个位置
    //如果输出的值等于输入元素的个数,则说明此时栈顶指针和栈底指针的位置都在正确的位置 
    printf("S.top - S.base = %d\n", S.top - S.base);
    return OK;
}

//插入新的栈顶元素并返回其值 
Status Push(SqStack& S, SElemType e)
{
    SElemType* Newbase = NULL;

    if (!S.base) {
        printf("顺序栈不存在,无法插入新的栈顶元素\n");
        return ERROR;
    }
    else if (S.top - S.base == S.stacksize)      //此时顺序栈已满,追加存储空间
    {
        Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

        if (!Newbase) {         //如果存储空间分配失败 
            printf("存储空间分配失败\n");
            exit(OVERFLOW);
        }

        S.base = Newbase;
        S.top = S.base + S.stacksize;
        S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小
    }

    //最后再写入新的栈顶元素
    *S.top = e;
    S.top++;
    return e;               //返回新插入的栈顶元素的值 
}

//打印顺序栈
Status PrintStack(SqStack S)
{
    int i = 0;

    if (!S.base) {
        printf("顺序栈不存在,无法打印\n");
        return ERROR;
    }
    else if (S.base == S.top)
        printf("顺序栈为空栈\n");

    printf("当前顺序栈的元素为:");
    for (i = 0; i < (S.top - S.base); i++) {
        printf(" %d", S.base[i]);
    }
/*此处跳出for循环时,最后一次将i++执行完毕之后,再进行i < (S.top - S.base)的判断,
 但是并没有再接着执行for循环中的printf语句,,而是直接跳出了for循环。
 因此此时的i并不是数组中最后一个元素的下标,而是最后一个元素的下一个元素的下标,也正好就是真正的栈的长度。*/

    printf("\nStack_Length : %d\n", i);
    //所以此处的length可直接使用i,不需要再加1,或直接使用S.top - S.base

    return OK;
}

在这里插入图片描述

3.3.1.3 顺序栈的出栈

#include<stdio.h>
#include<stdlib.h>
//#include<malloc.h>  //去掉
#include<windows.h>  //exit函数

#define OK       1     //出现OK相当于出现了1 
#define ERROR    0     //出现ERROR相当于出现了0
#define INFEASIBLE    -1 //不可行的
#define OVERFLOW      -2  //overflow一般用于exit的参数中

typedef int Status;
typedef int SElemType;

#define STACK_INIT_SIZE    10     //栈的储空间的初始分配量 
#define STACKINCREMENT     5      //栈的存储空间的分配增量

typedef struct {
    SElemType* top;        //栈顶指针 
    SElemType* base;       //栈底指针,在构造栈时和销毁栈时均为NULL 
    int stacksize;         //当前已经分配的存储空间 
}SqStack;

Status InitStack(SqStack& S);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status ValueStack(SqStack& S, int Num);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status Push(SqStack& S, SElemType e); //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status PrintStack(SqStack S);
SElemType Pop(SqStack& S);


int main()
{
    //SElemType a[] = { 10 };
    //SElemType b = 20;
    //SElemType* pa = a;
    //SElemType* pb = &b;
    //SqStack N = { N.base = pa,N.top = pb,N.stacksize = 10 };

    SqStack N = { N.base = NULL,N.top = NULL,N.stacksize = 0 };  //必须初始化为空,而不能是其它实际值


    InitStack(N);
    ValueStack(N, 13);
    PrintStack(N);

    Push(N, 999);
    PrintStack(N);

    printf("\n删除的栈顶元素为:%d\n",Pop(N));
    PrintStack(N);
    return 0;
}



//构造一个空的顺序栈 
Status InitStack(SqStack& S)
{
    S.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
    //开辟一块连续的空间 
    //此处的S.base为一元数组SElemType S.base[STACK_INIT_SIZE]的基地址(首个元素的地址)。参见2.4.1 线性表的顺序存储表示

    if (!S.base)                //如果存储空间分配失败 
        exit(OVERFLOW);

    S.top = S.base;            //当栈为空栈时栈底和栈顶指针位置相同
    S.stacksize = STACK_INIT_SIZE;      //更新栈的空间大小
    return OK;
}

//批量元素(从栈顶)入栈
Status ValueStack(SqStack& S, int Num)
{
    SElemType* Newbase = NULL;
	int length = 0;
	
    if (!S.base) {               //如果栈不存在 
        printf("栈不存在,元素无法入栈\n");
        return ERROR;
    }

    //判断传来的顺序栈中是否为空栈
    if (S.base != S.top) {         //如果不是空栈
    
		length = S.top - S.base;
		
        if (Num >= S.stacksize) {         //如果要入栈的元素数量大于栈的空间大小 
            //Num包括了原来已存在在栈的元素的个数,但是重新输入时,不用再输入已存在的数值。
            Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

            if (!Newbase) {         //如果存储空间分配失败 
                printf("存储空间分配失败\n");
                exit(OVERFLOW);
            }

            S.base = Newbase;
            /* 更新基地址base,看Newbase所在的位置还有没有足够的连续内存空间。
             ①如果有的话,分配更多的空间,(即使没有赋值那一步)返回的地址Newbase和原来的S.base相同;
             ②如果没有的话,在更大的空间内查找,如果找到(S.stacksize + STACKINCREMENT) * sizeof(SElemType)大小的空间,
             将旧的内容拷贝到新的内存中,把旧的内存释放掉,则返回新地址Newbase,如果分配内存失败则返回NULL.
             通过赋值语句将S.base指针也指到该Newbase处。*/
             
			S.top = S.base+ length;
            S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小 
        }

        int temp[] = { 0 };                  //定义一个辅助数组,方括号和花括号都不能少 

        for (int i = 1; i <= Num; i++) {
            printf("请输入第%d个元素的值:", i);
            scanf_s("%d", &temp[i - 1]);       //将之后输入的值暂时存储在临时数组中 
        }

        for (int j = 1; j <= Num; j++) {
            *S.top = temp[j - 1];
            //不能是S.top = &(temp[j - 1]),因为上面已经为S.base分配了连续的存储空间,要为该存储空间中依次存入值。而非更改指针S.top到其它的连续存储空间
            S.top++;
        }

        printf("%d个元素入栈成功\n", Num);
    }
    else 
    {
        if (Num >= S.stacksize) {    
            Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

            if (!Newbase) {        
                printf("存储空间分配失败\n");
                exit(OVERFLOW);
            }

            S.base = Newbase;
            S.top = Newbase;

            S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小 
        }
        
        for (int i = 1; i <= Num; i++) {
            printf("请输入第%d个值:", i);
            scanf_s("%d", &S.base[i - 1]);
            //或者用scanf_s("%d", S.top); 
            S.top++;
        }
        printf("%d个元素已入栈\n", Num);
    }

    //这条语句的目的是为了检测栈顶指针的位置是否在栈顶元素的下一个位置
    printf("S.top - S.base = %d\n", S.top - S.base);
    return OK;
}

//插入新的栈顶元素并返回其值 
Status Push(SqStack& S, SElemType e)
{
    SElemType* Newbase = NULL;

    if (!S.base) {
        printf("顺序栈不存在,无法插入新的栈顶元素\n");
        return ERROR;
    }
    else if (S.top - S.base == S.stacksize)      //此时顺序栈已满,追加存储空间
    {
        Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

        if (!Newbase) {         //如果存储空间分配失败 
            printf("存储空间分配失败\n");
            exit(OVERFLOW);
        }

        S.base = Newbase;
        S.top = S.base + S.stacksize;
        S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小
    }

    //最后再写入新的栈顶元素
    *S.top = e;
    S.top++;
    return e;               //返回新插入的栈顶元素的值 
}

//打印顺序栈
Status PrintStack(SqStack S)
{
    int i = 0;

    if (!S.base) {
        printf("顺序栈不存在,无法打印\n");
        return ERROR;
    }
    else if (S.base == S.top)
        printf("顺序栈为空栈\n");

    printf("当前顺序栈的元素为:");
    for (i = 0; i < (S.top - S.base); i++) {
        printf(" %d", S.base[i]);
    }
    printf("\nStack_Length : %d\n", i);
    //所以此处的length可直接使用i,不需要再加1,或直接使用S.top - S.base

    return OK;
}

SElemType Pop(SqStack& S)
{
	SElemType e;

	if (!S.base)
	{
		printf("删除栈顶元素时,栈不存在");
		return ERROR;
	}
	else if (S.base == S.top)
	{
		printf("删除栈顶元素时,栈为空");
	}

	e = *(S.top-1);
	S.top--;
	return e;
}

在这里插入图片描述

3.3.1.4 取顺序栈的栈顶元素

#include<stdio.h>
#include<stdlib.h>
//#include<malloc.h>  //去掉
#include<windows.h>  //exit函数

#define OK       1     //出现OK相当于出现了1 
#define ERROR    0     //出现ERROR相当于出现了0
#define INFEASIBLE    -1 //不可行的
#define OVERFLOW      -2  //overflow一般用于exit的参数中

typedef int Status;
typedef int SElemType;

#define STACK_INIT_SIZE    10     //栈的储空间的初始分配量 
#define STACKINCREMENT     5      //栈的存储空间的分配增量

typedef struct {
    SElemType* top;        //栈顶指针 
    SElemType* base;       //栈底指针,在构造栈时和销毁栈时均为NULL 
    int stacksize;         //当前已经分配的存储空间 
}SqStack;

Status InitStack(SqStack& S);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status ValueStack(SqStack& S, int Num);  //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status Push(SqStack& S, SElemType e); //必须使用c++中的引用传址机制,从而得以直接对所传入的对象进行修改。
Status PrintStack(SqStack S);
SElemType Pop(SqStack& S);
SElemType GetTop(SqStack S);

int main()
{
    //SElemType a[] = { 10 };
    //SElemType b = 20;
    //SElemType* pa = a;
    //SElemType* pb = &b;
    //SqStack N = { N.base = pa,N.top = pb,N.stacksize = 10 };

    SqStack N = { N.base = NULL,N.top = NULL,N.stacksize = 0 };  //必须初始化为空,而不能是其它实际值


    InitStack(N);
    ValueStack(N, 13);
    PrintStack(N);

    Push(N, 999);
    PrintStack(N);

    printf("\n删除的栈顶元素为:%d\n",Pop(N));
    PrintStack(N);
    printf("\n新的栈顶元素为:%d", GetTop(N));
    return 0;
}



//构造一个空的顺序栈 
Status InitStack(SqStack& S)
{
    S.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
    //开辟一块连续的空间 
    //此处的S.base为一元数组SElemType S.base[STACK_INIT_SIZE]的基地址(首个元素的地址)。参见2.4.1 线性表的顺序存储表示

    if (!S.base)                //如果存储空间分配失败 
        exit(OVERFLOW);

    S.top = S.base;            //当栈为空栈时栈底和栈顶指针位置相同
    S.stacksize = STACK_INIT_SIZE;      //更新栈的空间大小
    return OK;
}

//批量元素(从栈顶)入栈
Status ValueStack(SqStack& S, int Num)
{
    SElemType* Newbase = NULL;
	int length = 0;
	
    if (!S.base) {               //如果栈不存在 
        printf("栈不存在,元素无法入栈\n");
        return ERROR;
    }

    //判断传来的顺序栈中是否为空栈
    if (S.base != S.top) {         //如果不是空栈
    
		length = S.top - S.base;
		
        if (Num >= S.stacksize) {         //如果要入栈的元素数量大于栈的空间大小 
            //Num包括了原来已存在在栈的元素的个数,但是重新输入时,不用再输入已存在的数值。
            Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

            if (!Newbase) {         //如果存储空间分配失败 
                printf("存储空间分配失败\n");
                exit(OVERFLOW);
            }

            S.base = Newbase;
            /* 更新基地址base,看Newbase所在的位置还有没有足够的连续内存空间。
             ①如果有的话,分配更多的空间,(即使没有赋值那一步)返回的地址Newbase和原来的S.base相同;
             ②如果没有的话,在更大的空间内查找,如果找到(S.stacksize + STACKINCREMENT) * sizeof(SElemType)大小的空间,
             将旧的内容拷贝到新的内存中,把旧的内存释放掉,则返回新地址Newbase,如果分配内存失败则返回NULL.
             通过赋值语句将S.base指针也指到该Newbase处。*/
             
			S.top = S.base+ length;
            S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小 
        }

        int temp[] = { 0 };                  //定义一个辅助数组,方括号和花括号都不能少 

        for (int i = 1; i <= Num; i++) {
            printf("请输入第%d个元素的值:", i);
            scanf_s("%d", &temp[i - 1]);       //将之后输入的值暂时存储在临时数组中 
        }

        for (int j = 1; j <= Num; j++) {
            *S.top = temp[j - 1];
            //不能是S.top = &(temp[j - 1]),因为上面已经为S.base分配了连续的存储空间,要为该存储空间中依次存入值。而非更改指针S.top到其它的连续存储空间
            S.top++;
        }

        printf("%d个元素入栈成功\n", Num);
    }
    else 
    {
        if (Num >= S.stacksize) {    
            Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

            if (!Newbase) {        
                printf("存储空间分配失败\n");
                exit(OVERFLOW);
            }

            S.base = Newbase;
            S.top = Newbase;

            S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小 
        }
        
        for (int i = 1; i <= Num; i++) {
            printf("请输入第%d个值:", i);
            scanf_s("%d", &S.base[i - 1]);
            //或者用scanf_s("%d", S.top); 
            S.top++;
        }
        printf("%d个元素已入栈\n", Num);
    }

    //这条语句的目的是为了检测栈顶指针的位置是否在栈顶元素的下一个位置
    printf("S.top - S.base = %d\n", S.top - S.base);
    return OK;
}

//插入新的栈顶元素并返回其值 
Status Push(SqStack& S, SElemType e)
{
    SElemType* Newbase = NULL;

    if (!S.base) {
        printf("顺序栈不存在,无法插入新的栈顶元素\n");
        return ERROR;
    }
    else if (S.top - S.base == S.stacksize)      //此时顺序栈已满,追加存储空间
    {
        Newbase = (SElemType*)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));

        if (!Newbase) {         //如果存储空间分配失败 
            printf("存储空间分配失败\n");
            exit(OVERFLOW);
        }

        S.base = Newbase;
        S.top = S.base + S.stacksize;
        S.stacksize = S.stacksize + STACKINCREMENT;         //更新栈的空间大小
    }

    //最后再写入新的栈顶元素
    *S.top = e;
    S.top++;
    return e;               //返回新插入的栈顶元素的值 
}

//打印顺序栈
Status PrintStack(SqStack S)
{
    int i = 0;

    if (!S.base) {
        printf("顺序栈不存在,无法打印\n");
        return ERROR;
    }
    else if (S.base == S.top)
        printf("顺序栈为空栈\n");

    printf("当前顺序栈的元素为:");
    for (i = 0; i < (S.top - S.base); i++) {
        printf(" %d", S.base[i]);
    }
    printf("\nStack_Length : %d\n", i);
    //所以此处的length可直接使用i,不需要再加1,或直接使用S.top - S.base

    return OK;
}

SElemType Pop(SqStack& S)
{
	SElemType e;

	if (!S.base)
	{
		printf("删除栈顶元素时,栈不存在");
		return ERROR;
	}
	else if (S.base == S.top)
	{
		printf("删除栈顶元素时,栈为空");
	}

	e = *(S.top-1);
	S.top--;
	return e;
}

//取顺序栈顶栈顶元素(栈顶指针不变)
SElemType GetTop(SqStack S)
{

    if (!S.base)
    {
        printf("取顺序栈栈顶元素时,栈不存在");
        return ERROR;
    }
    else if (S.base == S.top)
    {
        printf("取顺序栈栈顶元素时,栈为空");
    }

    return *(S.top - 1);
}

在这里插入图片描述

3.3.2 链栈的表示和实现

3.3.2.1 链栈的初始化

#include <stdio.h>
#include <stdlib.h>

typedef struct StackNode
{
	int data;
	struct StackNode* next;
}*LinkStack;

void initStack(LinkStack &S);

int main()
{
	struct StackNode* N = NULL;
	initStack(N);
	return 0;
}

//算法3.5 链栈的初始化
void initStack(LinkStack& S)
{
	//S是链栈的栈顶指针
	S = NULL;
	printf("初始化链栈成功。\n");
}

在这里插入图片描述

3.3.2.2 链栈的入栈

#include <stdio.h>
#include <stdlib.h>

typedef struct StackNode
{
	int data;
	struct StackNode* next;
}*LinkStack;

void initStack(LinkStack &S);
void ValueStack(LinkStack& S);
void Push(LinkStack& S, int e);
void printStack(LinkStack& S);

int main()
{
	struct StackNode* N = NULL;
	initStack(N);

	ValueStack(N);
	printStack(N);

	printf("\n");
	Push(N, 999);
	printStack(N);
	return 0;
}

//算法3.5 链栈的初始化
void initStack(LinkStack& S)
{
	//S是链栈的栈顶指针
	S = NULL;
	printf("初始化链栈成功。\n");
}

//批量元素从栈顶入栈
void ValueStack(LinkStack& S)
{
	int len = 0;
	int i = 0;
	int num = 0;

	printf("请输入链栈的长度:");
	scanf_s("%d", &len);

	for (i = 1; i <= len; i++)
	{
		printf("请输入第%d个元素的值:", i);
		scanf_s("%d", &num);

		struct StackNode* p = (struct StackNode*)malloc(sizeof(struct StackNode));
		p->data = num;
		p->next = S;
		S = p;
		printf("元素%d入栈成功。\n", num);
	}

	printf("%d个元素入栈成功。\n", i - 1);
}

//算法3.6 链栈的入栈
void Push(LinkStack& S, int e)
{
	//在栈顶插入元素e
	struct StackNode* p = (struct StackNode*)malloc(sizeof(struct StackNode));
	p->data = e;
	p->next = S;
	S = p;
	printf("元素%d入栈成功。\n", e);
}

//打印链栈
void printStack(LinkStack& S)
{
	struct StackNode* pMove = S;
	int i = 0;

	printf("链栈中的元素为:");
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
		i++;
	}

	printf("\nStack_Length : %d\n", i );
}

在这里插入图片描述

3.3.2.3 链栈的出栈

#include <stdio.h>
#include <stdlib.h>

#define ERROR -2

typedef struct StackNode
{
	int data;
	struct StackNode* next;
}*LinkStack;

void initStack(LinkStack& S);
void ValueStack(LinkStack& S);
void Push(LinkStack& S, int e);
void printStack(LinkStack& S);
int Pop(LinkStack& S);

int main()
{
	struct StackNode* N = NULL;
	initStack(N);

	ValueStack(N);
	printStack(N);

	printf("\n");
	Push(N, 999);
	printStack(N);

	printf("删除的栈顶元素为:%d\n", Pop(N));
	printStack(N);
	return 0;
}

//算法3.5 链栈的初始化
void initStack(LinkStack& S)
{
	//S是链栈的栈顶指针
	S = NULL;
	printf("初始化链栈成功。\n");
}

//批量元素从栈顶入栈
void ValueStack(LinkStack& S)
{
	int len = 0;
	int i = 0;
	int num = 0;

	printf("请输入链栈的长度:");
	scanf_s("%d", &len);

	for (i = 1; i <= len; i++)
	{
		printf("请输入第%d个元素的值:", i);
		scanf_s("%d", &num);

		struct StackNode* p = (struct StackNode*)malloc(sizeof(struct StackNode));
		p->data = num;
		p->next = S;
		S = p;
		printf("元素%d入栈成功。\n", num);
	}

	printf("%d个元素入栈成功。\n", i - 1);
}

//算法3.6 链栈的入栈
void Push(LinkStack& S, int e)
{
	//在栈顶插入元素e
	struct StackNode* p = (struct StackNode*)malloc(sizeof(struct StackNode));
	p->data = e;
	p->next = S;
	S = p;
	printf("元素%d入栈成功。\n", e);
}

//打印链栈
void printStack(LinkStack& S)
{
	struct StackNode* pMove = S;
	int i = 0;

	printf("链栈中的元素为:");
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
		i++;
	}

	printf("\nStack_Length : %d\n", i);
}


//算法3.7 链栈的出栈
int Pop(LinkStack& S)
{
	int e = 0;

	if (S == NULL)
	{
		printf("删除栈顶元素时,栈为空。\n");
		return ERROR;
	}

	e = S->data;

	LinkStack p = S;
	S = S->next;
	free(p);
	return e;
}

在这里插入图片描述

3.3.2.4 取链栈的栈顶元素

#include <stdio.h>
#include <stdlib.h>

#define ERROR -2

typedef struct StackNode
{
	int data;
	struct StackNode* next;
}*LinkStack;

void initStack(LinkStack& S);
void ValueStack(LinkStack& S);
void Push(LinkStack& S, int e);
void printStack(LinkStack& S);
int Pop(LinkStack& S);
int GetTop(LinkStack S);

int main()
{
	struct StackNode* N = NULL;
	initStack(N);

	ValueStack(N);
	printStack(N);

	printf("\n");
	Push(N, 999);
	printStack(N);

	printf("删除的栈顶元素为:%d\n", Pop(N));
	printStack(N);
	printf("新的栈顶元素为:%d\n", GetTop(N));
	return 0;
}

//算法3.5 链栈的初始化
void initStack(LinkStack& S)
{
	//S是链栈的栈顶指针
	S = NULL;
	printf("初始化链栈成功。\n");
}

//批量元素从栈顶入栈
void ValueStack(LinkStack& S)
{
	int len = 0;
	int i = 0;
	int num = 0;

	printf("请输入链栈的长度:");
	scanf_s("%d", &len);

	for (i = 1; i <= len; i++)
	{
		printf("请输入第%d个元素的值:", i);
		scanf_s("%d", &num);

		struct StackNode* p = (struct StackNode*)malloc(sizeof(struct StackNode));
		p->data = num;
		p->next = S;
		S = p;
		printf("元素%d入栈成功。\n", num);
	}

	printf("%d个元素入栈成功。\n", i - 1);
}

//算法3.6 链栈的入栈
void Push(LinkStack& S, int e)
{
	//在栈顶插入元素e
	struct StackNode* p = (struct StackNode*)malloc(sizeof(struct StackNode));
	p->data = e;
	p->next = S;
	S = p;
	printf("元素%d入栈成功。\n", e);
}

//打印链栈
void printStack(LinkStack& S)
{
	struct StackNode* pMove = S;
	int i = 0;

	printf("链栈中的元素为:");
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
		i++;
	}

	printf("\nStack_Length : %d\n", i);
}

//算法3.7 链栈的出栈
int Pop(LinkStack& S)
{
	int e = 0;

	if (S == NULL)
	{
		printf("删除栈顶元素时,栈为空。\n");
		return ERROR;
	}

	e = S->data;

	LinkStack p = S;
	S = S->next;
	free(p);
	return e;
}

//算法3.8 取链栈的栈顶元素
int GetTop(LinkStack S)  //因为没有对S中的元素进行更改,所以不需要使用引用
{
	if (S == NULL)
	{
		printf("取栈顶元素时,栈为空。\n");
	}

	return S->data;
}

在这里插入图片描述

3.4 栈与递归

Hanoi塔问题的的递归算法

汉诺塔——经典递归问题(c语言实现)——lucas_dd

此问题难点是:
先抽象出对象较大的每一步1、2、3,
对象较大的每一步(如1)中再抽象出由对象较小的构成的每一步(如1.1、1.2、1.3),
且由对象较小的构成的每一步1.1、1.2、1.3,要跟对象较大的步骤1、2、3是相同或类同的。

1.1还可以往下接着抽象出同样与1、2、3相同或类同的1.1.1、1.1.2、1.1.3.

【此问题中的实例:
问题:将a座上的n个盘子借助b座移向c座

  1. 将A座上的n-1个盘子借助C座移向B座
  2. 将A座上最后一个盘子移向C座
  3. 将B座上的n-1个盘子借助A座移向C座

将步骤“1. 将A座上的n-1个盘子借助C座移向B座”抽象出下面的步骤:

1.1 将A座上的n-2个盘子借助b座移向C座
1.2 将A座上最后一个盘子移向B座
1.3 将C座上的n-2个盘子借助A座移向B座

要求:1.1、1.2、1.3 与 1、2、3是相同或类同的。】

1.2、1.3也同样如此。2、3也同样如此。
如此往下不断细分。
【以上即为递归分解过程】

每一个分支都只有达到递归结束的条件,才能停止细分,执行递归结束的语句,并依次向一层返回结果。
【此类步骤即为递归求值过程】

//算法3.10 Hanoi塔问题的的递归算法
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//将x座上最后一个盘子移向y座
void move(int x, int y)
{
	printf("%c->%c\n", x, y);
}

//将a座上的n个盘子借助b座移向c座
void hanoi(int n, char a, char b, char c)
{
	if (n == 1)
	{
		move(a, c);
	}
	else
	{
		hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
		move(a, c);//将A座上最后一个盘子移向C座
		hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
	}
}
//move中的实参与hanoi函数中的形参相对应,而hanoi函数中形参a,b,c所对应的值也是在有规律的变化


int main()
{
	int n = 0;
	scanf("%d", &n);
	hanoi(n, 'A', 'B', 'C');
	return 0;
}

在这里插入图片描述

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值