1.栈
1.1栈的概念及结构
栈是一种特殊的线性表,只允许在一端进行插入和删除元素的操作。进行数据插入和删除的一端称为栈顶,另一端称为栈底。进行插入的操作称为压栈。进行删除的操作称为出栈。元素遵循后进先出原则,即入数据在栈顶,出数据也在栈顶。
这就相当于一个桶,在不扰动的情况下往里面放东西,一定会放在最上层,想要取东西时,也是先取最上层。只是咱们把桶底称为栈底,桶顶称为栈顶,放东西到桶顶称为压栈(压:像是放东西到桶里的时候压一压,腾出更多空间),把桶顶的东西拿走就叫出栈。
1.2栈的实现
栈的实现一般可以用数组或者链表来实现,但是在尾部插入数据这一方面更好实现,之前也有提过顺序表像是数组的上位版本,那么这次咱将基于动态顺序表的结构来实现动态增长的栈。
先定义一个栈的结构体,之后咱再实现基本的接口。
typedef int SLDateType;
typedef struct Stack
{
SLDateType* _a;
int _top;
int _capacity;
}Stack;
1.2.1栈的初始化
将创建好的结构体指针进行初始化赋值。值得注意的是,_top表示的是栈顶元素的后一个位置,这里将_top初始化为0其实是有点别扭的 ( 因为栈底就是-1了 ) ,但是这样方便元素的计数,更好和当前的容量进行比较。_top也可以赋值为1,只是要在比较或者是给数组赋值的时候的时候-1。
void STInit(Stack* ps)
{
assert(ps);
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
1.2.2入栈
void STPush(Stack* ps, SLDateType x)
{
assert(ps);
//判断顶是否到达最大容量,如果是,扩容
if (ps->_top == ps->_capacity)
{
int DoubCapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
ps->_capacity = DoubCapacity;
SLDateType* ptr = (SLDateType*)realloc(ps->_a, sizeof(SLDateType) * DoubCapacity);
if (ptr == NULL)
{
perror("realloc fail");
return;
}
ps->_a = ptr;
}
//入栈
(ps->_a)[ps->_top] = x;
ps->_top++;
}
1.2.3出栈
出栈很简单,只需要改变_top的值,下一个入栈的数将会覆盖原来的数据。
void STPop(Stack* ps)
{
assert(ps);
ps->_top--;
}
1.2.4获取栈顶元素
这时候_top的概念就很重要了,_top是栈顶的下一个,那么获得栈顶的时候需要-1来获取。
SLDateType STTop(Stack* ps)
{
assert(ps);
assert(ps->_top > 0);
return (ps->_a)[ps->_top-1];
}
1.2.5获取栈的元素个数
这一步,简单的看起来没有必要,因为直接访问_top就够了。但其实有一个隐患,打个比方就是,别人在用咱们的代码的时候怎么知道_top的初始值呢,直接访问_top谁知道这是不是元素个数,所以咱们需要提供一个接口,减少不必要的麻烦。
int STSize(Stack* ps)
{
assert(ps);
return ps->_top;
}
1.2.6判空
判空也就是判断_top的值是否是初始值,如果是的话说明为空返回1,否则返回0。
int STEmpty(Stack* ps)
{
assert(ps);
return ps->_top == 0 ? 1 : 0;
}
1.2.7销毁
void STDestroy(Stack* ps)
{
assert(ps);
free(ps->_a);
ps->_a=NULL;
ps->_capacity = ps->_top = 0;
}
以上就是栈实现的几个接口。
2.队列
2.1队列的概念及结构
队列也是一种特殊的线性表,只允许在一端插入,在另一端删除数据。在进行插入的一端称为队尾,在进行删除的一端称为队头,遵循先进先出原则。
就像是排队进入游乐园,先来的人在队头,可以先进去,后来的人只能站队尾,轮到队尾时才能进去。
2.2队列的实现
队列也可以用数组或者顺序表实现,不过用数组的话需要挪动数据,效率较低,所以用链表的结构实现会更优。
先定义结构体,用于维护队列。
typedef int QDataType;
//队列节点结构体
typedef struct QListNode
{
struct QListNode* next;
QDataType val;
}QListNode;
//创建一个结构体,用于维护队列的队头、队尾、数据个数
typedef struct Queue
{
QListNode* phead;
QListNode* ptail;
int size;
}Queue;
2.2.1初始化队列
初始时,为空队列,所以不用创建节点,只是先初始化队列的基本数据。
void QueueInit(Queue* q)
{
assert(q);
q->phead = NULL;
q->ptail = NULL;
q->size = 0;
}
2.2.2入队列
void QueuePush(Queue* q, QDataType x)
{
assert(q);
//创建一个节点
QListNode* newnode = (QListNode*)malloc(sizeof(QListNode));
newnode->next = NULL;
//如果尾节点是NULL则新节点变成第一个节点
if (q->ptail == NULL)
{
q->phead = q->ptail = newnode;
q->size++;
}
else//否则尾节点后尾插一个节点
{
q->ptail->next = newnode;
q->ptail = newnode;
q->size++;
}
//赋值
q->ptail->val = x;
}
2.2.3出队列
void QueuePop(Queue* q)
{
assert(q);
assert(q->phead);
//创建一个指向第二个节点的指针
QListNode* next = q->phead->next;
free(q->phead);
q->phead = next;
//当队列一共只有一个节点时,ptail会变成野指针
if (next == NULL)
{
q->ptail = NULL;
}
q->size--;
}
2.2.4获取队头、尾数据
获取队头数据只需要访问头节点指针的数据,同理,队尾则只需要访问尾节点指针的数据。
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->phead);
return q->phead->val;
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->phead);
return q->ptail->val;
}
2.2.5获取队列元素个数
也是看上去简单的没必要的接口,但是也是必须的,获取栈的元素个数的接口同理。
int QueueSize(Queue* q)
{
assert(q);
assert(q->size > 0);
return q->size;
}
2.2.6判空
只需要简单访问队列维护个数的变量。
int QueueEmpty(Queue* q)
{
assert(q);
return q->size == 0 ? 1 : 0;
}
2.2.7销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
q->size = 0;
//和链表同理遍历销毁节点
while (q->phead!=q->ptail)
{
//当前指针指向头
QListNode* cur = q->phead;
//头指向下一个节点
q->phead = q->phead->next;
free(cur);
cur = NULL;
}
free(q->phead);
q->phead = q->ptail = NULL;
}
以上就是队列实现的几个接口。
696





