队列与栈都是一种特殊的线性结构,两者之间也可以做到相互实现
实现思路
数据结构中队列是一个先进先出的结构,而栈是一个先进后出的数据结构,现在我们需要设计一个程序使我们先输入的数据最后才能出来,而我们现在能使用的只有队列。
队列的特性就是最先进去的最先出来,最后进去的最后出来。而现在假设我们输入了一组数据在这个队列中,要出数据的时候我们需要拿到最后进去的数据将其提取出来便能实现栈的特性。
若要拿到队列中最后一个的数据那我们必须先把前面的所有数据先拿出,所以我们再定义一个队列(队列2)我们将队列1中除最后一个的所有数据都提取到队列2中,因为队列的先进先出特性,数据在队列2中的顺序与在队列1时一样。然后我们拿出最后一个数据将它输出这就拿到了最后进的数据实现我们的栈结构的特性。
创建初始化栈
typedef struct
{
Que s1;
Que s2;
} MyStack;
MyStack* myStackCreate()
{
MyStack* tmp = (MyStack*)malloc(sizeof(MyStack));
InitQueue(&tmp->s1);
InitQueue(&tmp->s2);
return tmp;
}
这里我们定义了一个结构体里面包含两个队列用于实现栈,这个队列与上文中的队列的实现一致不再描述了。
然后我们进行栈的创建,开辟一个空间用于存放栈结构体,然后调用结构体的初始化函数,传入结构体内两个队列的地址进行初始化和创建。
添加元素
void myStackPush(MyStack* obj, int x)
{
int i = Queempty(&obj->s1);
if (i == 1)
{
PushQue(&obj->s2, x);
}
else
{
PushQue(&obj->s1, x);
}
}
函数第一个参数是存有两个队列的结构体地址和待存入的数据。第一个函数调用是传入队列的地址判断队列是否为空,为空返回1。这里我们一开始对s1队列进行了判空,这是因为我们在删除数据的时候是将两个队列的数据传来传去的,每当我们进行一次出栈的时候都会进行一次队列间的数据传送,所以我们需要知道我们的数据是需要放在哪个队列中。因为队列中数据传输是不会影响到数据的顺序的,但是如果我们对一个空的队列进行数据插入这时它就是最先进入的数据,但是对于在另外一个队列中的数据来说它是最后一个进来的数据,这样顺序就乱了。所以若栈中有数据时每当我们插入数据一定要插入到存有数据的队列中。
删除元素
int myStackPop(MyStack* obj)
{
int i = Queempty(&obj->s1);
if (i == 1)
{
TranQue(&obj->s1, &obj->s2);
int n = Quefront(&obj->s2);
PopQue(&obj->s2);
return n;
}
else
{
TranQue(&obj->s2, &obj->s1);
int n = Quefront(&obj->s1);
PopQue(&obj->s1);
return n;
}
}
我们一进来还是先判空来确定哪个队列有数据,进入相对应的分支语句,这时我们需要先将队列中的数据除最后一个都转移到空的队列中,这里我提取出了转移的部分写了一个函数下面会有。转移好后我们定义一个变量去存储这个数据这里我是用了整形,可以在头部重命名一个数据类型来方便我们转换数据类型,存储好就调用队列函数将原来存储数据的队列中的元素出栈因为只剩一个数据调用一次就可以。返回这个数据我们就拿到了栈顶的数据并出栈。
转移函数
void TranQue(Que* s1, Que* s2)//将s2的数据转移size-1个数据到s1中
{
int j = Quesize(s2);
if (j <= 1)
return;
for (int i = 0; i < j - 1; i++)
{
PushQue(s1, Quefront(s2));
PopQue(s2);
}
}
进入函数我们先获取队列的数据个数,判断若队列为空直接返回,然后进入循环调用队列的添加函数第一个参数传入空队列的地址,第二个参数我们再调用一个获取队列头数据的函数来提取我们队列中的第一个数据,然后我们将存有队列数据的头数据出队,即完成了一个数据的转移。循环队列中的数据个数减一完成整个数据转移。
栈的销毁
void myStackFree(MyStack* obj)
{
QueDestroy(&obj->s1);
QueDestroy(&obj->s2);
}//两个队列实现栈尾巴
直接调用队列的销毁函数,销毁结构体中的两个队列。注意这里不要释放掉结构体空间,因为这里的传参是用的一级指针若释放掉结构体空间,主函数中的指针会变成野指针。
获取栈顶数据
int myStackTop(MyStack* obj)
{
int i = Queempty(&obj->s1);
if (i == 1)
{
return Queback(&obj->s2);
}
else
{
return Queback(&obj->s1);
}
}
栈顶数据就是最后一个存入的数据所以相较于队列来说就是最后一个数据,我们调用获取队列尾部数据的函数。
栈判空
bool myStackEmpty(MyStack* obj)
{
if (Queempty(&obj->s1) == 1 && Queempty(&obj->s2) == 1)
return true;
return false;
}
只要两个队列都为空即为空。