新手向C语言实现特殊的数据结构——栈(含用两个栈实现队列)
Posted 燕麦冲冲冲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了新手向C语言实现特殊的数据结构——栈(含用两个栈实现队列)相关的知识,希望对你有一定的参考价值。
目录
一、易错接口详解
1.1 栈的初始化
涉及到结构体的定义,请先跳转至第三部分查看结构体的详情。
算法实现:assert
断言参数是否为NULL
,即结构体指针不能为NULL
。
由于实现的是动态的栈,即会随着数据的增加不断增大栈的空间,所以需要给指向栈的指针初始化为NULL
。
最后把栈顶位置top
和空间容量capacity
初始化为0。
void StackInit(ST* ps)
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
1.2 栈的销毁
调用这个接口通常是在程序结束前将内存释放,还给操作系统,避免内存泄漏。
算法:assert
断言结构体指针是否为空。
若指向栈的指针不为NULL
则释放该指针。
最后把容量和栈顶位置均重置为0。
void StackDestroy(ST* ps)
assert(ps);
if (ps->a)
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
1.3 入栈
入栈都需要对容量进行检测,所以封装为一个函数,以便随时调用。
易错点在于realloc
开辟空间可能存在失败,如果直接把新空间的地址返回,而不是用一个中间变量暂时存储,一旦开辟失败,就会造成原本已有的地址都无法查找。
static void CheckCapacity(ST* ps)
if (ps->capacity == ps->top)
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDatatype* tmp = (STDatatype*)realloc(ps->a, sizeof(STDatatype) * newcapacity);
if (tmp == NULL)
printf("realloc fail\\n");
exit(-1);
ps->a = tmp;
ps->capacity = newcapacity;
入栈算法:断言是否为空,检查容量,放入数据,栈顶位置自增。
void StackPush(ST* ps, STDatatype x)
assert(ps);
CheckCapacity(ps);
ps->a[ps->top] = x;
ps->top++;
1.4 出栈
算法:断言结构体指针是否为空,断言栈是否为空,让栈顶位置自减。
判断栈是否为空的功能可以封装为一个函数,以便后续多次调用。
bool StackEmpty(ST* ps)
assert(ps);
return ps->top == 0;
void StackPop(ST* ps)
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
二、简单接口的实现
2.1 有效数据个数
int StackSize(ST* ps)
assert(ps);
return ps->top;
2.2 返回栈顶数据
STDatatype StackTop(ST* ps)
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
三、头文件的引用,结构体和函数的定义
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDatatype;
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);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
STDatatype StackTop(ST* ps);
四、拾枝杂谈——用两个栈实现队列
队列:先进先出
栈:后进先出
算法总结:1、入数据,进pushst
2、出数据,看popst
是否为空,如果为空,就先把pushst
的数据倒过来,然后出数据。如果不为空直接出数据。
4.1 队列结构体声明
typedef struct
ST pushST;
ST popST;
MyQueue;
此结构体为匿名结构体,typedef重定义一个新名字,定义其对象时就不必写struct
了。
其中包含两个栈。
4.2 队列初始化
MyQueue* myQueueCreate()
MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&q->pushST);
StackInit(&q->popST);
return q;
首先定义一个队列的结构体对象的指针,为其开辟空间。
再利用已实现的接口为队列的结构体对象中的成员——两个栈进行初始化。
返回值为该对象的地址。
4.3 入队
其本质是对pushST这个栈进行压栈操作,调用已实现的接口即可。
void myQueuePush(MyQueue* obj, int x)
StackPush(&obj->pushST, x);
4.4 出队
其本质是对popST
这个栈进行出栈操作。
但是需要保证这个栈中有数据,若没有数据,则需要把pushST
中的所有数据全部压栈进入popST
中。
最后返回“出队”的数据。
int myQueuePop(MyQueue* obj)
if(StackEmpty(&obj->popST))
while(!StackEmpty(&obj->pushST))
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
int front = StackTop(&obj->popST);
StackPop(&obj->popST);
return front;
4.5 取队头数据
取对头数据即取popST
的栈顶数据。
此操作并没有让数据出栈,但是栈为空时需要将pushST
中的数据压栈进入。
int myQueuePeek(MyQueue* obj)
if(StackEmpty(&obj->popST))
while(!StackEmpty(&obj->pushST))
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
return StackTop(&obj->popST);
4.6 判断队列是否为空
判断队列结构体对象中的两个栈是否为空即可。
bool myQueueEmpty(MyQueue* obj)
return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
4.7 销毁队列
利用已实现的接口对队列结构体对象中的两个栈进行销毁,再释放这个对象本身。
void myQueueFree(MyQueue* obj)
StackDestroy(&obj->popST);
StackDestroy(&obj->pushST);
free(obj);
以上是关于新手向C语言实现特殊的数据结构——栈(含用两个栈实现队列)的主要内容,如果未能解决你的问题,请参考以下文章