栈
顺序栈+链式栈
一、基本概念
栈是一种逻辑结构,是特殊的线性表。特殊在:
只能在固定的一端操作
只要满足上述条件,那么这种特殊的线性表就会呈现一种"后进先出"的逻辑,这种逻辑就被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们、嵌套函数的参数等等。
由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的"插入"、"删除",另起了下面这些特定的名称:
- 栈顶:可以进行插入删除的一端
- 栈底:栈顶的对端(不能进行任何的插入和删除操作)
- 入栈:将节点插入栈顶之上,也称为压栈,函数名通常为push()
- 出栈:将节点从栈顶剔除,也称为弹栈,函数名通常为pop()
- 取栈顶:取得栈顶元素,但不出栈,函数名通常为top()
基于这种固定一端操作的简单约定,栈获得了"后进先出"的基本特性,如下图所示,最后一个放入的元素,最先被拿出来:
二、存储形式
栈只是一种数据逻辑,如何将数据存储于内存则是另一回事。一般而言,可以采用顺序存储形成顺序栈,或采用链式存储形成链式栈。
顺序栈
顺序存储意味着开辟一块连续的内存来存储数据节点,一般而言,管理栈数据除了需要一块连续的内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置(下标),如果有多线程还需要配互斥锁(互斥机制)和信号量(同步机制)等信息,为了便于管理,通常将这些信息统一于在一个管理结构体之中:
struct seqStack
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
};
链式栈
链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:
// 链式栈节点
typedef struct node
{
datatype data;
struct node *next;
}node;
// 链式栈管理结构体
struct linkStack
{
node *top; // 链式栈栈顶指针
int size; // 链式栈当前元素个数
};
三、基本操作
顺序栈
设计管理结构体并初始化
// 设计数据类型
typedef int dataType ;
//设计管理结构体
struct seqStack
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
};
//初始化
Cntl_t * StackInit( unsigned int size )
{
Cntl_t * cntl = calloc(1, sizeof(Cntl_t));
if (cntl == NULL)
{
perror("calloc cntl error ");
return NULL ;
}
cntl->Data = calloc( size , sizeof(dataType));
if (cntl->Data == NULL)
{
perror("calloc Data error");
return NULL ;
}
cntl->Size = size ;
cntl->Top = -1 ;
return cntl ;
}
入栈
int push( Cntl_t * Stack , dataType * NewData )
{
// 0
if (NewData == NULL)
{
// 栈顶下标+1 = 当前有效数据量
return Stack->Top+1 ;
}
// 1
if (Stack->Top + 1 >= Stack->Size)
{
printf("当前栈已满..\n");
return -1 ;
}
// 2
Stack->Top ++ ;
// 3
Stack->Data [Stack->Top] = *NewData ;
// 4
return Stack->Top + 1 ;
}
出栈
dataType * pop( Cntl_t * Stack )
{
if (Stack->Top == -1)
{
printf("当前栈为空..\n");
return NULL ;
}
// 使用临时指针指向栈顶元素的数据地址
dataType * tmp = Stack->Data + Stack->Top ;
// 更新栈顶下标
Stack->Top -- ;
return tmp ;
}
销毁
void StackDestroy(Cntl_t * Stack)
{
if (Stack == NULL) return;
if (Stack->Data != NULL)
{
free(Stack->Data);
Stack->Data = NULL;
}
free(Stack);
}
链式栈
设计管理结构体并初始化
对于链表一般情况下我们更倾向于带有头节点的链表,因为他操作起来更为便利,而链式栈的管理结构体便可以充当这个头节点的角色。
再思考可以发现对于栈这种逻辑,链表的随意性实际中在栈中都被禁用了,因此可以选用最简单的方案就是单向不循环链表。
P_Cntl_t InitStack( void )
{
// 申请管理结构体
P_Cntl_t cntl = calloc(1, sizeof(Cntl_t));
if (cntl == NULL)
{
perror("calloc Cntl erorr");
return NULL ;
}
// 初始化成员
cntl->top = NULL ;
cntl->Count = 0 ;
return cntl ;
}
入栈
int push( P_Cntl_t cntl , Node_t * NewNode )
{
NewNode->Next = cntl->top ;
cntl->top = NewNode ;
cntl->Count ++ ;
}
出栈
Node_t * pop( P_Cntl_t cntl )
{
if (cntl->Count <= 0 )
{
printf("链式栈为空..\n") ;
return NULL ;
}
Node_t * tmp = cntl->top ;
cntl->top = tmp->Next ;
tmp->Next = NULL ;
cntl->Count -- ;
return tmp ;
}
销毁
/* 销毁整个链式栈 */
void DestroyStack(P_Cntl_t * pCntl) /* 传二级指针,方便把实参置空 */
{
if (pCntl == NULL || *pCntl == NULL)
return;
/* 1. 清空所有节点 */
while ((*pCntl)->Count > 0)
{
Node_t * node = pop(*pCntl);
free(node);
}
/* 2. 释放管理结构体 */
free(*pCntl);
/* 3. 把实参指针置空 */
*pCntl = NULL;
}

浙公网安备 33010602011771号