栈是限定仅在表尾进行插入和删除操作的线性表。
把允许插入和删除操作的一端称为栈顶,另一端称为栈底。栈又称为后进先出的线性表,简称为LIFO结构。(Last In First Out)
和线性表一样,栈也分为顺序栈和链栈,顺序栈尾插尾删的效率都较高,而链栈如果用单链表实现,则尾删效率低,除非使用双向链表。这里我们采用顺序栈的方式实现栈的基本操作。
typedef struct stack
{
STDataType* a;//指向一块数组空间
int top;//记录当前栈顶的位置
int capacity;//记录顺序栈的最大容量
}ST;
void StackInit(ST* ps);//初始化栈
void StackDestroy(ST* ps);//销毁栈
void StackPush(ST* ps, STDataType x);//入栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//返回栈顶元素
bool StackEmpty(ST* ps);//判断栈是否为空
int StackSize(ST* ps);//返回栈中元素的个数
void StackInit(ST* ps)
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType));
ps->top = 0;
ps->capacity = 0;
}
top和capacity初始化为0,其中top初始化为0,top指向栈顶元素的下一个位置,top也可初始化为-1,此时top指向当前的栈顶元素。
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->top = 0;
ps->capacity = 0;
}
将malloc出的数组空间释放掉,top和capacity都置为0
void StackPush(ST* ps,STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)//栈满,考虑扩容
{
int newcapacity=ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(int));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;//重新指向扩容好的空间
ps->capacity = newcapacity;//更新栈的最大容量
}
ps->a[ps->top] = x;//因为top指向的是栈顶元素的下一个,所以直接将top位置的值赋值为x
ps->top++;//top++,继续指向栈顶元素的下一个
}
这里扩容和顺序表的扩容一样,需要单独考虑第一次扩容的问题,因为第一次capacity的值为0,如果扩大二倍还是0,所以第一次直接赋值为4。
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));//栈不能为空
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));//栈不能为空
return ps->a[ps->top - 1];
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
top值若为0,则是空栈,bool值为true,反之则为false
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
因为top指向栈顶元素的下一个位置,所以top的值即为当前栈中元素的个数
void test1()
{
ST ps;
StackInit(&ps);
StackPush(&ps,1);
StackPush(&ps, 2);
StackPush(&ps, 3);//入栈1,2,3
printf("%d\n", StackTop(&ps));//返回栈顶元素
StackPop(&ps);//出栈
StackPush(&ps, 4);
StackPush(&ps, 5);//入栈4,5
printf("%d\n", StackSize(&ps));//栈中元素的个数
while (!StackEmpty(&ps))//依次打印栈中的元素
{
printf("%d ", StackTop(&ps));
StackPop(&ps);
}
StackDestroy(&ps);//销毁栈
}
由于对栈的操作都只能在栈顶进行,所以要依次打印栈中的元素,应用循环(只要栈不为空就一直打印),不断返回栈顶元素,并将栈顶元素出栈。
运行结果
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出的线性表(First In First Out),允许插入的一端称为队尾,允许删除的一端称为队头。
typedef struct QNode
{
struct QNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;//头指针和尾指针封装成一个结构体
队列用链式结构实现,因为只能在队头删除,队尾插入,所以应定义两个指针指向单链表,一个实现删除操作,一个实现插入操作。同时将两个指针封装成一个新的结构体,可以避免在接口函数传二级指针。
void QueueInit(Queue* pq);//队列的初始化
void QueueDestroy(Queue* pq);//队列的销毁
void QueuePush(Queue* pq, QDataType x);//入队
void QueuePop(Queue* pq);//出队
QDataType QueueFront(Queue* pq);//返回队头元素
QDataType QueueBack(Queue* pq);//返回队尾元素
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//返回队列中的元素个数
void QueueInit(Queue* pq)
{
assert(pq);//pq为指向Queue结构体的指针,一定不为空
pq->head = NULL;
pq->tail = NULL;
}
尾指针和头指针都先置空
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)//依次free掉链表的每个结点
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
释放链表中的结点应注意先保存链表下个位置的结点,然后再free,最后头指针和尾指针置NULL
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)//第一次入队,直接将头尾指针指向新结点
{
pq->head = pq->tail = newnode;
}
else//正常尾插
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
这里要单独考虑第一次入队的情况,入队时,尾指针指向新的结点,然后更新尾指针
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));//队列不能为空
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
出队时先提前保存头结点的下一个结点,然后释放头结点,将头指针指向新的头结点
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
头指针指向的即为队头,尾指针指向的即为队尾
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
头指针指向空即为空队列
int QueueSize(Queue* pq)
{
int size = 0;
QNode* cur = pq->head;
while (cur)//遍历整个链表,记录个数
{
size++;
cur = cur->next;
}
return size;
}
void test1()
{
Queue pq;
QueueInit(&pq);
QueuePush(&pq, 1);
QueuePush(&pq, 2);
QueuePush(&pq, 3);
QueuePush(&pq, 4);
QueuePush(&pq, 5);
QueuePush(&pq, 6);//入队1,2,3,4,5,6
printf("%d\n", QueueSize(&pq));//打印队列中元素的个数6
while (!QueueEmpty(&pq))
{
printf("%d ", QueueFront(&pq));//依次打印队头元素1,2,3,4,5,6
QueuePop(&pq);//队头元素出队
}
printf("\n");
QueueDestroy(&pq);
}