zoukankan      html  css  js  c++  java
  • 栈学习笔记

    栈:

      栈是限定仅在表尾进行插入和删除操作的线性表。遵循先进后出的原则。允许插入、删除的一端为栈顶top;另一端为栈底bottom。

      栈,首先它是一个线性表,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表。特殊之处在于限制了这个线性表的插入和删除位置,它始终都是在栈顶进行。栈底是固定的,最先进入的只能在栈底。

      1.栈的顺序存储结构:

      对于栈只能一头插入、删除操作的线性表来说,用数组的下标为0的一端作为栈底,定义一个top指示栈顶元素在数组中位置,如果申请的数组长度为SIZE,则栈顶的位置必须小于SIZE,当栈中存在一个元素时,top = 0。栈满时,top = SIZE - 1;

      定义栈的结构体:

    1 typedef int datatype;
    2 typedef struct 
    3 {
    4     datatype data[SIZE]; 
    5     int top;
    6 }sqstack;

      栈的入栈操作:

     1 int push(sqstack *s, datatype x)
     2 {
     3     if (s->top == SIZE - 1)
     4     {
     5         printf("stack full\n");
     6         return -1;
     7     }
     8     s->top++;
     9     s->data[s->top] = x;
    10     return 0;
    11 }

      栈的出栈操作:

     1 int pop(sqstack *s)
     2 {
     3     int ret;
     4     if (s->top == -1)
     5     {
     6         printf("stack empty\n");
     7         return -1;
     8     }
     9     ret = s->data[s->top];
    10     s->top--;
    11     return 0;
    12 }

      2.两个栈共享空间

      栈的顺序存储操作起来还是很方便的,毕竟它只需操作栈顶top进出元素,因此,也就不存在插入删除时需要移动大量元素的问题。但是它还是存在一个缺陷的,就是你必须先确定好数组存储空间的大小,如果不够用,还得通过软件编程去扩充数组的大小,这也带来了麻烦。

      如果有两个相同类型的栈,我们各自为其开辟了空间,有可能是其中一个栈满了,而另一个栈还有很多的存储空间,想想,我们可以通过构建一个数组来存储这两个栈,其中一个栈的栈底在数组的下标0的位置,另一个栈的栈底在数组的下标为SIZE - 1的位置,这样当两个栈都进行入栈操作时,数据元素都向数组中间延伸。这是我们必须判断什么时候数组满和空的。我们假定其中一个栈的栈顶为top1,另一个为top2,当栈1为空时,top1 = -1,栈2为空时top2 = SIZE。栈满的情况①若栈1为空,即top1 = -1,栈2的top2 = 0时,数组满;②当栈2为空,即top2 = SIZE,栈1的top1 = SIZE - 1时,数组满。③当栈1和栈2都向入栈,最终数组满的情况是:top1 + 1 == top2,即两个栈的栈顶top相差1时,栈满。

      定义两个栈共享空间结构:

    1 typedef int datatype;
    2 typedef struct 
    3 {
    4     datatype data[SIZE];
    5     int top1;
    6     int top2;
    7 }sqstack;

      创建操作:

    1 void CreateStack(sqstack *s)
    2 {
    3     s->top1 = -1;     //栈1为空
    4     s->top2 = SIZE;  //栈2为空
    5    return ;
    6 }

      入栈操作:思路是判断是栈1还是栈2要入栈,如是栈1则先将栈1的top1+1后,将元素入栈;若是栈2入栈的话,则先将栈2的top2-1,在将元素入栈。

     1 int push(sqstack *s, datatype x, int number)
     2 {
     3     if (s->top1 + 1 == s->top2)
     4     {
     5         printf("stack full\n");
     6         return -1;
     7     }
     8     switch (number)
     9     {
    10         case 1:
    11             s->top1++;
    12             s->data[s->top] = x;
    13             break;
    14         case 2:
    15             s->top2--;
    16             s->data[s->top] = x;
    17             break;
    18         default:
    19             break;
    20     }
    21     return 0;
    22 }

      出栈操作:主要思路是判断要操作的栈是否为空,如果不为空,将其栈顶元素出栈,最后将栈1的栈顶top1-1,栈2的栈顶top2+1.

     1 int pop(sqstack *s, datatype *x, int number)
     2 {
     3     switch (number)
     4     {
     5         case 1:
     6             if (s->top1 == -1)    
     7                 return -1;
     8             *x = s->data[s->top1];
     9             s->top1--;
    10             break;
    11         case 2:
    12             if (s->top2 == SIZE)
    13                 return -1;
    14             *x = s->data[s->top2];
    15             s->top2++;
    16             break;
    17         default:
    18             break;
    19     }
    20     return 0;
    21 }

      3.栈的链式存储结构 

      栈的链式存储结构也称链栈。对于链栈来说,基本上不存在栈满的情况。链栈为空时top = NULL;

      链栈的结点和栈类型定义:

      其中栈结点的类型,包含一个数据域和一个指针域,指针域用于保存下一结点的地址。而栈类型的结构定义包含一个栈结点类型的指针top和栈的元素个数number。

     1 typedef int datatype;
     2 typedef struct stacknode
     3 {
     4     datatype data;
     5     struct stacknode *next;
     6 }stacknode, *stackptr;
     7 
     8 typedef struct linkstack
     9 {
    10     stackptr top;
    11     int number;
    12 }linkstack;

      创建一个链栈:

      申请一个内存地址,链栈为空时s->top = NULL,元素个数number = 0.返回一个栈类型的指针。

     1 linkstack *CreateLinkStack()
     2 {
     3     linkstack *s = (linkstack *)malloc(sizeof(linkstack));
     4     if (s != NULL)
     5     {
     6         s->top = NULL;
     7         s->number = 0;
     8     }
     9     return s;
    10 }

      链栈的入栈操作:入栈操作,①要申请一个新的结点sp(sp中包含了数据域data和指针域next),②将要入栈的元素的值赋值给sp->data,即放入新结点的数据域中。③将当前栈顶元素赋值给新结点的直接后继ps->next = s->top。④将新结点ps赋值给栈顶指针s->top = ps。⑤栈元素个数number+1.

     1 int push(linkstack *s, datatyped e)
     2 {
     3     stackptr sp = (stackptr)malloc(sizeof(stacknode));
     4     if (sp == NULL)
     5         return -1;
     6     sp->data = e;
     7     ps->next = s->top;
     8     s->top = ps;
     9     s->number++;
    10     return 0;
    11 }

      链栈的出栈操作:①先判断链栈是否为空栈,s->top = NULL 时为空。②保存出栈值*e = s->top->data。③将栈顶结点赋值给ps。④使栈顶指针向下移动一位,指向后一结点s->top = s->top->next;⑤释放掉ps。⑥栈元素个数number-1.

     1 int pop(linkstack *s, datatype *e)
     2 {
     3     stackptr sp;
     4     if (s->top == NULL)
     5         return -1;
     6     *e = s->top->data;
     7     ps = s->top;
     8     s->top = s->top->next;
     9     free(sp);
    10     s->number--;
    11     return 0;
    12 }

      返回栈顶元素:链栈不为空的条件下,返回栈顶元素。

    1 int GetTop(linkstack *s, datatype *e)
    2 {
    3     if (s->top == NULL && s->number == 0 && e == NULL)
    4         return -1;
    5     *e = s->top->data;
    6     return 0;
    7 }

      比较下,栈的链式存储和顺序存储,它们的时间复杂度都是O(1),从空间上考虑,顺序栈必须实现分配一个固定的长度,可能存在内存浪费的问题,但是顺序栈存取十分方便;链栈则要求每个元素都有指针域和数据域,这样也增加了内存的开销,但是链栈不受长度的限制。所以呢,在选择的时候,就要依实际情况来决定,如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好用链栈,反之,如果它的变化在可控制范围内,建议使用顺序栈会更好。

      2013-1-26 15:58

  • 相关阅读:
    Linux下MySQL数据库常用基本操作 一
    Cdnbes负载均衡的权重用法解释
    docker安装
    centos网卡配置和防火墙停止和启动
    Excel 如何锁定表头
    权值线段树 基础入门知识详解
    JZOJ 3362. 【NOI2013模拟】数数(DFS)
    JZOJ 3348. 【NOI2013模拟】秘密任务(最短路+最小割唯一性)
    JZOJ 3303. 【集训队互测2013】城市规划(卷积+分治NTT)
    FFT快速傅里叶变换(超详细的入门学习总结)
  • 原文地址:https://www.cnblogs.com/zhou2011/p/2877694.html
Copyright © 2011-2022 走看看