来源:互联网 发布:红警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
原创粉丝点击