第12课——栈的定义及实现
栈的定义
* 栈仅能在线性表的一端进行操作
栈顶(Top) : 允许操作的一端
栈底(Bottom) :不允许操作的一端
栈的性质

栈的操作
# 栈的一些常用操作
* 创建栈
* 销毁栈
* 清空栈
* 进栈
* 出栈
* 获取栈顶元素
* 获取栈的大小
栈的顺序存储实现

下面的顺序栈是不能支持结构体的!
现在我们先来实现顺序栈,由于之前我们实现了顺序表,现在代码复用,用其来实现顺序栈
SeqStack.h
SeqStack.hSeqStack.c
SeqStack.c复用之前的代码
SeqList.c
SeqList.cSeqList.h
SeqList.hmain.c
#include <stdio.h>
#include <stdlib.h>
#include "SeqStack.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[])
{
SeqStack* stack = SeqStack_Create(20);
int a[10];
int i = 0;
for (i = 0; i<10; i++)
{
a[i] = i;
SeqStack_Push(stack, a + i);
}
printf("Top: %d\n", *(int*)SeqStack_Top(stack));
printf("Capacity: %d\n", SeqStack_Capacity(stack));
printf("Length: %d\n", SeqStack_Size(stack));
while (SeqStack_Size(stack) > 0)
{
printf("Pop: %d\n", *(int*)SeqStack_Pop(stack));
}
SeqStack_Destroy(stack);
return 0;
}
输出结果:

没有输出?代码?
由于编译器是64位,此时把编译环境换成32位。
输出结果:

结果如上所示,为什么呢?
看一下表头的定义和顺序表的创建:


在32位系统上,指针为4字节。在64位系统上指针为8字节。
在顺序表的创建中,malloc的内存是:表头的结构体[4字节+4字节+指针(4字节或8字节)] + 4字节*capacity
再看看顺序表的插入函数和get函数:


在64位系统中,指针为8字节,在插入函数中,node强转成TseqListNode(unsigned int)赋值给sList->node[i](指针)。指针的偏移是8字节。
顾把一个8字节的指针强转成4字节,发生指针截断,这里就会出错。
改进代码为:
把SeqList.c文件中的typedef unsigned int TSeqListNode;
替换为:

这样如果编译器为64位,则开启64位的宏,如果编译器为32位,则开启32位的宏。
栈的链式实现:
链式栈是可以支持结构体的,但是要求和链式表一样需要包含特定的头,为了后续的历程复用,下面不使用结构体类型入栈操作。
eg:
LinkStack.h
#ifndef _LINKSTACK_H_ #define _LINKSTACK_H_ //抽象数据类型 typedef void LinkStack; //创建顺序栈 LinkStack* LinkStack_Create(); //销毁 void LinkStack_Destroy(LinkStack* stack); //清除 void LinkStack_Clear(LinkStack* stack); //压栈 int LinkStack_Push(LinkStack* stack, void* item); //出栈,弹出 void* LinkStack_Pop(LinkStack* stack); //栈顶 void* LinkStack_Top(LinkStack* stack); //栈大小 int LinkStack_Size(LinkStack* stack); #endif
源文件:LinkStack.c
#include <malloc.h>
#include "LinkStack.h"
#include "LinkList.h"
typedef struct _tag_LinkStackNode
{
LinkListNode header;
void* item;
} TLinkStackNode;
LinkStack* LinkStack_Create()
{
return LinkList_Create();
}
void LinkStack_Destroy(LinkStack* stack)
{
LinkStack_Clear(stack);
LinkList_Destroy(stack);
}
void LinkStack_Clear(LinkStack* stack)
{
while( LinkStack_Size(stack) > 0 )
{
LinkStack_Pop(stack);
}
}
int LinkStack_Push(LinkStack* stack, void* item)
{
TLinkStackNode* node = (TLinkStackNode*)malloc(sizeof(TLinkStackNode));
int ret = (node != NULL) && (item != NULL);
if( ret )
{
node->item = item;
ret = LinkList_Insert(stack, (LinkListNode*)node, 0);
}
if( !ret )
{
free(node);
}
return ret;
}
void* LinkStack_Pop(LinkStack* stack)
{
TLinkStackNode* node = (TLinkStackNode*)LinkList_Delete(stack, 0);
void* ret = NULL;
if( node != NULL )
{
ret = node->item;
free(node);
}
return ret;
}
void* LinkStack_Top(LinkStack* stack)
{
TLinkStackNode* node = (TLinkStackNode*)LinkList_Get(stack, 0);
void* ret = NULL;
if( node != NULL )
{
ret = node->item;
}
return ret;
}
int LinkStack_Size(LinkStack* stack)
{
return LinkList_Length(stack);
}
注意,上面的push操作时插入到链表首的,这样更快一些,不用循环到链表尾部。
链式栈的清除不能直接调用链式表的清除,否则产生内存泄露,这里主要是为了之后的栈的例子,所以使用基本类型元素入栈,如果实现成可以支持结构体入栈的形式,直接调用链式表的清除函数即可,这个留给大家练手,可参考链式表的实现。
链式栈的销毁是在调用了栈的清除函数之后再次调用链式表的销毁构成的,其他的复用没有什么变化。
main.c
#include <stdio.h>
#include <stdlib.h>
#include "LinkStack.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[])
{
LinkStack* stack = LinkStack_Create();
int a[10];
int i = 0;
for(i=0; i<10; i++)
{
a[i] = i;
LinkStack_Push(stack, a + i);
}
printf("Top: %d\n", *(int*)LinkStack_Top(stack));
printf("Length: %d\n", LinkStack_Size(stack));
while( LinkStack_Size(stack) > 0 )
{
printf("Pop: %d\n", *(int*)LinkStack_Pop(stack));
}
LinkStack_Destroy(stack);
return 0;
}
输出结果和顺序栈是一样的。
posted on 2019-12-05 13:50 blogernice 阅读(227) 评论(0) 收藏 举报

浙公网安备 33010602011771号