栈
来源:互联网 发布:红警3起义时刻兵种数据 编辑:程序博客网 时间:2024/05/29 04:53
前面聊过了链表,接下来聊一下栈和队列这两种常见的数据结构,先通过下面的图比较一下:线性表 栈 队列:
区别嘛,都在图上了,需要仔细看喽,所以就不在这里文字啰嗦了。
在本篇文章中主要是聊聊栈,什么是栈?还是看图比较直观一些:
栈有顺序栈和链栈,顺序栈由于有长度限制的局限性,在实际开发中链栈的使用比较广泛,所以这里主要讨论链栈。在讨论链表的时候,常常会提到头结点,但在栈种如果将栈顶放在单链表的头部,出栈入栈直接通过操作修改栈顶即可,所以头结点也就失去了意义了。但是在创建栈的时候会创建一个空结点,数据域为空(无定义),指针域指向NULL。初始的时候栈顶栈底都指向这个空结点,然后随着栈的向上增长,就是栈底指向这个空结点了,所以在栈中比较重要的不是头结点而是栈底的空结点(其实这个空结点就是单链表的头结点,然后单链表向后增长)。当然也可以将栈底的空结点放入数据,不过我认为还是不放的为好!下面还是老规矩在代码中详细讨论栈吧!
#include<stdio.h>#include<stdlib.h>#include<malloc.h>//定义结点typedef struct Node{ int data; //数据域 struct Node * pNext; //指向下一结点的指针} NODE, * PNODE; //NODE等价于struct Node, PNODE等价于struct Node *//定义栈typedef struct Stack{ PNODE pTop; //栈顶结点 PNODE pBottom; //栈底结点 int size; //用于记录栈的大小} STACK, * PSTACK; //STACK等价于struct Stack, PSTACK等价于struct Stack *//栈操作函数声明void initStack(PSTACK pStack); //栈初始化,建立一个空栈void pushStack(PSTACK pStack, int val); //入栈bool popStack(PSTACK pStack, int * pVal); //出栈,*pVal用来保存出栈的元素内容bool GetTop(PSTACK pStack,int * pVal); //返回栈顶元素,*pVal用来保存返回的栈顶元素int GetSize(PSTACK pStack); //返回栈的大小void traverseStack(PSTACK pStack); //遍历栈bool isEmpty(PSTACK pStack); //判断栈是否为空void clearStack(PSTACK pStack); //清空栈void DestroyStack(PSTACK pStack) //销毁栈/*栈初始化,建立一个空栈*/void initStack(PSTACK pStack){ pStack = (PSTACK)malloc(sizeof(STACK)); //建立栈 pStack->pTop = (PNODE)malloc(sizeof(NODE)); //创建一个空结点,让pTop指向它 if(NULL != pStack->pTop) //如果内存分配成功 { pStack->pBottom = pStack->pTop; //将pBottom也指向空节点 pStack->pTop->pNext = NULL; //清空空结点的指针域 pStack->size = 0; //初始化栈的大小为0 } else //如果内存分配失败 { printf("内存分配失败!程序退出!\n"); exit(-1); } return;}/*入栈*/void pushStack(PSTACK pStack, int val){//在看下面的入栈算法前小小提示一下:栈是向上增长的哦! PNODE pNew = (PNODE)malloc(sizeof(NODE)); // 创建一个新结点 pNew->data = val; // 设置新结点的数据域的值 pNew->pNext = pStack->pTop; //设置新结点的next指针指向原来栈顶pTop结点 pStack->pTop = pNew; //设置栈顶pTop指向新结点 pStack->size++; return;}/* 出栈 */bool popStack(PSTACK pStack, int * pVal){ if(isEmpty(pStack)) //首先判断当前栈是否为空 { return false; } else { PNODE rNode = pStack->pTop; //先保存栈顶元素的地址 *pVal = rNode->data; //获取要出栈的数据 pStack->pTop = rNode->pNext; //然后将pTop指向栈内下一元素 free(rNode); //最后释放之前栈顶元素的内存,并将其指针设为空指针 rNode = NULL; pStack->size--; return true; }}/* 返回栈顶元素,*pVal用来保存返回的栈顶元素,如果成功返回true,否者返回false */bool GetTop(PSTACK pStack,int * pVal){if( !isEmpty(pStack) ){ *pVal = pStack->pTop->data; return true;}elsereturn false;}/* 返回栈的大小 */int GetSize(PSTACK pStack){if(isEmpty(pStack)) //首先判断当前栈是否为空{ return pStack->size;}} /* 遍历栈 */void traverseStack(PSTACK pStack){//声明一个临时结点为栈顶结点的替身用于后面的遍历,以避免修改栈顶 PNODE pNode = pStack->pTop; while(pStack->pBottom != pNode ) //循环遍历栈,直到栈底 { printf("%d ", pNode->data); pNode = pNode->pNext; } printf("\n"); return;}/* 判断栈是否为空 */bool isEmpty(PSTACK pStack){ if(pStack->pTop == pStack->pBottom) //判断栈为空的条件就是看栈顶和栈底是否指向同一个结点 return true; else return false;}/* 清空栈 */void clearStack(PSTACK pStack){ if(isEmpty(pStack)) //首先判断当前栈是否为空 { return; } else { //两个结点指针变量用来释放栈中元素的内存 PNODE p = pStack->pTop; PNODE q = NULL; while(p != pStack->pBottom) //循环释放栈中所有元素的内存 { q = p->pNext; free(p); p = q; } pStack->pTop = pStack->pBottom; //设置将栈顶指向栈底 pStack->size = 0; //将栈的大小清零 return; }}/* 销毁栈 */void DestroyStack(PSTACK pStack) { if(isEmpty(pStack)) //如果当前栈不为空 { clearStack(pStack); //清空当前栈 free(pStack->pTop); //销毁空结点 pStack->pTop = pStack->pBottom = NULL; //将栈中的头底结点指针置空,避免野指针 free(pStack); //销毁栈 }}/* 测试主函数 */int main(void){ STACK stack; //定义一个栈变量,STACK等价于struct Stack int val; //用来保存出栈的内容 initStack(&stack); //调用栈的初始化函数 pushStack(&stack, 10); //调用入栈的函数 pushStack(&stack, 20); pushStack(&stack, 30); pushStack(&stack, 50); traverseStack(&stack); //调用遍历栈的函数 //调用出栈的函数 if(popStack(&stack, &val)) printf("出栈成功,出栈的元素值为:%d\n", val); else printf(" 出栈失败!"); //调用清空栈的函数 clearStack(&stack); traverseStack(&stack); //调用遍历栈的函数 system("pause"); return 0;}
因为栈本质上是只能在表尾插入和删除的线性表,所以它的时间复杂度是O(1),空间复杂度的话,顺序栈会存在空间浪费问题,链栈虽然每个元素增加了指针域,在这方面增加了内存的开销,但是它对长度的控制比较精确。
0 0
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- 栈
- linux下使用yum安装mysql
- C连接交换指定符号extern “C”
- .NET 根据下拉列表选择不同的值动态改变对应textbox的属性
- SQL Server中的三种物理连接操作
- mysql密码的加密与解密
- 栈
- Java线程面试题 Top 50
- jQuery 淡入淡出
- 深入浅出MFC学习笔记,进程与线程简介
- SQLServer基本函数
- Python 获取最长单词的两种方法
- Xshell远程连接Linux服务器出错——Could not connect to '114.214.166.5' (port 22): Connection failed.
- ViewPager禁止左右滑动
- Dex 文件格式详解