Bootstrap

最全数据结构个人笔记【栈-链栈】

1.基本概念

​ 栈是一种逻辑结构,是特殊的线性表,特殊在只能在固定一端操作

只要满足上述条件,那么这种特殊的线性表就会呈现出一种"后进先出"的逻辑,这种逻辑就被称为栈,栈在生活中到处可见,比如堆叠的盘子、电梯中的人等等。

在这里插入图片描述

由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的"插入"、“删除”,另起了下面这些特殊的名称:

  1. 栈顶 : 可以进行插入删除的一端

  2. 栈底:栈顶的对端

  3. 入栈: 将节点插入栈顶之上,也称为压栈,函数名通常为push()

  4. 出栈:将节点从栈顶剔除,也称为弹栈,函数名通常额外pop()

  5. 取栈顶: 取得栈顶元素,但不出栈,函数名通常为top()

基于这种固定一端操作的简单约定,栈获得了"后进先出"的基本特征,如图所示,最后一个放入的元素,最先被拿出来:

在这里插入图片描述

2.存储方式

栈只是一种数据逻辑,如何将数据存储于内存则是另外一回事,一般而言,可以采用顺序存储形成顺序栈,或者采用链式存储形成链式栈。
1.顺序栈
顺序存储意味着开辟一块连续的内存用于存储数据节点,一般而言,管理栈数据除了需要一块连续的内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置,如果有多线程还需要配合互斥锁和信号量等信息,为了方便管理,通常将这些信息统一于一个管理结构体中;

struct seqStack
	{
	    datatype *data; // 顺序栈入口
	    int size;       // 顺序栈总容量
	    int top;        // 顺序栈栈顶元素下标
	};

在这里插入图片描述
2.链式栈
链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:

// 链式栈节点
	typedef struct node
	{
	    datatype data;
	    struct node *next;
	}node;
	
	// 链式栈管理结构体
	struct linkStack
	{
	    node *top; // 链式栈栈顶指针
	    int  size; // 链式栈当前元素个数
	};

在这里插入图片描述

3.基本操作

​ 不管是顺序栈,链式栈,栈的操作逻辑都是一样的,但由于存储形式不同,代码的实现是不同的。下面分别将顺序栈和链式栈的基本核心操作罗列出来:

1.顺序栈

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

typedef int dataType;

typedef struct seqStack
{
    dataType *data; // 顺序栈入口
    int size;  // 顺序栈总容量
    int top;   // 顺序栈栈顶元素下标
} seqStack;

// 初始化空栈
seqStack *initStack(int size)
{
    seqStack *stack = (seqStack *)malloc(sizeof(seqStack));
    if (stack != NULL)
    {
        stack->data = (dataType *)malloc(sizeof(dataType) * size);
        if (stack->data == NULL)
        {
            free(stack);
            return NULL;
        }

        stack->size = size;
        stack->top = -1;
    }

    return stack;
}

// 判断栈是否已满
bool isFull(seqStack *s)
{
    return s->top == s->size - 1;
}

// 判断栈是否为空
bool isEmpty(seqStack *s)
{
    return s->top == -1;
}

// 入栈
bool push(seqStack *s, dataType data)
{
    if (isFull(s))
        return false;

    s->data[++s->top] = data;
    return true;
}

// 取栈顶元素
bool top(seqStack *s, dataType *pm) 
{
    if (isEmpty(s))
        return false;
    *pm = s->data[s->top];
    return true;
}

// 出栈
bool pop(seqStack *s, dataType *pm)
{
    if (top(s, pm) == false)
        return false;

    s->top--;
    return true;
}


int main(int argc, char const *argv[])
{
    // 初始化栈
    seqStack *s = initStack(10);
    if (s == NULL)
    {
        perror("init stack failed:");
        return -1;
    }

    //入栈
    push(s,1);
    push(s,2);
    push(s,3);
    push(s,4);

    dataType data;
    
    //弹栈
    while (s->top != -1)
    {
        bool ret = pop(s,&data);
        if(!ret)
        {
            printf("栈为空!\n");
            continue;
        }
        printf("%d\t",data);
    }

    printf("\n");

    // 释放内存
    free(s->data);
    free(s);

    return 0;
}

「课堂练习1」

使用顺序栈,接收键盘的输入,实现如下功能:

  1. 输入数字时,依次入栈。

  2. 输入字母时,依次出栈。

  3. 每次入栈或者出栈,都将顺序栈中的各个元素输出出来。

    在这里插入图片描述

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <ctype.h>  // isdigit() 函数    isalpha() 函数
    
    
    typedef int dataType;
    
    typedef struct seqStack
    {
        dataType *data; // 顺序栈入口
        dataType size;  // 顺序栈总容量
        dataType top;   // 顺序栈栈顶元素下标
    } seqStack;
    
    // 初始化空栈
    seqStack *initStack(int size)
    {
        seqStack *stack = (seqStack *)malloc(sizeof(seqStack));
        if (stack != NULL)
        {
            stack->data = (dataType *)malloc(sizeof(dataType) * size);
            if (stack->data == NULL)
            {
                free(stack);
                return NULL;
            }
    
            stack->size = size;
            stack->top = -1;
        }
    
        return stack;
    }
    
    // 判断栈是否已满
    bool isFull(seqStack *s)
    {
        return s->top == s->size - 1;
    }
    
    // 判断栈是否为空
    bool isEmpty(seqStack *s)
    {
        return s->top == -1;
    }
    
    // 入栈
    bool push(seqStack *s, dataType data)
    {
        if (isFull(s))
            return false;
    
        s->data[++s->top] = data;
        return true;
    }
    
    // 取栈顶元素
    bool top(seqStack *s, dataType *pm) 
    {
        if (isEmpty(s))
            return false;
        *pm = s->data[s->top];
        return true;
    }
    
    // 出栈
    bool pop(seqStack *s, dataType *pm)
    {
        if (top(s, pm) == false)
            return false;
    
        s->top--;
        return true;
    }
    
    // 输出栈中的所有元素
    void show_stack(seqStack *s)
    {
        if(isEmpty(s))
        {
            printf("空栈!\n");
            return;
        }
    
        for (int i = s->top; i > -1; i--)
        {
            printf(" %d",s->data[i]);
            if( i == s->top)
            {
                printf("<——栈顶");
                printf("\n");
            }
        }
        printf("------------------\n");
        printf("总计%d个元素\n",s->top+1 );
    
    }
    
    int main(int argc, char const *argv[])
    {
        // 初始化栈
        seqStack *s = initStack(10);
        if (s == NULL)
        {
            perror("init stack failed:");
            return -1;
        }
        char input;
        printf("请输入数字(0-9)进行入栈,输入字母(a-z或A-Z)进行出栈,输入'q'退出程序:\n");
        
        while (1)
        {
            
            scanf(" %c",&input);
    
            if(input == 'q')
                break;
            else if(isdigit(input))  //如果输入数字,入栈
            {
                int num = input - '0';
                if(!push(s,num))         
                    printf("栈已满!无法入栈~\n");
    
            }
            else if(isalpha(input))     //输入字母,出栈
            {
                dataType data;
                if(!pop(s,&data))  
                    printf("栈已空!无法出栈~\n");
    
            }
            else   
                printf("无效的输入!请输入字母或者字符~\n");
            
            show_stack(s);
        }
        
    
        // 释放内存
        free(s->data);
        free(s);
    
        return 0;
    }
    
    

    在这里插入图片描述

2.链式栈

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

typedef int dataType;

// 链式栈节点
typedef struct node
{
    dataType data;
    struct node *next;
}node;

// 链式栈管理结构体
typedef struct LinkStack
{
    node *top;  //链式栈 栈顶指针
    int size;   //链式栈当前元素个数
}LinkStack;

//初始化空栈
LinkStack *init_stack(void)
{
    LinkStack *s = (LinkStack*)malloc(sizeof(LinkStack));
    if(s != NULL)
    {
        s->top = NULL;
        s->size = 0;
    }
    return s;
}

//判断栈是否为空
bool isEmpty(LinkStack *s)
{
    return (s->size == 0);
}

//入栈
bool push(LinkStack *s,dataType data)
{
    //创建链表节点
    node *new = (node*)malloc(sizeof(node));
    if(new == NULL)
        return false;
    
    new->data = data;

    //将节点置入栈顶
    new->next = s->top;
    s->top = new;

    //更新栈元素个数
    s->size++;
    
    return true;
}

//出栈
bool pop(LinkStack *s,dataType *data)
{
    if(isEmpty(s))
        return false;
    
    node *tmp = s->top;

    //将原栈顶元素剔除出栈
    s->top = tmp->next;
    tmp->next = NULL;

     // 返回栈顶元素,并释放节点
    *data = tmp->data;
    free(tmp); 

    //更新栈元素个数
    s->size--;

    return true;
}
// 取栈顶元素
bool top(LinkStack *s, dataType *pm)
{
	if(isEmpty(s))
		return false;

    // 返回栈顶元素,并释放节点
    *pm = s->top->data;
	return true;
}

void show(LinkStack *s)
{
    if(isEmpty(s))
        return ;
    
    node *p = s->top;
    int n = s->size;
    while (n--)
    {
        printf("%d\t",p->data);
        p =p->next;
    }
    printf("\n");
    
}

int main(int argc, char const *argv[])
{
    //初始化栈
    LinkStack *s = init_stack(); 
    if(s == NULL)
    {
        perror("init stack failed:");
        return -1;
    }
    //入栈
    push(s,1);
    push(s,2);
    push(s,3);

    //取栈顶元素
    dataType data;
    if(top(s,&data))
        printf("成功!取到栈顶元素%d \n",data);
    else
        printf("失败!取了个寂寞!\n");
    show(s);

    //出栈
    if(pop(s,&data))
        printf("成功!出栈元素%d \n",data);
    else
        printf("失败!吊都没出来!\n");
    show(s);


    return 0;
}

在这里插入图片描述

「课堂练习2」

  1. 使用链式栈,实现十进制转八进制:键盘输入一个十进制数,经过链式栈的相关算法,输出八进制数。

    在这里插入图片描述

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

typedef int dataType;

// 链式栈节点
typedef struct node
{
    dataType data;
    struct node *next;
} node;

// 链式栈管理结构体
typedef struct LinkStack
{
    node *top; // 链式栈 栈顶指针
    int size;  // 链式栈当前元素个数
} LinkStack;

// 初始化空栈
LinkStack *init_stack(void)
{
    LinkStack *s = (LinkStack *)malloc(sizeof(LinkStack));
    if (s != NULL)
    {
        s->top = NULL;
        s->size = 0;
    }
    return s;
}

// 判断栈是否为空
bool isEmpty(LinkStack *s)
{
    return (s->size == 0);
}

// 入栈
bool push(LinkStack *s, dataType data)
{
    // 创建链表节点
    node *new = (node *)malloc(sizeof(node));
    if (new == NULL)
        return false;

    new->data = data;

    // 将节点置入栈顶
    new->next = s->top;
    s->top = new;

    // 更新栈元素个数
    s->size++;

    return true;
}

// 出栈
bool pop(LinkStack *s, dataType *data)
{
    if (isEmpty(s))
        return false;

    node *tmp = s->top;

    // 将原栈顶元素剔除出栈
    s->top = tmp->next;
    tmp->next = NULL;

    // 返回栈顶元素,并释放节点
    *data = tmp->data;
    free(tmp);

    // 更新栈元素个数
    s->size--;

    return true;
}
// 取栈顶元素
bool top(LinkStack *s, dataType *pm)
{
    if (isEmpty(s))
        return false;

    // 返回栈顶元素,并释放节点
    *pm = s->top->data;
    return true;
}
//显示
void show(LinkStack *s)
{
    if (isEmpty(s))
    {
        printf("栈为空\n");
        return;
    }
       

    node *p = s->top;
    while (p != NULL) 
    {
        printf("%d", p->data);
        p = p->next;
    }
    printf("\n");
}

// 销毁栈,释放内存
void destroy_stack(LinkStack *s) 
{
    node *p = s->top;
    while (p != NULL) 
    {
        node *temp = p;
        p = p->next;
        free(temp);
    }
    free(s);
}

int main(int argc, char const *argv[])
{
    // 初始化栈
    LinkStack *s = init_stack();
    if (s == NULL)
    {
        perror("初始化栈失败!");
        return -1;
    }

    printf("请输入一个十进制整数: ");
    int n,num;
    if (scanf("%d", &n) != 1) 
    {
        printf("输入无效\n");
        // 清除输入缓冲区中的残余数据
        while (getchar() != '\n');
        destroy_stack(s);
        return -1;
    }
    
    num = n;    // 记录原始输入值

    // 十进制转八进制
    if (n == 0) 
    {
        push(s, 0);
    } 
    else 
    {
        while (n > 0) 
        {
            int remain = n % 8;
            push(s, remain);    //余数入栈
            n /= 8;
        }
    }

    // 显示转换结果
    printf("%d转换后的八进制数为",num);
    show(s);

    // 销毁栈
    destroy_stack(s);
    return 0;
}


       perror("初始化栈失败!");
        return -1;
    }

    printf("请输入一个十进制整数: ");
    int n,num;
    if (scanf("%d", &n) != 1) 
    {
        printf("输入无效\n");
        // 清除输入缓冲区中的残余数据
        while (getchar() != '\n');
        destroy_stack(s);
        return -1;
    }
    
    num = n;    // 记录原始输入值

    // 十进制转八进制
    if (n == 0) 
    {
        push(s, 0);
    } 
    else 
    {
        while (n > 0) 
        {
            int remain = n % 8;
            push(s, remain);    //余数入栈
            n /= 8;
        }
    }

    // 显示转换结果
    printf("%d转换后的八进制数为",num);
    show(s);

    // 销毁栈
    destroy_stack(s);
    return 0;
}

在这里插入图片描述

;