狐狸梦见乌鸦

当坚持成为一种习惯,目标将不在遥远```
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

栈-学习笔记

Posted on 2013-01-26 12:01  灬啊U  阅读(271)  评论(0编辑  收藏  举报

栈:

  栈是限定仅在表尾进行插入和删除操作的线性表。遵循先进后出的原则。允许插入、删除的一端为栈顶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