C语言数据结构----栈的定义及实现
本节主要说的是数据结构中的栈的基本定义和实现的方式,其中实现的方式采用的是复用顺序表和单向链表的方式。
一、栈的基本定义
1.栈是一种特殊的线性表,只能从固定的方向进出,而且栈进出的基本原则是:先进栈的元素后出栈。
2.老唐对栈顶栈底的定义:
栈顶:允许操作的一端。
栈底:不允许操作的一端。
二、栈的基本实现方式
1.使用顺序存储的方式实现栈
在这种方式下,我们采用顺序表的复用方法来实现栈的数据存储。
2.使用链式存储来实现栈
在这种方式下,我们采用单向链表的复用技术来实现链栈。
三、普通的顺序栈
1.首先定义的顺序栈中的数据结点的结构,主要包括两个部分,一部分是数据元素,另一部分是顺序栈的长度。
具体代码如下:
typedef struct _tag_stack_
{
int a[20];
int top;
}Sqstack;
2.使用顺序栈之前要先初始化顺序栈。
主要是为顺序栈结点分配一个空间,然后将顺序栈的长度初始化为0.
Sqstack* InitStack ()
{
Sqstack *ret = NULL;
ret = (Sqstack*)malloc (sizeof(Sqstack));
if (ret)
{
/*将栈的长度初始化为0*/
ret -> top = 0;
}
return ret;
}
3.将元素压入栈,这里采用复用方式。
int Push(Sqstack *stack, int data)
{
/*这里有一个复用方式,也就是顺序栈的长度和数组的下标进行复用s*/
stack -> a[stack -> top] = data;
stack -> top++;
return 1;
}
4.将已经在栈中的元素进行打印,因为栈不是只是一种存储数据的结构,所以我们不经过弹出栈中的元素也是可以访问到栈中的元素的。
void Play (Sqstack *stack)
{
int i = 0;
if (stack -> top == 0)
{
printf ("It is empty\n");
}
/*stack -> top,栈的长度*/
else
{
for (i = 0; i < stack -> top; i++)
{
printf ("栈中的数据为:%d\n", stack -> a[i]);
}
}
}
5.数据结点出栈
int Pop (Sqstack *stack, int *data)
{
if (stack -> top == 0)
{
printf ("the stack is empty\n");
printf ("弹出已经被改变了的u的值");
}
else
{
stack -> top--;
*data = stack -> a[stack -> top];
}
return 1;
}
6.测试部分代码如下:
int main()
{
int h = 4;
int p = 0;
int i = 0;
int u = 3;
Sqstack* qq; qq = InitStack(); for (i = 0; i < 5; i++)
{
Push (qq, i);
}
Play (qq); /*弹出操作*/
Pop (qq, &u);
printf ("弹出的元素是:%d\n",u);
Pop (qq, &u);
printf ("弹出的元素是:%d\n",u);
Pop (qq, &u);
printf ("弹出的元素是:%d\n",u);
Pop (qq, &u);
printf ("弹出的元素是:%d\n",u);
Pop (qq, &u);
printf ("弹出的元素是:%d\n",u);
Pop (qq, &u);
printf ("%d\n",u); return 1;
}
7.虽然顺序栈实现了栈的基本功能,毕竟是顺序存储结构,而且占用的内存空间也必须是连续的,所以还是有一定的局限性的。
四、栈的具体实现代码解析
1.顺序栈的实现
主要运用的是顺序表的复用方法,栈的建立和进栈出栈的过程都是采用的顺序表的复用技术。
具体的代码如下:
#include <stdio.h>
#include "1.h"
#include "SeqList.h" /*******************************************************************************
*函数名: SeqStack_Create
*参数:capacity 栈中元素的个数
*返回值:SeqStack*类型,是一个void*类型,然后再由接收函数进行强制类型转换
*功能:创建顺序栈,调用顺序表创建函数
*******************************************************************************/
SeqStack* SeqStack_Create(int capacity)
{
return SeqList_Create(capacity);
} /*******************************************************************************
*函数名: SeqStack_Destroy
*参数:SeqStack* stack 栈指针
*返回值:void
*功能:销毁顺序栈,调用顺序表销毁函数
*******************************************************************************/
void SeqStack_Destroy(SeqStack* stack)
{
SeqList_Destroy (stack);
} /*******************************************************************************
*函数名: SeqStack_Clear
*参数:SeqStack* stack 栈指针
*返回值:void
*功能:清空顺序栈,调用顺序表清空函数
*******************************************************************************/
void SeqStack_Clear(SeqStack* stack)
{
SeqList_Clear (stack);
} /*******************************************************************************
*函数名: SeqStack_Push
*参数:SeqStack* stack 栈指针 void* item要进栈的元素
*返回值:void
*功能:将一个item元素压入栈
*******************************************************************************/
int SeqStack_Push(SeqStack* stack, void* item)
{
return SeqList_Insert(stack, item, SeqList_Length(stack));
} /*******************************************************************************
*函数名: SeqStack_Pop
*参数:SeqStack* stack 栈指针
*返回值:void
*功能:将元素弹出栈
*******************************************************************************/
void* SeqStack_Pop(SeqStack* stack)
{
return SeqList_Delete(stack, SeqList_Length(stack) - 1);
} /*******************************************************************************
*函数名: SeqStack_Top
*参数:SeqStack* stack 栈指针
*返回值:void
*功能:获取栈顶元素
*******************************************************************************/
void* SeqStack_Top(SeqStack* stack)
{
return SeqList_Get(stack, SeqList_Length(stack) - 1);
} /*******************************************************************************
*函数名: SeqStack_Size
*参数:SeqStack* stack 栈指针
*返回值:int 返回栈的长度
*功能:获取栈的长度
*******************************************************************************/
int SeqStack_Size(SeqStack* stack)
{
return SeqList_Length (stack);
} /*******************************************************************************
*函数名: SeqStack_Capacity
*参数:SeqStack* stack 栈指针
*返回值:void
*功能:获取栈的容量
*******************************************************************************/
int SeqStack_Capacity(SeqStack* stack)
{
return SeqList_Capacity(stack);
}
测试部分代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "1.h" /*int main(int argc, char *argv[])
{
int i;
int a[10];
int q = 20;
int temp; SeqStack* stack = SeqStack_Create (20);
for (i = 1; i < 10; i++)
{
a[i] = i;
SeqStack_Push (stack, a + i);
} printf ("栈的长度是: %d\n", SeqStack_Size (stack)); /*这里必须加上强制类型转换,因为调用函数结束以后返回的也是void *类型,所以要转换*/
/*printf ("栈顶元素是: %d\n", *(int*)SeqStack_Top (stack)); for (i = 1; i < 10; i++)
{
printf ("栈中的元素分别是:%d\n", *(int *)SeqStack_Pop (stack));
} temp = (int) SeqStack_Capacity(stack);
printf ("栈的容量为:%d\n", temp); temp = SeqStack_Size(stack);
printf ("栈的元素个数为:%d\n", temp); return 0;
}*/ int main()
{
int i = 0;
char a[10];
char temp; SeqStack* stack = SeqStack_Create (20);
for (i = 0; i < 9; i++)
{
a[i] = 'a';
SeqStack_Push (stack, a + i);
}
a[9] = 'b';
SeqStack_Push (stack, a + 9); for (i = 0; i < 10; i++)
{
temp = *(char*)SeqStack_Pop (stack);
printf ("%c\n", temp);
}
return 0;
}
2.链式栈的实现
(1)定义数据结点
数据结点用结构体来封装,这个结构体中包含了每一个next元素的信息和进栈元素的地址,虽然我们在创建链表的时候已经进行了一个结构体的定义,但是我们的栈成员并不适用于那套链表,所以这里进行重新定义。
结构体定义如下:
typedef struct _tag_LinkStack_
{
LinkListNode header;
void *item;
}TLinkStackNode;
(2)销毁栈的函数
void LinkStack_Destroy(LinkStack* stack)
{
/*调用栈清空函数*/
LinkStack_Clear (stack);
/*调用链表销毁函数*/
LinkList_Destroy(stack);
}
销毁栈的函数中主要调用了栈的清空函数和链表的销毁函数,销毁栈的的前提首先要销清空栈中的每一个成员,然后在销毁栈的头。
(3)栈的清空函数
void LinkStack_Clear(LinkStack* stack)
{
while (LinkStack_Size (stack) > 0)
{
LinkStack_Pop (stack);
}
}
栈的清空函数中和链表的清空函数是由区别的,在栈的清空函数中我们主要是对栈中是否还存在元素进行了判定,如果还有元素就对将栈中的元素弹出,而链表的清空只是将链表头指向NULL,将链表长度置为0.
 (4)将元素压入栈的操作
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;
}
在将元素压入栈的过程中,我们首先要为我们即将压入栈的元素开辟一块空间,因为是链式栈,所以我们的空间不一定非要是连续的,这是采用malloc的方式。
然后进行安全性的检测,我们要判断开辟的空间是否成功,然后还要判断我们要插入的元素的地址是不是空。如果条件都成立那我们进行元素的进栈操作。
元素的进栈操作,我们将要插入的数据结点的地址赋给栈结构体的item,然后调用链表的插入函数操作,将这个栈的数据结点插入栈中,而且由于我们是把链表的头作为栈顶,所以我们插入栈元素的位置为0.
如果我们的安全性检测没有通过,那么我们就释放为了插入一个栈元素而释放的空间。
(5)元素的出栈操作
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;
}
元素的出栈操作中,首先通过调用链表的元素删除函数来删除我们要弹出栈的元素,因为栈永远都是从栈顶弹出元素,而我们进栈的方向也是从链表的0位置方向进栈的,所以我们只要删除链表中的第0个元素即可(所谓的链表第0个元素哈)。
然后我们判定我们要删除的元素是否为空,如果不为空,那么我们将返回我们要弹出栈的元素。
在将元素弹出栈以后我们就要释放为这个链表中的元素开辟的空间。而我们的链表的操作的空间是在操作具体数据元素的时候才开辟空间,我们不使用链表的时候它是不占用空间的。
(6)获取栈顶元素操作
void* LinkStack_Top(LinkStack* stack)
{
TLinkStackNode* node = (TLinkStackNode*)LinkList_Get(stack, 0);
void *ret = NULL;
if (node != NULL)
{
ret = node->item;
}
return ret;
}
通过LinkList_Get函数可以获得固定位置的元素,由于我们进栈的时候就是从0位置 所以在我们获取栈顶元素的时候也是从0位置开始获取。
五、链式栈的实现代码
主要附上链式表的实现部分代码,复用单向链表的代码这里就不再附了。
1.链式栈的代码实现部分
#include <stdio.h>
#include <malloc.h>
#include "1.h"
#include "LinkList.h" typedef struct _tag_LinkStack_
{
LinkListNode header;
void *item;
}TLinkStackNode; /*******************************************************************************
*函数名: LinkStack_Create
*参数:void
*返回值:LinkStack* 栈的指针
*功能:创建一个链栈,并返回创建成功后的指针
*******************************************************************************/
LinkStack* LinkStack_Create()
{
return LinkList_Create();
} /*******************************************************************************
*函数名:LinkStack_Destroy
*参数:void
*返回值:LinkStack* 栈的指针
*功能:销毁栈 调用栈清除函数和链表销毁函数
*******************************************************************************/
void LinkStack_Destroy(LinkStack* stack)
{
/*调用栈清空函数*/
LinkStack_Clear (stack);
/*调用链表销毁函数*/
LinkList_Destroy(stack);
} /*******************************************************************************
*函数名:LinkStack_Clear
*参数:void
*返回值:LinkStack* 栈的指针
*功能:栈清空函数
*******************************************************************************/
void LinkStack_Clear(LinkStack* stack)
{
while (LinkStack_Size (stack) > 0)
{
LinkStack_Pop (stack);
}
} /*******************************************************************************
*函数名:LinkStack_Push
*参数:LinkStack* stack 栈指针 void* item 要压入栈的元素
*返回值:int 判断压栈操作是否成功
*功能:将数据元素压入栈
*******************************************************************************/
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;
} /*******************************************************************************
*函数名:LinkStack_Pop
*参数:LinkStack* stack 栈指针
*返回值:void* 返回的是出栈的元素
*功能:将数据元素弹出栈
*******************************************************************************/
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;
} /*******************************************************************************
*函数名:LinkStack_Top
*参数:LinkStack* stack 栈指针
*返回值:void* 返回的是栈顶的元素
*功能:返回栈顶元素
*******************************************************************************/
void* LinkStack_Top(LinkStack* stack)
{
TLinkStackNode* node = (TLinkStackNode*)LinkList_Get(stack, 0);
void *ret = NULL;
if (node != NULL)
{
ret = node->item;
}
return ret;
} /*******************************************************************************
*函数名:LinkStack_Size
*参数:LinkStack* stack 栈指针
*返回值:int 失败返回-1,成功返回栈的大小
*功能:返回链栈的大小
*******************************************************************************/
int LinkStack_Size(LinkStack* stack)
{
int ret = -1;
ret = LinkList_Length(stack);
return ret;
}
2.头文件部分
#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
3.测试代码部分
#include <stdio.h>
#include <stdlib.h>
#include "1.h" int main(int argc, char *argv[])
{
int i = 0;
int a[10];
int temp; LinkStack * stack = LinkStack_Create();
for (i = 0; i < 10; i++)
{
a[i] = i;
LinkStack_Push(stack, a + i);
} temp = LinkStack_Size(stack);
printf ("栈的大小为:%d\n", temp);
for (i = 0; i < 10; i++)
{
printf ("出栈元素为:%d\n", *(int*)LinkStack_Pop(stack));
} return 0;
}
C语言数据结构----栈的定义及实现的更多相关文章
- C语言数据结构-栈
		
一.栈的定义 栈(statck)这种数据结构在计算机中是相当出名的.栈中的数据是先进后出的(First In Last Out, FILO).栈只有一个出口,允许新增元素(只能在栈顶上增加). 移出元 ...
 - C语言数据结构-栈的实现-初始化、销毁、长度、取栈顶元素、查找、入栈、出栈、显示操作
		
1.数据结构-栈的实现-C语言 #define MAXSIZE 100 //栈的存储结构 typedef struct { int* base; //栈底指针 int* top; //栈顶指针 int ...
 - C语言数据结构----栈与递归
		
本节主要说程序中的栈函数栈的关系以及栈和递归算法的关系. 一.函数调用时的栈 1.程序调用时的栈是也就是平时所说的函数栈是数据结构的一种应用,函数调用栈一般是从搞地质向低地址增长的,栈顶为内存的低地址 ...
 - C语言数据结构----栈的应用(程序的符号匹配检测)
		
本节主要讲利用栈来实现一个程序中的成对出现的符号的检测,完成一个类似编译器的符号检测的功能,采用的是链式栈. 一.问题的提出以及解决方法 1.假定有下面一段程序: #include <stdio ...
 - C语言数据结构栈
		
#include<stdio.h>#include<stdlib.h>typedef struct Node{ int data; struct Node* pnext;}no ...
 - 数据结构——栈(C语言实现)
		
#include <stdio.h> #include <stdlib.h> #include<string.h> #include<malloc.h> ...
 - C语言数据结构——第三章 栈和队列
		
三.栈和队列 栈和队列是两种重要的线性结构.从数据结构的角度来看,栈和队列也是线性表,它的特殊性在于栈和队列的基本操作是线性表操作的子集,它们的操作相对于线性表来说是受到限制的,因此,可以称其为限定性 ...
 - C语言数据结构之栈:中缀表达式的计算
		
*注:本人技术不咋的,就是拿代码出来和大家看看,代码漏洞百出,完全没有优化,主要看气质,是吧 学了数据结构——栈,当然少不了习题.习题中最难的也是最有意思的就是这个中缀表达式的计算了(可以算+-*/和 ...
 - C语言数据结构基础学习笔记——栈和队列
		
之前我们学过了普通的线性表,接下来我们来了解一下两种特殊的线性表——栈和队列. 栈是只允许在一端进行插入或删除的线性表. 栈的顺序存储结构也叫作顺序栈,对于栈顶指针top,当栈为空栈时,top=-1: ...
 
随机推荐
- _int64、long long 的区别
			
C++的64位整数[原]by 赤兔 http://baike.baidu.com/view/1235293.htm 在做ACM题时,经常都会遇到一些比较大的整数.而常用的内置整数类型常常显得太小了:其 ...
 - 【c语言】求最大值
			
一.我个人觉得求最大值比较简单的一种方法(当然同时求最大值和最小值时稍微改改也能行) #include <stdio.h> int main(void) { int f, i, max; ...
 - AutoCAD 2013官方简体中文破解版(32 / 64位),带激活码和注册机
			
AutoCAD 2014下载地址:http://ideapad.zol.com.cn/61/160_603697.html 安装及破解方法:(注册机下载在下方) 1.安装Autodesk AutoCA ...
 - perl encode_utf8 和decode_utf8
			
encode_utf8 等于 $octets = encode_utf8($string); 这个字符串 在$string 在Perl的内部格式,返回结果是作为一个顺序的字节. 因为所有的可能的字符串 ...
 - Android 的平台碎片化问题
			
Android 的平台碎片化问题 看到篇不错的文章,转载过来. -------------------------------------- 与iOS开发相比,Android开发平添了不小的工作量,因 ...
 - notify()、notifyAll()和wait()
			
看到一道面试题,写一个多线程程序,交替输出1.2.1.2…… 先写下程序: /** * Created by Andrew on 2015/10/28. */ public class OutputT ...
 - c++,给常成员变量赋值
			
C++中,常成员变量只能在构造函数赋值,且只能通过参数列表的形式赋值,且必须在构造函数赋值. (拥有常成员变量的类的构造函数必须对所有成员变量赋值.) #include <iostream> ...
 - 编译kernel:内核makefile的作用
			
< 嵌入式linux应用完全开发手册 > 韦东山 内核Makefile的使命: 编译哪些内核文件? 读取各级子目录makefile, .config, auto.conf, Kbuild, ...
 - 使用SourceTree Push 出现 POST git-receive-pack (chunked)  的解决方法
			
在使用SourceTree上传资料的时候,遇到 POST git-receive-pack (chunked) 从 stackoverflow 看到这样一则 This is a bug in Git; ...
 - jQuery UI Widget 原理
			
先看下代码的相关注释: /*! * jQuery UI Widget 1.8.1 * * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/abo ...