zoukankan      html  css  js  c++  java
  • 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
    ");
    	}
    	/*stack -> top,栈的长度*/
    	else
    	{
    		for (i = 0; i < stack -> top; i++)
    		{
    			printf ("栈中的数据为:%d
    ", stack -> a[i]);
    		}
    	}
    }

    5.数据结点出栈

    int Pop (Sqstack *stack, int *data)
    {
    	if (stack -> top == 0)
    	{
    		printf ("the stack is empty
    ");
    		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
    ",u);
    	Pop (qq, &u);
    	printf ("弹出的元素是:%d
    ",u);
    	Pop (qq, &u);
    	printf ("弹出的元素是:%d
    ",u);
    	Pop (qq, &u);
    	printf ("弹出的元素是:%d
    ",u);
    	Pop (qq, &u);
    	printf ("弹出的元素是:%d
    ",u);
    	Pop (qq, &u);
    	printf ("%d
    ",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
    ", SeqStack_Size (stack));
    	
    	/*这里必须加上强制类型转换,因为调用函数结束以后返回的也是void *类型,所以要转换*/
    	/*printf ("栈顶元素是: %d
    ", *(int*)SeqStack_Top (stack));
    	
    	for (i = 1; i < 10; i++)
    	{
    		printf ("栈中的元素分别是:%d
    ", *(int *)SeqStack_Pop (stack));
    	} 
    	
    	
    	temp =  (int) SeqStack_Capacity(stack);
    	printf ("栈的容量为:%d
    ", temp);
    	
    	temp = SeqStack_Size(stack); 
    	printf ("栈的元素个数为:%d
    ", 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
    ", 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
    ", temp);
    	for (i = 0; i < 10; i++)
    	{
    		printf ("出栈元素为:%d
    ", *(int*)LinkStack_Pop(stack));
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    一点一点学ASP.NET系列
    深入理解JavaScript系列
    MVVM模式应用体会
    SQL查询oracle的nclob字段
    CSLA多语言设置
    用2个无线路由器桥接实现扩大无线范围方法
    DevExpress的GridControl控件设置自定义显示方法
    android配置开发环境
    warning MSB3162: 所选的“Microsoft Report Viewer 2012 Runtime”项需要“Microsoft.SqlServer.SQLSysClrTypes.11.0”。在“系统必备”对话框中选择缺少的系统必备组件,或者为缺少的系统必备组件创建引导程序包。
    GDI+实现双缓冲绘图方法一
  • 原文地址:https://www.cnblogs.com/pangblog/p/3285674.html
Copyright © 2011-2022 走看看