数据结构学习笔记(6.顺序栈及链式栈)

本节知识点:

1.数据结构中栈的定义:栈就是一种特殊的线性表,满足后进先出(LIFO)规则的线性表,既然是线性表,就存在顺序栈和链式栈!
2. 栈仅能在线性表的一端进行操作:栈顶(允许操作的一端),栈底(不允许操作的一端)
如图:

3. 顺序栈是在顺序表的基础上进行了一层代码封装而实现的,链式栈是在单链表的基础上封装得到的!需要注意的是,对于顺序表我们要将,顺序表尾部当做栈顶,因为如果放在头部的话,出栈和入栈的时间复杂度是O(n)这样会使整个顺序表都跟着移动,放在尾部就不会,时间复杂度是O(1)。对于链表我们要将,链表头部当做栈顶,因为这样出栈和入栈操作不需要便利整个链表,时间复杂度为O(1)。

示例代码:

顺序栈:SeqStack.h文件:
#ifndef __SeqStack_H__
#define __SeqStack_H__

typedef void SeqStack;

SeqStack* Creat_SeqStack(int capacity);

void  Destroy_SeqStack(SeqStack* stack);

int Get_SeqStack_Length(SeqStack* stack);

int Get_SeqStack_Capacity(SeqStack* stack);

int Clean_SeqStack_Length(SeqStack* stack);

int SeqStack_Push(SeqStack* stack, void* node);

void* Get_SeqStack_Top(SeqStack* stack);

void* SeqStack_Pop(SeqStack* stack); 

#endif


SeqStack.c文件:
/************************************************************************************ 
文件名:SeqStack.c
头文件:SeqStack.h 
时间: 2013/08/30 
作者: Hao 
功能:基于可以复用 带有增 删 改 查 功能的顺序表 创建的顺序栈  
************************************************************************************/

#include "SeqStack.h"
#include "Seqlist.h"

/************************************************************************************ 
函数名:   Creat_SeqStack
函数功能: 创建一个容量为capacity的顺序栈 
参数:     顺序栈的容量 
返回值:   成功返回  顺序栈的起始地址   失败返回 NULL 
************************************************************************************/
SeqStack* Creat_SeqStack(int capacity)
{
	return Creat_SeqList(capacity);
}

/************************************************************************************ 
函数名:   Destroy_SeqSeqStack
函数功能: 销毁顺序栈 
参数:     void* stack 描述顺序栈结构体指针 
返回值:   无
************************************************************************************/
void  Destroy_SeqSeqStack(SeqStack* stack)
{
	Destroy_SeqList(stack);
} 

/************************************************************************************ 
函数名:   Get_SeqStack_Length
函数功能: 获得顺序栈的长度 
参数:     void* stack 描述顺序栈结构体指针  
返回值:   int ret  成功返回length
                    失败返回-1 
************************************************************************************/
int Get_SeqStack_Length(SeqStack* stack)
{
	return Get_Seqlist_Length(stack); 
}	

/************************************************************************************ 
函数名:   Get_SeqStack_Capacity
函数功能: 获得顺序栈的总容量 
参数:     void* stack 描述顺序栈结构体指针  
返回值:   int ret  成功返回capacity 
                    失败返回-1 
************************************************************************************/
int Get_SeqStack_Capacity(SeqStack* stack)
{
	return Get_Seqlist_Capacity(stack);
}

/************************************************************************************ 
函数名:   Clean_SeqStack_Length
函数功能: 清空顺序栈  其实就是给length=0; 
参数:     void* stack 描述顺序栈结构体指针  
返回值:   int ret  成功返回0
                    失败返回-1
************************************************************************************/
int Clean_SeqStack_Length(SeqStack* stack)
{
	return Clean_Seqlist_Length(stack);
}

/************************************************************************************ 
函数名:   SeqStack_Push
函数功能: 压栈操作 将一个元素压入顺序栈中 
参数:     void* stack 描述顺序栈结构体指针    
           void* node  要压入栈中的元素地址 
返回值:   int ret  成功返回1
                    失败返回0 
注意:     对于顺序栈的栈顶  要选择顺序表的尾部 
           因为这样push和pop操作的时间复杂度是O(1) 
		   避免了插入头部和删除头部过程中不必要的操作 
************************************************************************************/
int SeqStack_Push(SeqStack* stack, void* node)
{
	return Seqlist_Add(stack, node ,Get_Seqlist_Length(stack));
}

/************************************************************************************ 
函数名:   Get_SeqStack_Top
函数功能: 获得顺序栈的栈顶元素 
参数:     void* stack 描述顺序栈结构体指针  
返回值:   void*  成功返回  栈顶元素的地址 
                  失败返回  NULL 
************************************************************************************/
void* Get_SeqStack_Top(SeqStack* stack)
{
	return Get_Node(stack, Get_Seqlist_Length(stack)-1);
}

/************************************************************************************ 
函数名:   SeqStack_Pop
函数功能: 将顺序栈的栈顶元素 弹出栈 
参数:     void* stack 描述顺序栈结构体指针  
返回值:   void*  成功返回  栈顶元素的地址 
                  失败返回  NULL 
************************************************************************************/
void* SeqStack_Pop(SeqStack* stack)
{
	return Del_Node(stack, Get_Seqlist_Length(stack)-1);
}






main.c文件:
#include <stdio.h>
#include <stdlib.h>
#include "SeqStack.h"

typedef struct _tag_str
{
	int a;
	int b;
}str;
int main(int argc, char *argv[])
{
	str str1 = {1,1};
	str str2 = {2,2};
	str str3 = {3,3};
	SeqStack* stack = Creat_SeqStack(15); 
	
	SeqStack_Push(stack, &str1);
	SeqStack_Push(stack, &str3);
	SeqStack_Push(stack, &str2);
	
	printf("%d\n",((str*)Get_SeqStack_Top(stack))->a);
	printf("Length: %d \n",Get_SeqStack_Length(stack));
	printf("Capacity: %d \n",Get_SeqStack_Capacity(stack));
	
	printf("%d\n",((str*)SeqStack_Pop(stack))->a);
	printf("%d\n",((str*)SeqStack_Pop(stack))->a);
	printf("%d\n",((str*)SeqStack_Pop(stack))->a);
	
	Destroy_SeqSeqStack(stack);
	return 0;
}


链式栈: LinkStack.h文件:
#ifndef __LinkStack_H__
#define __LinkStack_H__
#include "LinkList.h"
typedef void LinkStack;

typedef struct _tag_Str_LinkStack //这个是栈的结构 
{
	LinkListNode node; //node用来连接链表 
	void* StackData;   //StackData用来保存栈中数据的地址 
}LinkStackNode;

LinkStack* Creat_LinkStack(void);

void  Destroy_LinkStack(LinkStack* stack);

int Get_LinkStack_Length(LinkStack* stack);

int Clean_LinkStack(LinkStack* stack);

int LinkStack_Push(LinkStack* stack, void* node);

void* Get_LinkStack_Top(LinkStack* stack);

void* LinkStack_Pop(LinkStack* stack); 

#endif

LinkStack.c文件:
/***************************************************************************************
文件名:LinkStack.c
头文件:LinkStack.h 
时间: 2013/08/31
作者: Hao
功能:  基于可以复用 带有增 删 改 查 功能的单链表 写的链式栈 
****************************************************************************************/

#include "LinkList.h"
#include "LinkStack.h"
#include <malloc.h>
#include <stdlib.h> 


/****************************************************************************************
函数名:  Creat_LinkStack
函数功能:创建链式栈 返回栈头(这个就是对栈操作的一个对象而已) 
参数:    无
返回值:  返回栈头 
*****************************************************************************************/
LinkStack* Creat_LinkStack(void)
{
	return Creat_LinkListHead();
}
 
/****************************************************************************************
函数名:  Get_LinkStack_Length
函数功能:获得栈的长度 
参数:    栈头 
返回值:  成功返回链表长度  失败返回0 
*****************************************************************************************/ 
int Get_LinkStack_Length(LinkStack* stack)
{
	return Get_Length(stack);
} 
 
/****************************************************************************************
函数名:  Clean_LinkStack
函数功能:清除链式栈中的所有数据  只剩下栈头 
参数:    栈头 
返回值:  成功返回1  失败返回0 
*****************************************************************************************/  
int Clean_LinkStack(LinkStack* stack)
{
	int ret = 1;
	void* temp = NULL;
	while(Get_LinkStack_Length(stack)) 
	{
		temp = LinkStack_Pop(stack);
		if(NULL == temp)
		{
			ret = 0;
			break;
		}
	}
	return ret;
} 
 
/****************************************************************************************
函数名:  Destroy_LinkStack
函数功能:清空栈 并销毁栈头
参数:    栈头 
返回值:  void 
*****************************************************************************************/
void  Destroy_LinkStack(LinkStack* stack)
{
	Clean_LinkStack(stack); //先清除链式栈中的所有数据 
	Destroy_LinkListHead(stack); //在清除栈头 
}

/****************************************************************************************
函数名:  LinkStack_Push
函数功能:压栈操作 把数据压入栈中 
参数:    LinkStack* stack栈头   void* Node要压入栈中的数据的地址 
返回值:  压栈成功返回1  压栈失败返回0
*****************************************************************************************/
int LinkStack_Push(LinkStack* stack, void* node)
{
	int ret = 1;
	if(NULL != node)
	{
		/*以前的这个结构是需要定义在数据中的结构体的*/ 
		LinkStackNode* data = (LinkStackNode*)malloc(sizeof(LinkStackNode));
		if(NULL != data)
		{
			data->StackData = node;
			ret = Add_LinkList(stack, (LinkListNode*)data, 0); //头插  头取 
		}
		else 
		{
			ret = 0;
		}
		
	}
	else
	{
		ret = 0;
	}
	return ret;
} 

/****************************************************************************************
函数名:  LinkStack_Pop
函数功能:出栈操作 把数据从栈中弹出 
参数:    LinkStack* stack栈头    
返回值:  出栈成功返回栈顶数据地址  出栈失败返回NULL 
*****************************************************************************************/
void* LinkStack_Pop(LinkStack* stack)
{
	 void* ret = NULL;
	 LinkStackNode* temp = (LinkStackNode*)Del_LinkListNode(stack, 1); // 头插  头取 
	 if(NULL != temp)
	 {
			ret = temp->StackData; 
			free(temp);
	 } 
	 return ret;
}

/****************************************************************************************
函数名:  Get_LinkStack_Top
函数功能:获得栈顶数据 
参数:    LinkStack* stack栈头    
返回值:  成功获得栈顶数据  返回栈顶数据地址  失败返回NULL 
*****************************************************************************************/
void* Get_LinkStack_Top(LinkStack* stack)
{
	 void* ret = NULL;
	 LinkStackNode* temp = (LinkStackNode*)Get_LinkListNode(stack, 1); // 头插  头取 
	 if(NULL != temp)
	 {
			ret = temp->StackData; 
	 } 
	 return ret;
} 

 

main.c文件:
#include <stdio.h>
#include <stdlib.h>
#include "LinkStack.h"

int main(int argc, char *argv[]) 
{
	LinkStack*  stack = Creat_LinkStack();
	int a = 9;
	int b = 12;
	int c = 1;
	
	LinkStack_Push(stack,&a);
	LinkStack_Push(stack,&b);
	LinkStack_Push(stack,&c);
	
	printf("%d\n",Get_LinkStack_Length(stack));
	
	printf("%d\n",*(int*)Get_LinkStack_Top(stack));
	
	printf("%d\n",*(int*)LinkStack_Pop(stack));
	printf("%d\n",*(int*)LinkStack_Pop(stack));
	printf("%d\n",*(int*)LinkStack_Pop(stack));
	Destroy_LinkStack(stack);
	return 0;
}
注意:链式栈和单链表有了一个小区别,就是单链表的插入数据的格式是固定的,每个结构体中都必须存在LinkListNode 链表结构,否则链表的链接会失败!这样就导致一些基本类型的单链表存入数据很不方便!但是链式栈封装掉了这个问题,它自己malloc了一个结构,把要存入的数据地址和LinkListNode链表结构存在了一起,这个步骤是封装掉的,用户是看不见的!


链表的升级:根据上面说的问题,我决定把单链表也改成像链式栈一样的处理方式,让用户根本就不知道有LinkListNode这样一个链表结构的存在,当然这样做的代价就是需要额外提供一个void* 的内存空间,来保存数据的地址!而且LinkListNode的空间变成了堆区的空间!
typedef struct Str_LinkList LinkListNode;  //这个结构体是链表的真身 
struct Str_LinkList   //每一个链表元素的结构都会包含这个结构  因为当给链表元素强制类型 
{                     //转换成(LinkListNode* )的时候  其实就是要开始对每个元素中的 LinkListNode进行赋值了 
	LinkListNode* next;
	void* listdata;
};

新单链表: LinkList.h文件:
#ifndef __LinkList_H__
#define __LinkList_H__

typedef void LinkList;  //这个是为了 封装方便 
typedef struct Str_LinkList LinkListNode;  //这个结构体是链表的真身 
struct Str_LinkList   //每一个链表元素的结构都会包含这个结构  因为当给链表元素强制类型 
{                     //转换成(LinkListNode* )的时候  其实就是要开始对每个元素中的 LinkListNode进行赋值了 
	LinkListNode* next;
	void* listdata;
};

LinkList* Creat_LinkListHead(void);

int Destroy_LinkListHead(LinkList* head);

int Get_Length(LinkList* head);

int Clean_LinkListHead(LinkList* head);

int Add_LinkList(LinkList* head, void* data, int pos);

void* Get_LinkListNode(LinkList* head, int pos);

void* Del_LinkListNode(LinkList* head, int pos); 
 
#endif

LinkList.c文件:
/*******************************************************************************************************
文件名:LinkList.c
头文件:LinkList.h 
时间: 2013/08/07
作者: Hao
功能:  可以复用 带有增 删 改 查 功能的单链表
难道: 1.typedef struct Str_LinkList LinkListNode;  //这个结构体是链表的真身 
		struct Str_LinkList   //每一个链表元素的结构都会包含这个结构  因为当给链表元素强制类型 
		{                     //转换成(LinkListNode* )的时候  其实就是要开始对每个元素中的 LinkListNode进行赋值了 
			LinkListNode* next;
		}; 
		这个链表结构在链表元素中起到的作用 是本节的难点 
		2.切记一个问题  就是已经是链表中元素的 千万不要再往链表中添加了 否则链表一定出现无穷的错误 
		3.对于pos值的问题  add、get、del三个函数中 的链表都是 从1开始的到length  0是链表头 
						  在add函数中pos为0的时候是和pos为1的情况是一样的  都是头插法  0~~~~~无穷大 
		                  在get函数中pos为0的时候是获得链表头 地址      0~~~~~length 
						  在del函数中pos为0的时候是无效的 del失败       1~~~~~length 
*******************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "LinkList.h"

typedef struct str_list_head  //这个是链表头 其实也可以当作一个没有前驱的 链表元素 元素的内容是链表长度 
{
	//LinkListNode* next;
	LinkListNode head; //这个参数要特别重视 每一个链表元素结构的第一个参数一定是 LinkListNode
	                   //因为在寻找链表元素后继的时候 其实就是将链表元素强制类型转换成 LinkListNode*  然后给next进行赋值 其实就是给 LinkListNode变量赋值 
	int length; //链表长度 
}list_head;

/*******************************************************************************************************
函数名: Creat_LinkListHead
函数功能:创建一个链表的链表头 并给链表头分配空间
参数: void
返回值:ret 成功返回链表头地址  失败返回NULL 
*******************************************************************************************************/
LinkList* Creat_LinkListHead(void)
{
	list_head* ret = NULL;
	ret = (list_head* )malloc( sizeof(list_head)*1 );
	if(NULL != ret) //malloc分配成功 
	{
		ret -> length = 0;
		//ret -> next = NULL;
		ret -> head.next = NULL;
	}
	return (LinkList* )ret; 
}

/*******************************************************************************************************
函数名:Destroy_LinkListHead
函数功能:释放一个链表头指针 
参数:LinkList* head 链表头指针 
返回值: ret 释放成功返回1  释放失败返回0 
*******************************************************************************************************/
int Destroy_LinkListHead(LinkList* head)
{
	int ret = 0; 
	list_head* lhead = (list_head* )head;
	if( NULL != lhead )
	{
		Clean_LinkListHead(lhead);
		free(lhead);
		ret = 1;
	}
	return ret;
}

/*******************************************************************************************************
函数名:Get_Length
函数功能:获得链表的长度 
参数: LinkList* head 链表头指针 
返回值: ret 成功返回链表长度  失败返回0 
*******************************************************************************************************/
int Get_Length(LinkList* head) 
{
	int ret = 0;
	list_head* lhead = (list_head* )head;
	if( NULL != lhead )
	{
		ret = lhead -> length;
	}	
	return ret;
}

/*******************************************************************************************************
函数名:Clean_LinkListHead
函数功能:	清空链表 
参数: LinkList* head 链表头指针 
返回值:ret 成功返回1 失败返回0 
*******************************************************************************************************/
int Clean_LinkListHead(LinkList* head) 
{
	int ret = 0;
	list_head* lhead = (list_head* )head;
	if( NULL != lhead )
	{
		while(Get_Length(lhead))
		{
			Del_LinkListNode(lhead, 1);
		}
		
		lhead -> length = 0;
		//lhead -> next = NULL;
		lhead -> head.next = NULL;
		ret = 1;
	}	
	return ret;
}

/*******************************************************************************************************
函数名:Add_LinkList
函数功能:往链表里面添加一个链表元素 如果pos的值是0(就是链表头)和1(链表的第一元素 链表元素个数是从1开始算的)都是头插法
          pos的值大于链表长度是尾插法  这里面pos值得注意的是 i=1 pos为a的时候 是把链表元素插入第a个元素的位置 
          当i=0 pos为a的时候 是把链表元素插入 第a个元素位置的后面    切忌:这里面0位置是链表头指针 从1开始是链表元素 
参数:   LinkList* head链表头指针    void* data 要插入数据的地址  int pos 插入位置 
         pos的有效值范围是 从0到无穷大  
返回值: ret 插入成功返回1  插入失败返回0 
*******************************************************************************************************/
int Add_LinkList(LinkList* head, void* data, int pos)
{
	int ret = 0;
	int i = 0;
	LinkListNode* Node = (LinkListNode*)malloc(sizeof(LinkListNode));
	list_head* lhead = (list_head* )head;
	LinkListNode* node = (LinkListNode* )head;
	ret=( NULL != node) && ( NULL != Node) && (pos >= 0) && (NULL != Node);
	if(1 == ret)
	{
		for(i=1; ( (i<pos) && (node->next != NULL) ); i++)
		{
			node = node->next;
		}
		Node -> next = node -> next;
		node -> next = Node;
		
		lhead -> length++; 
		Node->listdata = data;
	}
	return ret;
}

/*******************************************************************************************************
函数名:Get_LinkListNode
函数功能:获得链表中第pos个元素位置的链表元素 链表是从1开始的  0是链表头   pos为0的时候表示get链表头 
参数: LinkList* head链表头指针    int pos获得链表元素的位置  pos的有效取值范围是 1 到  length  0是链表头 
返回值: void*类型 第pos个链表中元素的地址 
*******************************************************************************************************/
void* Get_LinkListNode(LinkList* head, int pos)
{
	int ret = 0;
	int i = 0;
	list_head* lhead = (list_head* )head;
	ret=( NULL != lhead) && (pos >= 0) && (pos <= lhead->length);
	if(1 == ret)
	{
		LinkListNode* node = (LinkListNode* )head;
		for(i=0; i<pos; i++) //执行 pos次   得到的是第pos位置的node 
		{
			node = node->next;
		}	
		return node->listdata;
	}
	return NULL;
}

/*******************************************************************************************************
函数名:Del_LinkListNode
函数功能:删除链表中第pos位置的链表元素 
参数: LinkList* head链表头指针    int pos删除链表元素的位置  pos是删除的链表元素的位置 跟get和add中的
       pos是配套的  有效取值范围依然是 1到 length  在这个函数里面由于不能删除链表头 所以pos为0的时候无效 
返回值: LinkListNode* ret这个返回值很重要 因为这个删除仅仅是把链表元素踢出了链表 并没有free开辟的内存
         应该通过这个返回的地址free  释放内存
		 删除成功返回 删除链表元素的地址   删除失败返回 NULL 
*******************************************************************************************************/
void* Del_LinkListNode(LinkList* head, int pos)
{
	void* temp = NULL;
	LinkListNode* ret = NULL;
	int i = 0;
	list_head* lhead = (list_head* )head;
	if(( NULL != lhead) && (pos > 0) && (pos <= lhead->length))
	{
		LinkListNode* node = (LinkListNode* )head;
		for(i=1; i<pos; i++)//执行 pos次   得到的是第pos位置的node  这个方法行不通 
		{                   //因为要想删除第pos位置的node 应该先找到它上一个链表元素 
			node = node->next; //所以这里面i=1 比get函数少执行了一次  得到第pos-1位置的node 
		}
		ret = node->next;
		node->next = ret->next;	
		
		lhead->length--;
		
		temp = ret->listdata;
		free(ret);
	}
	return temp;
}



main.c文件:
#include <stdio.h>
#include <stdlib.h>
#include "LinkList.h"

int main(int argc, char *argv[]) 
{
	int a = 1;
	int b = 99;
	int c = 20;
	
	LinkList* list =  Creat_LinkListHead();
	
	Add_LinkList(list, &a, 0);
	Add_LinkList(list, &b, 0);
	Add_LinkList(list, &c, 0);
	
	printf("%d\n",Get_Length(list));
	
	printf("%d\n",*(int*)Get_LinkListNode(list, 1));
	printf("%d\n",*(int*)Get_LinkListNode(list, 2));
	printf("%d\n",*(int*)Get_LinkListNode(list, 3));
	
	Destroy_LinkListHead(list);
	return 0;
}





  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值