课后题答案

第二章

2.算法设计

1将两个递增的有序链表合并为一个递增的有序链表。要求结果链表仍使用原来两个链表的存储空间,不另外占用其它的存储空间。表中不允许有重复的数据。

[题目分析]

合并后的新表使用头指针Lc指向,papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点,从第一个结点开始进行比较,当两个链表LaLb均为到达表尾结点时,依次摘取其中较小者重新链接在Lc表的最后。如果两个表中的元素相等,只摘取La表中的元素,删除Lb表中的元素,这样确保合并后表中无重复的元素。当一个表到达表尾结点,为空时,将非空表的剩余元素直接链接在Lc表的最后。

[算法描述]

void MergeList(LinkList &La,LinkList&Lb,LinkList &Lc)

{//合并链表LaLb,合并后的新表使用头指针Lc指向

  pa=La->next; pb=Lb->next;   

   //papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点

  Lc=pc=La; //La的头结点作为Lc的头结点

  while(pa && pb)

{if(pa->data<pb->data){pc->next=pa;pc=pa;pa=pa->next;}

     //取较小者La中的元素,将pa链接在pc的后面,pa指针后移

    else if(pa->data>pb->data) {pc->next=pb; pc=pb;pb=pb->next;}

      //取较小者Lb中的元素,将pb链接在pc的后面,pb指针后移

    else //相等时取La中的元素,删除Lb中的元素

{pc->next=pa;pc=pa;pa=pa->next;

     q=pb->next;delete pb ;pb =q;

}

     }

 pc->next=pa?pa:pb;   //插入剩余段

     delete Lb;           //释放Lb的头结点

} 

2将两个非递减的有序链表合并为一个非递增的有序链表。要求结果链表仍使用原来两个链表的存储空间,不另外占用其它的存储空间。表中允许有重复的数据。

[题目分析]

合并后的新表使用头指针Lc指向,papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点,从第一个结点开始进行比较,当两个链表LaLb均为到达表尾结点时,依次摘取其中较小者重新链接在Lc表的表头结点之后,如果两个表中的元素相等,只摘取La表中的元素,保留Lb表中的元素。当一个表到达表尾结点,为空时,将非空表的剩余元素依次摘取,链接在Lc表的表头结点之后。

[算法描述]

voidMergeList(LinkList& La, LinkList& Lb, LinkList& Lc, )

{//合并链表LaLb,合并后的新表使用头指针Lc指向

  pa=La->next; pb=Lb->next;

//papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点

  Lc=pc=La; //La的头结点作为Lc的头结点

  Lc->next=NULL;

  while(pa||pb )

{//只要存在一个非空表,用q指向待摘取的元素

    if(!pa) {q=pb; pb=pb->next;}

//La表为空,用q指向pbpb指针后移

    else if(!pb) {q=pa; pa=pa->next;}

//Lb表为空,用q指向papa指针后移

    else if(pa->data<=pb->data) {q=pa; pa=pa->next;}

//取较小者(包括相等)La中的元素,用q指向papa指针后移

    else {q=pb; pb=pb->next;}

//取较小者Lb中的元素,用q指向pbpb指针后移

     q->next = Lc->next; Lc->next = q;  

//q指向的结点插在Lc表的表头结点之后

    }

    deleteLb;            //释放Lb的头结点

}  

 

3已知两个链表AB分别表示两个集合,其元素递增排列。请设计算法求出AB的交集,并存放于A链表中。

[题目分析]

只有同时出现在两集合中的元素才出现在结果表中,合并后的新表使用头指针Lc指向。papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点,从第一个结点开始进行比较,当两个链表LaLb均为到达表尾结点时,如果两个表中相等的元素时,摘取La表中的元素,删除Lb表中的元素;如果其中一个表中的元素较小时,删除此表中较小的元素,此表的工作指针后移。当链表LaLb有一个到达表尾结点,为空时,依次删除另一个非空表中的所有元素。

[算法描述]

void Mix(LinkList& La, LinkList& Lb,LinkList& Lc)

{ pa=La->next;pb=Lb->next;

papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点

Lc=pc=La; //La的头结点作为Lc的头结点

while(pa&&pb)

{if(pa->data==pb->data)∥交集并入结果表中。

   { pc->next=pa;pc=pa;pa=pa->next;

    u=pb;pb=pb->next;delete u;}

  else if(pa->data<pb->data) {u=pa;pa=pa->next; deleteu;}

else {u=pb; pb=pb->next; delete u;}

}

while(pa) {u=pa; pa=pa->next;delete u;}∥释放结点空间

while(pb) {u=pb; pb=pb->next; deleteu;}∥释放结点空间

pc->next=null;∥置链表尾标记。

delete Lb; //释放Lb的头结点

  }

4已知两个链表AB分别表示两个集合,其元素递增排列。请设计算法求出两个集合AB的差集(即仅由在A中出现而不在B中出现的元素所构成的集合),并以同样的形式存储,同时返回该集合的元素个数。

[题目分析]

求两个集合AB的差集是指在A中删除AB中共有的元素,即删除链表中的相应结点,所以要保存待删除结点的前驱,使用指针pre指向前驱结点。papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点,从第一个结点开始进行比较,当两个链表LaLb均为到达表尾结点时,如果La表中的元素小于Lb表中的元素,pre置为La表的工作指针pa删除Lb表中的元素;如果其中一个表中的元素较小时,删除此表中较小的元素,此表的工作指针后移。当链表LaLb有一个为空时,依次删除另一个非空表中的所有元素。

[算法描述]

void DifferenceLinkList& La, LinkList& Lb,int *n

{∥差集的结果存储于单链表La中,*n是结果集合中元素个数,调用时为0

pa=La->next; pb=Lb->next;     

papb分别是链表LaLb的工作指针,初始化为相应链表的第一个结点

  pre=La;         preLapa所指结点的前驱结点的指针

  whilepa&&pb

{ifpa->data<q->data{pre=pa;pa=pa->next;*n++;}

A链表中当前结点指针后移

else ifpa->data>q->dataq=q->next;    B链表中当前结点指针后移

   else{pre->next=pa->next;     ∥处理AB中元素值相同的结点,应删除

         u=pa; pa=pa->next; delete u;}  ∥删除结点

}

}

5设计算法将一个带头结点的单链表A分解为两个具有相同结构的链表BC,其中B表的结点为A表中值小于零的结点,而C表的结点为A表中值大于零的结点(链表A中的元素为非零整数,要求BC表利用A表的结点)。

[题目分析]

B表的头结点使用原来A表的头结点,为C表新申请一个头结点。从A表的第一个结点开始,依次取其每个结点p,判断结点p的值是否小于0,利用前插法,将小于0的结点插入B,大于等于0的结点插入C表。

[算法描述]

void DisCompose(LinkedList A)

{  B=A;

B->next= NULL;   B表初始化

    C=new LNode;∥C申请结点空间

    C->next=NULL;    C初始化为空表

    p=A->next;       p为工作指针

    while(p!= NULL)

    { r=p->next;     ∥暂存p的后继

       if(p->data<0)

        {p->next=B->next;B->next=p; }∥将小于0的结点链入B,前插法

       else {p->next=C->next;C->next=p; }∥大于等于0的结点链入C,前插法

       p=r;∥p指向新的待处理结点。

    }

}

 

6设计一个算法,通过一趟遍历在单链表中确定值最大的结点。

[题目分析]

假定第一个结点中数据具有最大值,依次与下一个元素比较,若其小于下一个元素,则设其下一个元素为最大值,反复进行比较,直到遍历完该链表。

[算法描述]

ElemType Max (LinkList L ){

    if(L->next==NULL)return NULL;

    pmax=L->next;//假定第一个结点中数据具有最大值

    p=L->next->next;

    while(p!= NULL ){//如果下一个结点存在

        if(p->data> pmax->data) pmax=p;//如果p的值大于pmax的值,则重新赋值

        p=p->next;//遍历链表

    }

    returnpmax->data;

7设计一个算法,通过遍历一趟,将链表中所有结点的链接方向逆转,仍利用原表的存储空间。

[题目分析]

从首元结点开始,逐个地把链表L的当前结点p插入新的链表头部。

[算法描述]

void inverse(LinkList &L)

{// 逆置带头结点的单链表 L

   p=L->next; L->next=NULL;

   while ( p) {

       q=p->next;   // q指向*p的后继

       p->next=L->next;

       L->next=p;      // *p插入在头结点之后

       p = q;

    }

}

8设计一个算法,删除递增有序链表中值大于mink且小于maxk的所有元素(minkmaxk是给定的两个参数,其值可以和表中的元素相同,也可以不同 )。

[题目分析]

分别查找第一个值>mink的结点和第一个值 ≥maxk的结点,再修改指针,删除值大于mink且小于maxk的所有元素。

[算法描述]

void delete(LinkList &L, int mink, intmaxk) {

  p=L->next; //首元结点

   while(p && p->data<=mink)

      {pre=p; p=p->next; } //查找第一个值>mink的结点

   if(p)

{while (p &&p->data<maxk) p=p->next;

                     // 查找第一个值 ≥maxk的结点

     q=pre->next;  pre->next=p; // 修改指针

     while (q!=p)

        { s=q->next; delete q; q=s; } // 释放结点空间

   }//if

}

9已知p指向双向循环链表中的一个结点,其结点结构为datapriornext三个域,写出算法change(p),交换p所指向的结点和它的前缀结点的顺序。

[题目分析]

知道双向循环链表中的一个结点,与前驱交换涉及到四个结点(p结点,前驱结点,前驱的前驱结点,后继结点)六条链。

[算法描述]

void  ExchangeLinkedList p

p是双向循环链表中的一个结点,本算法将p所指结点与其前驱结点交换。

{q=p->llink

 q->llink->rlink=p  p的前驱的前驱之后继为p

 p->llink=q->llink  p的前驱指向其前驱的前驱。

 q->rlink=p->rlink  p的前驱的后继为p的后继。

 q->llink=p         p与其前驱交换

 p->rlink->llink=q  p的后继的前驱指向原p的前驱

 p->rlink=q         p的后继指向其原来的前驱

}∥算法exchange结束。

10已知长度为n的线性表A采用顺序存储结构,请写一时间复杂度为O(n)、空间复杂度为O(1)的算法,该算法删除线性表中所有值为item的数据元素。

[题目分析]

在顺序存储的线性表上删除元素,通常要涉及到一系列元素的移动(删第i个元素,第i+1至第n个元素要依次前移)。本题要求删除线性表中所有值为item的数据元素,并未要求元素间的相对位置不变。因此可以考虑设头尾两个指针(i=1j=n),从两端向中间移动,凡遇到值item的数据元素时,直接将右端元素左移至值为item的数据元素位置。

[算法描述]

void DeleteElemType A[ ]int n

A是有n个元素的一维数组,本算法删除A中所有值为item的元素。

{i=1j=n;∥设置数组低、高端指针(下标)。

 whilei<j

  {whilei<j&& A[i]!=itemi++        ∥若值不为item,左移指针。

   ifi<jwhilei<j && A[j]==itemj--;∥若右端元素为item,指针左移

   ifi<jA[i++]=A[j--]}


3  栈和队列

1选择题

1)若让元素12345依次进栈,则出栈次序不可能出现在(  )种情况。

A54321  B21543    C43125    D23541

答案:C

解释:栈是后进先出的线性表,不难发现C选项中元素1比元素2先出栈,违背了栈的后进先出原则,所以不可能出现C选项所示的情况。

2)若已知一个栈的入栈序列是123,…,n,其输出序列为p1p2p3,…,pn,若p1=n,则pi为(  )。

Ai               Bn-i               Cn-i+1            D.不确定

答案:C

解释:栈是后进先出的线性表,一个栈的入栈序列是123,…,n,而输出序列的第一个元素为n,说明123,…,n一次性全部进栈,再进行输出,所以p1=np2=n-1,…,pi=n-i+1

3)数组Q[n]用来表示一个循环队列,f为当前队列头元素的前一位置,r为队尾元素的位置,假定队列中元素的个数小于n,计算队列中元素个数的公式为(  )。

Ar-f             B(n+f-r)%n       Cn+r-f           D.(n+r-f)%n

答案:D

解释:对于非循环队列,尾指针和头指针的差值便是队列的长度,而对于循环队列,差值可能为负数,所以需要将差值加上MAXSIZE(本题为n),然后与MAXSIZE(本题为n)求余,即n+r-f)%n

4)链式栈结点为:(data,link)top指向栈顶.若想摘除栈顶结点,并将删除结点的值保存到x,则应执行操作 

Ax=top->data;top=top->link           Btop=top->link;x=top->link   

Cx=top;top=top->link                    Dx=top->link

答案:A

解释:x=top->data将结点的值保存到x中,top=top->link栈顶指针指向栈顶下一结点,即摘除栈顶结点。

5设有一个递归算法如下

        int fact(intn) {  //n大于等于0

            if(n<=0) return 1;

            else returnn*fact(n-1);        }

则计算fact(n)需要调用该函数的次数为  

A n+1              B n-1              C n                  D n+2

答案:A

解释:特殊值法。设n=0,易知仅调用一次fact(n)函数,故选A

6)栈在   中有所应用。

A递归调用       B函数调用     C表达式求值        D前三个选项都有

答案:D

解释:递归调用、函数调用、表达式求值均用到了栈的后进先出性质。

7为解决计算机主机与打印机间速度不匹配问题,通常设一个打印数据缓冲区。主机将要输出的数据依次写入该缓冲区,而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是 )。

A队列          B            C线性表           D有序表

答案:A

解释:解决缓冲区问题应利用一种先进先出的线性表,而队列正是一种先进先出的线性表。

8设栈S和队列Q的初始状态为空,元素e1e2e3e4e5e6依次进入栈S,一个元素出栈后即进入Q,若6个元素出队的序列是e2e4e3e6e5e1,则栈S的容量至少应该是( )。

A2              B3              C4                D 6

答案:B

解释:元素出队的序列是e2e4e3e6e5e1,可知元素入队的序列是e2e4e3e6e5e1,即元素出栈的序列也是e2e4e3e6e5e1,而元素e1e2e3e4e5e6依次进入栈,易知栈S中最多同时存在3个元素,故栈S的容量至少为3

9)若一个栈以向量V[1..n]存储,初始栈顶指针top设为n+1,则元素x进栈的正确操作是(    )

Atop++; V[top]=x;                   BV[top]=x;top++;

Ctop--; V[top]=x;                   DV[top]=x;top--;

答案:C

解释:初始栈顶指针topn+1,说明元素从数组向量的高端地址进栈,又因为元素存储在向量空间V[1..n]中,所以进栈时top指针先下移变为n,之后将元素x存储在V[n]

10设计一个判别表达式中左,右括号是否配对出现的算法,采用( )数据结构最佳。

A.线性表的顺序存储结构              B队列    

C. 线性表的链式存储结构              D.

答案:D

解释:利用栈的后进先出原则。

11用链接方式存储的队列,在进行删除运算时( )

A. 仅修改头指针                      B. 仅修改尾指针

C. 头、尾指针都要修改                D. 头、尾指针可能都要修改

答案:D

解释:一般情况下只修改头指针,但是,当删除的是队列中最后一个元素时,队尾指针也丢失了,因此需对队尾指针重新赋值。

12循环队列存储在数组A[0..m]中,则入队时的操作为( )

A. rear=rear+1                       B. rear=(rear+1)%(m-1)

  C. rear=(rear+1)%m                   D.rear=(rear+1)%(m+1)

答案:D

解释:数组A[0..m]中共含有m+1个元素,故在求模运算时应除以m+1

13最大容量为n的循环队列,队尾指针是rear,队头是front,则队空的条件是( )

  A. (rear+1)%n==front                  B. rear==front                                                          

Crear+1==front                      D. (rear-l)%n==front

答案:B

解释:最大容量为n的循环队列,队满条件是(rear+1)%n==front,队空条件是rear==front

14栈和队列的共同点是( )

A. 都是先进先出                       B. 都是先进后出  

C. 只允许在端点处插入和删除元素       D. 没有共同点

答案:C

解释:栈只允许在栈顶处进行插入和删除元素,队列只允许在队尾插入元素和在队头删除元素。

15一个递归算法必须包括( )

A. 递归部分                           B.终止条件和递归部分

C. 迭代部分                           D. 终止条件和迭代部分

答案:B

 

2.算法设计

1将编号为01的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。当第0号栈的栈顶指针top[0]等于-1时该栈为空,当第1号栈的栈顶指针top[1]等于m时该栈为空。两个栈均从两端向中间增长。试编写双栈初始化,判断栈空、栈满、进栈和出栈等算法的函数。双栈数据结构的定义如下:

Typedef struct

{int top[2],bot[2];                        //栈顶和栈底指针

 SElemType *V;                          //栈数组

 int m;                                            //栈最大可容纳元素个数

}DblStack

[题目分析]

两栈共享向量空间,将两栈栈底设在向量两端,初始时,左栈顶指针为-1,右栈顶为m。两栈顶指针相邻时为栈满。两栈顶相向、迎面增长,栈顶指针指向栈顶元素。

[算法描述]

(1) 栈初始化

int Init()

 {S.top[0]=-1;

  S.top[1]=m;

  return 1; //初始化成功

}

(2) 入栈操作:

int push(stk S ,int i,int x)

i为栈号,i=0表示左栈,i=1为右栈,x是入栈元素。入栈成功返回1,失败返回0

{if(i<0||i>1){ cout<<“栈号输入不对”<<endl;exit(0);}

if(S.top[1]-S.top[0]==1) {cout<<“栈已满”<<endl;return(0);}

switch(i)

 {case 0: S.V[++S.top[0]]=x; return(1); break;

case 1:S.V[--S.top[1]]=x; return(1);

}

}push

(3) 退栈操作

ElemType pop(stk S,int i)

∥退栈。i代表栈号,i=0时为左栈,i=1时为右栈。退栈成功时返回退栈元素

∥否则返回-1

{if(i<0 || i>1){cout<<“栈号输入错误”<<endlexit(0);}

 switch(i)

{case 0: if(S.top[0]==-1){cout<<“栈空”<<endlreturn-1);}

else return(S.V[S.top[0]--]);

case 1: if(S.top[1]==m{ cout<<“栈空”<<endl; return(-1);}

else return(S.V[S.top[1]++]);

   }switch     

}∥算法结束

(4) 判断栈空

int Empty();

{return (S.top[0]==-1 &&S.top[1]==m);

}

[算法讨论

请注意算法中两栈入栈和退栈时的栈顶指针的计算。左栈是通常意义下的栈,而右栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。

 

(2)回文是指正读反读均相同的字符序列,如“abba”和“abdba”均是回文,但“good”不是回文。试写一个算法判定给定的字符向量是否为回文。(提示:将一半字符入栈) 

[题目分析]

将字符串前一半入栈,然后,栈中元素和字符串后一半进行比较。即将第一个出栈元素和后一半串中第一个字符比较,若相等,则再出栈一个元素与后一个字符比较,……,直至栈空,结论为字符序列是回文。在出栈元素与串中字符比较不等时,结论字符序列不是回文。

[算法描述]

#define StackSize 100 //假定预分配的栈空间最多为100个元素

typedef char DataType;//假定栈元素的数据类型为字符

typedef struct

{DataType data[StackSize];

int top;

}SeqStack;

 

int IsHuiwen( char *t)

{//判断t字符向量是否为回文,若是,返回1,否则返回0

SeqStack s;

int i , len;

char temp;

InitStack( &s);

len=strlen(t); //求向量长度

for ( i=0; i<len/2; i++)//将一半字符入栈

Push( &s, t[i]);

while( !EmptyStack( &s))

{// 每弹出一个字符与相应字符比较

temp=Pop (&s);

if( temp!=S[i])  return 0 ;// 不等则返回0

else i++;

return 1 ; // 比较完毕均相等则返回 1

}

(3)设从键盘输入一整数的序列:a1, a2, a3,…,an,试编写算法实现:用栈结构存储输入的整数,当ai≠-1时,将ai进栈;当ai=-1时,输出栈顶整数并出栈。算法应对异常情况(入栈满等)给出相应的信息。

[算法描述]

#define maxsize 栈空间容量

void InOutS(int s[maxsize])

//s是元素为整数的栈,本算法进行入栈和退栈操作。

{int top=0;             //top为栈顶指针,定义top=0时为栈空。

 for(i=1; i<=n; i++)    //n个整数序列作处理。

    {cin>>x);    //从键盘读入整数序列。

     if(x!=-1)           // 读入的整数不等于-1时入栈。

    {if(top==maxsize-1){cout<<“栈满”<<endl;exit(0);}

else s[++top]=x; //x入栈。

     else   //读入的整数等于-1时退栈。

     {if(top==0){ cout<<“栈空”<<endl;exit(0);}

else cout<<“出栈元素是”<< s[top--]<<endl;}

}

}//算法结束。

 

4从键盘上输入一个后缀表达式,试编写算法计算表达式的值。规定:逆波兰表达式的长度不超过一行,以$符作为输入结束,操作数之间用空格分隔,操作符只可能有+-*/四种运算。例如:234 34+2*$

[题目分析]

逆波兰表达式(即后缀表达式)求值规则如下:设立运算数栈OPND,对表达式从左到右扫描(读入),当表达式中扫描到数时,压入OPND栈。当扫描到运算符时,从OPND退出两个数,进行相应运算,结果再压入OPND栈。这个过程一直进行到读出表达式结束符$,这时OPND栈中只有一个数,就是结果。

[算法描述]

float expr( )

//从键盘输入逆波兰表达式,以‘$’表示输入结束,本算法求逆波兰式表达式的值。

float OPND[30];   // OPND是操作数栈。

init(OPND);       //两栈初始化。

float num=0.0;    //数字初始化。

cin>>x;//x是字符型变量。

while(x!=’$’)

    {switch

     {case0’<=x<=’9’:

while((x>=’0’&&x<=’9’)||x==’.’)  //拼数

if(x!=’.’)   //处理整数

{num=num*10+ord(x)-ord(‘0’); cin>>x;}

else           //处理小数部分。

{scale=10.0; cin>>x;

while(x>=’0’&&x<=’9’)

{num=num+(ord(x)-ord(‘0’)/scale;

scale=scale*10;  cin>>x; }

}//else

push(OPND,num); num=0.0;//数压入栈,下个数初始化

      case x=’:break;  //遇空格,继续读下一个字符。

      case x=+’:push(OPND,pop(OPND)+pop(OPND));break;

      case x=-:x1=pop(OPND);x2=pop(OPND);push(OPND,x2-x1);break;

      case x=*’:push(OPND,pop(OPND)*pop(OPND));break;

      case x=/:x1=pop(OPND);x2=pop(OPND);push(OPND,x2/x1);break;

       default:       //其它符号不作处理。

     }//结束switch

     cin>>x;//读入表达式中下一个字符。

   }//结束whilex=$’

cout<<“后缀表达式的值为”<<pop(OPND);

}//算法结束。

[算法讨论]假设输入的后缀表达式是正确的,未作错误检查。算法中拼数部分是核心。若遇到大于等于‘0且小于等于‘9的字符,认为是数。这种字符的序号减去字符‘0的序号得出数。对于整数,每读入一个数字字符,前面得到的部分数要乘上10再加新读入的数得到新的部分数。当读到小数点,认为数的整数部分已完,要接着处理小数部分。小数部分的数要除以10(或10的幂数)变成十分位,百分位,千分位数等等,与前面部分数相加。在拼数过程中,若遇非数字字符,表示数已拼完,将数压入栈中,并且将变量num恢复为0,准备下一个数。这时对新读入的字符进入‘+’、‘-’、‘*’、‘/’及空格的判断,因此在结束处理数字字符的case后,不能加入break语句。

 

5假设以IO分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由IO组成的序列,称可以操作的序列为合法序列,否则称为非法序列。

下面所示的序列中哪些是合法的?

   A.IOIIOIOO     B. IOOIOIIO      C. IIIOIOIO     D. IIIOOIOO

通过对的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回true,否则返回false(假定被判定的操作序列已存入一维数组中)。

答案:

AD是合法序列,BC 是非法序列。

设被判定的操作序列已存入一维数组A中。

int Judge(char A[])

        //判断字符数组A中的输入输出序列是否是合法序列。如是,返回true,否则返回false

        {i=0;                //i为下标。

         j=k=0;              //jk分别为I和字母O的的个数。

         while(A[i]!=\0’) //当未到字符数组尾就作。

           {switch(A[i])

             {caseI’: j++; break; //入栈次数增1

              caseO’: k++; if(k>j){cout<<“序列非法”<<ednlexit(0);}

              }

i++; //不论A[i]是‘I’或‘O’,指针i均后移。}

         if(j!=k) {cout<<“序列非法”<<endlreturn(false);}

         else { cout<<“序列合法”<<endlreturn(true);}

        }//算法结束。

     [算法讨论]在入栈出栈序列(即由‘I’和‘O’组成的字符串)的任一位置,入栈次数(‘I’的个数)都必须大于等于出栈次数(即‘O’的个数),否则视作非法序列,立即给出信息,退出算法。整个序列(即读到字符数组中字符串的结束标记‘\0),入栈次数必须等于出栈次数(题目中要求栈的初态和终态都为空),否则视为非法序列。

 

(6)假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素站点(注意不设头指针) ,试编写相应的置空队、判队空、入队和出队等算法。

[题目分析]

置空队就是建立一个头节点,并把头尾指针都指向头节点,头节点是不存放数据的;判队空就是当头指针等于尾指针时,队空;入队时,将新的节点插入到链队列的尾部,同时将尾指针指向这个节点;出队时,删除的是队头节点,要注意队列的长度大于1还是等于1的情况,这个时候要注意尾指针的修改,如果等于1,则要删除尾指针指向的节点。

[算法描述]

//先定义链队结构:

typedef struct queuenode

{Datatype data;

struct queuenode *next;

}QueueNode; //以上是结点类型的定义

typedef struct

{queuenode *rear;

}LinkQueue; //只设一个指向队尾元素的指针

 

(1)    置空队

void InitQueue(LinkQueue *Q)
{ //
置空队:就是使头结点成为队尾元素
QueueNode *s;

Q->rear =Q->rear->next;//将队尾指针指向头结点

while(Q->rear!=Q->rear->next)//当队列非空,将队中元素逐个出队

{s=Q->rear->next;

Q->rear->next=s->next;

delete s;

}//回收结点空间

}

 

(2)    判队空 

int EmptyQueue(LinkQueue *Q)

{ //判队空。当头结点的next指针指向自己时为空队

return Q->rear->next->next==Q->rear->next;

}

 

(3)    入队

void EnQueue(LinkQueue *Q, Datatype x)

{ //入队。也就是在尾结点处插入元素

QueueNode *p=new QueueNode;//申请新结点

p->data=x;p->next=Q->rear->next;//初始化新结点并链入

Q-rear->next=p; 

Q->rear=p;//将尾指针移至新结点

}

 

(4)    出队

Datatype DeQueue(LinkQueue *Q)

{//出队,把头结点之后的元素摘下

Datatype t;

QueueNode *p;

if(EmptyQueue( Q ))

Error("Queueunderflow");

p=Q->rear->next->next;//p指向将要摘下的结点

x=p->data; //保存结点中数据

if (p==Q->rear)

{//当队列中只有一个结点时,p结点出队后,要将队尾指针指向头结点

Q->rear = Q->rear->next;

Q->rear->next=p->next;

}

else 

Q->rear->next->next=p->next;//摘下结点p

delete p;//释放被删结点

return x;

}

 

7假设以数组Q[m]存放循环队列中的元素, 同时设置一个标志tag,以tag== 0tag == 1来区别在队头指针(front)和队尾指针(rear)相等时,队列状态为还是。试编写与此结构相应的插入(enqueue)和删除(dlqueue)算法。

[算法描述]

(1)初始化

SeQueue QueueInit(SeQueue Q)

{//初始化队列

Q.front=Q.rear=0; Q.tag=0;

return Q;

}

(2)入队

SeQueue QueueIn(SeQueue Q,int e)

{//入队列

if((Q.tag==1) &&(Q.rear==Q.front)) cout<<"队列已满"<<endl;

else 

{Q.rear=(Q.rear+1) % m;

Q.data[Q.rear]=e;

if(Q.tag==0) Q.tag=1; //队列已不空

}

return Q;

}

(3)出队

ElemType QueueOut(SeQueue Q)

{//出队列

if(Q.tag==0) { cout<<"队列为空"<<endl;exit(0);}

else

{Q.front=(Q.front+1) % m;

e=Q.data[Q.front];

if(Q.front==Q.rear)Q.tag=0; //空队列

}

return(e);

}

 

(8)如果允许在循环队列的两端都可以进行插入和删除操作。要求:

写出循环队列的类型定义;

写出“从队尾删除”和“从队头插入”的算法。

[题目分析] 用一维数组 v[0..M-1]实现循环队列,其中M是队列长度。设队头指针 front和队尾指针rear,约定front指向队头元素的前一位置,rear指向队尾元素。定义front=rear时为队空,(rear+1)%m=front 为队满。约定队头端入队向下标小的方向发展,队尾端入队向下标大的方向发展。

[算法描述]

#define M  队列可能达到的最大长度

typedef struct

{elemtp data[M];

int front,rear;

}cycqueue;

elemtp delqueue ( cycqueue Q)

    //Q是如上定义的循环队列,本算法实现从队尾删除,若删除成功,返回被删除元素,否则给出出错信息。

{if (Q.front==Q.rear)  { cout<<"队列空"<<endl;exit(0);}

Q.rear=(Q.rear-1+M)%M;          //修改队尾指针。

return(Q.data[(Q.rear+1+M)%M]);//返回出队元素。

}//从队尾删除算法结束

 

void enqueue (cycqueue Q, elemtp x)

// Q是顺序存储的循环队列,本算法实现“从队头插入”元素x

{if (Q.rear==(Q.front-1+M)%M)  { cout<<"队满"<<endl;exit(0);)

 Q.data[Q.front]=x;        //x 入队列

Q.front=(Q.front-1+M)%M;  //修改队头指针。

}// 结束从队头插入算法。

 

9已知Ackermann函数定义如下:

  

写出计算Ack(m,n)的递归算法,并根据此算法给出出Ack(2,1)的计算过程。

写出计算Ack(m,n)的非递归算法。

[算法描述]

int Ack(int m,n)

{if (m==0) return(n+1);

      elseif(m!=0&&n==0) return(Ack(m-1,1));

      else return(Ack(m-1,Ack(m,m-1));

     }//算法结束

Ack(2,1)的计算过程

Ack(2,1)= Ack(1,Ack(2,0))           //m<>0,n<>0而得

             = Ack(1,Ack(1,1))            //m<>0,n=0而得

             = Ack(1,Ack(0,Ack(1,0)))     // m<>0,n<>0而得

             = Ack(1,Ack(0,Ack(0,1)))     // m<>0,n=0而得

             = Ack(1,Ack(0,2))            // m=0而得

             = Ack(1,3)                   // m=0而得

             = Ack(0,Ack(1,2))            //m<>0,n<>0而得

             = Ack(0,Ack(0,Ack(1,1)))     //m<>0,n<>0而得

             = Ack(0,Ack(0,Ack(0,Ack(1,0)))) //m<>0,n<>0而得

             = Ack(0,Ack(0,Ack(0,Ack(0,1)))) //m<>0,n=0而得

             = Ack(0,Ack(0,Ack(0,2)))     //m=0而得

             = Ack(0,Ack(0,3))            //m=0而得

             = Ack(0,4)                   //n=0而得

             =5                           //n=0而得

int Ackerman(intm, int n)

{intakm[M][N];int i,j;

for(j=0;j<N;j++) akm[0][j]=j+1;

for(i=1;i<m;i++)

{akm[i][0]=akm[i-1][1];

for(j=1;j<N;j++)

akm[i][j]=akm[i-1][akm[i][j-1]];

}

return(akm[m][n]);

}//算法结束

 

10已知f为单链表的表头指针, 链表中存储的都是整型数据,试写出实现下列运算的递归算法:      

求链表中的最大整数;

求链表的结点个数;

求所有整数的平均值。

[算法描述]

int GetMax(LinkList p)

{

         if(!p->next)      

                   returnp->data;

         else

         {

                   intmax=GetMax(p->next);

                   returnp->data>=max ? p->data:max;

         }

}

int GetLength(LinkList p)

{

         if(!p->next)

                   return 1;

         else

         {

                   returnGetLength(p->next)+1;

         }

}

double GetAverage(LinkList p , int n)

{

         if(!p->next)

                   returnp->data;

         else

         {

                   doubleave=GetAverage(p->next,n-1);

                   return(ave*(n-1)+p->data)/n;

         }

}



  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值