【C语言数据结构】之栈

一、基本概念

栈和队列是被限定只能在端点处进行操作的线性表。本文介绍栈。
栈有两种实现方式,一种是顺序栈,一种是链式栈。顺序栈由数组的方式实现,链式栈由链表的形式实现。我们一般情况下使用顺序栈较多。
在这里插入图片描述
栈只能在尾部插入尾部输出,我们称这种性质为后进先出,LIFO(Last in Fist out)

二、 顺序栈

顺序栈是基于顺序表(通常是数组)来实现的栈。
在栈中没有元素的时候,栈顶游标我们可以初始化为-1
为什么要初始化为-1,因为当有一个新元素要入栈的话,栈顶游标++刚好就是0,即数组的下标为0的数组第一位。

1. 宏定义和结构体

# define True 1
# define False 0
# define Size 50
typedef struct Stack
{
    int data[Size];
    int itop;
}Stack;

2. 初始化栈

Stack* CreateStack()
{
    Stack* p = (Stack*)malloc(sizeof(Stack));
    if(p == NULL)
    {
        exit(1);
    }
    memset(p,0,sizeof(Stack));
    p->itop = -1;
    return p;
}

3. 判断栈空,栈满

当栈中没有元素就是栈空,这时候栈顶游标是-1
当栈顶游标等于初始化时候的大小Size-1的时候,就是栈满。我们有50个空间的栈,下标为0-49,当栈顶游标为49时就说明栈满了。

int IsEmpty(Stack* p)
{
    if(p->itop == -1)
    {
        return True;
    }
    return False;
}
int IsFull(Stack* p)
{
    if(p->itop == Size-1)
    {
        return True;
    }
    return False;
}

4. 入栈

在这里插入图片描述

void InsertStack(Stack* p,int num)
{
    if(IsFull(p) == True)
    {
        return;
    }
    p->itop++;
    p->data[p->itop] = num;
}

5. 出栈

出栈可以只出栈,也可以出栈并获取出栈元素,我们这里采用出栈并获取出栈元素的写法。

int PopStack(Stack* p)
{
    if(IsEmpty(P) == True)
    {
        return;
    }
    int x = p->data[p->itop];
    p->itop--;
    return x;
}

6. 获取栈顶游标

直接返回栈顶游标即可。

int Getitop(Stack* p)
{
    if(p == NULL)
    {
        return;
    }
    return p->itop;
}

7. 打印栈顶元素

当前栈顶游标就是栈顶元素在数组中的下标,根据栈顶游标找到栈顶元素并输出。

void PrintTop(Stack* p)
{
    if(IsEmpty(p) == True)
    {
        return;
    }
    printf("栈顶元素为%d\n",p->data[p->itop]);
}

8. 打印栈中所有元素

说白了就是按顺序输出数组中的元素。

void PrintStack(Stack* p)
{
    if(IsEmpty(p) == True)
    {
        return;
    }
    int i =0;
    for(i;i<=p->itop;i++)
    {
        printf("%d ",p->data[i]);
    }
    putchar('\n');
}

三、 链式栈

采用链式存储的栈叫做链式栈。链式栈不存在栈满一说,链式栈的入栈出栈操作实际上就是单链表的尾插尾删操作。
对链式栈操作就看做单链表操作,只不过只允许尾插尾删。大家想看详细介绍可以移步我的【C语言数据结构】之单链表文章。

1. 宏定义和结构体

# define True 1
# define False 0

typedef struct Stack
{
	struct Stack* next;
	int data;
}Stack;

2. 初始化栈

Stack* CreateStack()
{
	Stack* p = (Stack*)malloc(sizeof(Stack));
	assert(p);
	memset(p, 0, sizeof(Stack));
	return p;
}

3. 判断栈空

int IsEmpty(Stack* p)
{
	assert(p);
	if (p->next == NULL)
	{
		return True;
	}
	return False;
}

4. 入栈

单链表尾插操作

void InsertStack(Stack* p,int num)
{
	assert(p);
	Stack* node = (Stack*)malloc(sizeof(Stack));
	memset(node, 0, sizeof(Stack));
	node->data = num;
	Stack* tmp = p;
	while (tmp->next != NULL)
	{
		tmp = tmp->next;
	}
	tmp->next = node;
	node->next = NULL;
}

5. 出栈

单链表尾删操作

int PopStack(Stack* p)
{
	assert(p);
	if (IsEmpty(p) == True)
	{
		printf("空栈无法出栈\n");
		return -1;
	}
	Stack* tmp1 = p;
	Stack* tmp2 = p->next;
	while (tmp2->next != NULL)
	{
		tmp1 = tmp1->next;
		tmp2 = tmp2->next;
	}
	int x = tmp2->data;
	free(tmp2);
	tmp1->next = NULL;
	return x;
}

6. 获取栈顶游标

int Getitop(Stack* p)
{
	int i = -1;
	if (IsEmpty(p) == True)
	{
		return i;
	}
	Stack* tmp = p->next;
	while (tmp != NULL)
	{
		i++;
		tmp = tmp->next;
	}
	return i;
}

7. 打印栈顶元素

void PrintTop(Stack* p)
{
	assert(p);
	if (IsEmpty(p) == True)
	{
		printf("栈空无栈顶元素\n");
		return;
	}
	Stack* tmp = p->next;
	while (tmp->next != NULL)
	{
		tmp = tmp->next;
	}
	printf("栈顶元素为%d \n", tmp->data);
}

8. 打印栈中所有元素

void PrintStack(Stack* p)
{
	assert(p);
	if (IsEmpty(p) == True)
	{
		printf("栈空,无元素\n");
		return;
	}
	Stack* tmp = p->next;
	while (tmp != NULL)
	{
		printf("%d ", tmp->data);
		tmp = tmp->next;
	}
	putchar('\n');
}

四、总结

牢记栈的特性 – 后进先出(LIFO)
顺序栈和链式栈在进行入栈出栈操作的时候的时间复杂度都是O(1)
顺序栈优点就是逻辑上简单,它就是个数组,但是他在空间上没有灵活性。
链式栈相对来说难理解一点,但是它就是个限制了输入输出的单链表,我们只要掌握了单链表操作,对于链式栈来说就是一点问题也没有了,链式栈的优点是不用担心栈满,但是会增加内存开销。
数据结构最重要的就是单链表操作,说白了,双向链表,循环链表,栈,队列,哈希表等都是单链表的拓展,所以我们要学好单链表操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiuJWHHH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值