关于栈的学习

原创博文,转载请注明出处

栈(Stack)是一种线性表,但是只允许在一端进行插入或删除操作。

栈的顺序存储成为顺序栈,它是利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶的位置。

栈的顺序存储类型可描述为

1 #define MaxSize 50
2 typedef struct{
3         Elemtype data[MaxSize];
4         int top    
5 }SqStack

初始时设置S.top=-1,栈顶元素:S.data[S.top]

栈空条件:S.top==-1;栈满条件:S.top==MaxSize-1;栈长:S.top+1

关于进栈和出栈的操作比较简单(只需要操作栈顶指针)在这里就不再描述。

利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数据空间,将两个栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。在进行出栈和进栈操作时要注意栈顶指针的操作。

栈的链式存储结构:采用链式存储,便于多个栈共享存储空间和提高效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行。链栈的操作与链表类似。需注意对于带头结点和不带头结点的链栈之间的区别。

试题精选:

            1、3个不同元素依次进栈,能得到( )种不同的出栈序列。

                 对于n个不同元素进栈,出栈序列的个数为 (1/n+1)Cn2n=(1/n+1)((2n)! / (n!)*(n!)) 本题答案为5。

           2、若一个栈的输入序列是P1, P2, P3,...,Pn,其输出序列是1,2,3,...,n。若P3=1,则P1的值( )

               A、可能是2           B、一定是2         C、不可能是2         D、不可能是3

               由于 P3=1,即P1、P2、P3 连续入栈后,第一个出栈元素是P3,第二个出栈元素是2,而此时P1不是栈顶元素,因此P1的元素不可能是2。

          3、设栈的初始状态为空,当字符序列“n 1 _”作为栈的输入时,输出长度为3,且可用做C语言标识符的序列有(  )个。

              标识符的第一个字符必须是大小写字母或下划线,而不能是数字。则符合规定的标识符有 n1_、n_1、_1n、_n1四种形式。根据 栈的操作特性,_n1这种情况是不可能出现的。

          4、元素 a、b、c、d、e依次进入初始为空的栈中,若元素进栈后可停留、可出栈,知道所有元素都出栈,则在所有可能的出栈序列中,以元素d开头的序列个数是( )。

               出栈序列必是 d_c_b_a,e的顺序不定,在任意一个“_”上都有可能。

    5、设单链表的表头指针为h,结点结构由data和next两个域构成,其中 data 域为字符型。试设计算法判断该链表的前n个字符是否中心对称。例如 xyx、xyyx 都是中心对称。

              思路:使用栈来判断表中的数据是否中心对称。将链表的前一半元素依次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,若相等,接着进行下一个元素的比较,一直到链表尾部。若这时栈是空栈,则证明是中心对称。

 1 int dc( LinkList ,int n ){
 2       char s[n/2];
 3       p=L->next;
 4       for( i=0;i<=n/2;i++ ){
 5             s[i]=p->next;
 6             p=p->next;
 7     }
 8      i--;
 9      if( n%2==1 )
10            p=p->next;
11      while(p!=NULL&&s[i]==p->data){
12      i--;
13      p=p->next;
14     }
15     if(i==-1)
16        return 1;
17     else
18        return 0;
19 }

      6、设有两个栈s1、s2都采用顺序栈方式,并且共享一个存储区[0,...,maxsize-1],为了尽量利用空间,减少溢出的可能,可采用栈顶相向、迎面增长的存储方式。试设计s1、s2有关入栈和出栈的操作算法。

     思路:

            算法的关键在于两个栈入栈和退栈时栈顶指针的计算。s1栈与通常意义下的栈操作一样,而s2入栈时,其栈顶指针要减1(左移),退栈操作时 要加1 (右移)。

1 #define maxsize
2 typedef struct{
3           ElemType stack[maxsize];
4           int top[2];                       //2个栈顶指针
5 }stk;
6 stk s;

     (1) 入栈操作

 1 int push(int i, int x){
 2  //i 是栈号,i=1表示s1栈,i=2表示s2栈。成功返回1,失败返回0
 3      if(i<1||i>2){
 4         printf("栈号输入不对,请输入1或2\n");
 5         exit(0);
 6      }
 7      if(s.top[1]-s.top[0]==1){
 8         printf(" 栈已满\n ")
 9         return 0;
10      }
11      switch(i){
12         case 1: s.stack[++s.top[0]]=x;return 1;break;
13         case 2: s.stack[--s.top[1]]=x;return 1;break;
14     }
15 }

       (2)退栈操作

 1 int pop(int i){
 2      if(i<1||i>2){
 3          printf("栈号输入不对\n");
 4          exit(0);
 5        }
 6       switch(i){
 7           case 1:
 8                  if(s.top[0]==-1){
 9                      printf("栈空\n");
10                      return -1;
11                  }
12                 else
13                     return s.stack[s.top[0]--];
14           calse 2;
15                  if(s.top[1]==maxsize){
16                      printf("栈空\n");
17                      return -1;
18                  }
19                 else
20                     return s.stack[s.top[1]++];                 
21     }
22 }

      7、假设一个算数表达式中包含圆括号、方括号、花括号三种类型的括号,编写一个算法来判别表达式中的括号是否配对,以字符“\0”作为算数表达式的结束符。

         括号匹配是栈的一个经典应用,基本思想是扫描每个字符,遇到三种括号的左括号进栈,遇到三种括号的右括号时检查栈顶元素是否为相应的左括号,若是,退栈,否则配对错误。最后栈不为空也为错误。

 1 bool check(char *str){
 2           InitStack(S);i=1;
 3           Push(S,str[0]);
 4           while(str[i]!='\0'){
 5               switch(str[i]){
 6                    case '(' : Push(S,'(');break;
 7                    case '[' : Push(S,'[');break;
 8                    case '{' : Push(S,'{');break;

9 case ')' : Pop(S,e): 10 if (e!='(') return false; 11  break; 12 case ']' : Pop(S,e): 13 if (e!=']') return false; 14  break; 15 case '}' : Pop(S,e): 16 if (e!='}') return false; 17 break; 18  default; 19  break; 20 } 21 i++ 22 } 23 if (! isEmpty(s)){ 24 printf("括号不匹配\n"); 25 return false; 26 } 27 else{ 28 printf("括号匹配\n"); 29 return true; 30 } 31 }

       8、在递归调用的过程中,系统为每一层的返回点、局部变量、传入实参等开辟了递归工作栈来进行数据存储,递归次数过多容易造成栈溢出等。而其效率不高时因为递归调用过程中包含过多重复的计算。其优点就在于代码简单,容易理解。

        利用一个栈实现以下递归函数的非递归计算:

 

     思路:设置一个栈用于保存n和对应的Pn(x)值,然后边出栈边计算Pn(x),栈空后值就计算出来了。

 1 double p(int n, double x){
 2        struct stack{
 3            int no;   //保存n
 4            double val;  //保存Pn(x)的值
 5     }st[maxsize]
 6      int top=-1,i;
 7      double fv1=1,fv2=2*x; //当n=0 和n=1时的初值
 8      for(i=n;i>=2;i--){
 9          top++;
10          st[top].no=i
11     }
12      while(top>0){
13           st[top].val=2*x*fv2-2*(st[top-1].no -1)*fv1;
14           fv1=fv2;
15           fv2=st[top].val
16           top--; 
17     }
18     return fv2;
19 }

上面的这种方法 需要栈的长度至少为n,还有一种方法只需要栈的长度为3,st[2]的值是Pn(x)的值,而st[0],st[1]分别存储着Pn-2(x)和Pn-1(x)的值,这三个值会跟随递归运算而发生变化。但是这样只能得到三个结果。不能存储各个对应值。

由于栈的内容比较简单,所以就写这些吧。

posted @ 2013-11-27 19:41  枫桦宁  阅读(1921)  评论(0编辑  收藏  举报