数据结构学习笔记(6.顺序栈及链式栈)
2014-03-19 14:51
453 查看
本节知识点:
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; }
相关文章推荐
- C++ —— 数据结构之 顺序栈,链式栈?
- C#简单实现顺序栈与链式栈
- 数据结构(顺序单链表、链式单链表、顺序栈、链式栈、顺序队列、链式队列)
- 数据结构--顺序栈和链式栈
- 栈:顺序栈和链式栈
- 顺序栈和链式栈的结构及其基本操作(置空,获取栈顶元素,入栈,出栈)
- 数据结构——栈、顺序栈、双栈共享同一栈空间、链式栈
- 栈的实现 -- 顺序栈和链式栈(C++描述)
- 数据结构学习笔记(三) 树形结构之一般二叉树的顺序存储_二叉链表表示法_转换
- 数据结构笔记之用C++实现顺序栈和链式栈
- 完成顺序栈,,链式栈的基本操作
- 数据结构学习笔记(六)-- 栈的顺序结构
- 数据结构学习笔记(3)线性表-顺序映像
- 数据结构课设--栈(顺序栈,链式栈)
- 【数据结构】C语言实现顺序栈和链式栈
- 数据结构之栈(顺序栈和链式栈)
- 数据结构学习笔记(二)顺序表链式表示
- 数据结构学习笔记(顺序表的基本操作)
- 栈:顺序栈和链式栈
- Java栈的实现(顺序栈、链式栈)及栈的应用