专升本数据结构习题答案.docx

上传人:b****5 文档编号:10187342 上传时间:2023-02-09 格式:DOCX 页数:69 大小:79.67KB
下载 相关 举报
专升本数据结构习题答案.docx_第1页
第1页 / 共69页
专升本数据结构习题答案.docx_第2页
第2页 / 共69页
专升本数据结构习题答案.docx_第3页
第3页 / 共69页
专升本数据结构习题答案.docx_第4页
第4页 / 共69页
专升本数据结构习题答案.docx_第5页
第5页 / 共69页
点击查看更多>>
下载资源
资源描述

专升本数据结构习题答案.docx

《专升本数据结构习题答案.docx》由会员分享,可在线阅读,更多相关《专升本数据结构习题答案.docx(69页珍藏版)》请在冰豆网上搜索。

专升本数据结构习题答案.docx

专升本数据结构习题答案

答案

第一章

1.1简述下列概念:

数据、数据元素、数据类型、数据结构、逻辑结构、存储结构、线性结构、非线性结构。

数据:

指能够被计算机识别、存储和加工处理的信息载体。

●数据元素:

就是数据的基本单位,在某些情况下,数据元素也称为元素、结点、顶点、记录。

数据元素有时可以由若干数据项组成。

●数据类型:

是一个值的集合以及在这些值上定义的一组操作的总称。

通常数据类型可以看作是程序设计语言中已实现的数据结构。

●数据结构:

指的是数据之间的相互关系,即数据的组织形式。

一般包括三个方面的内容:

数据的逻辑结构、存储结构和数据的运算。

●逻辑结构:

指数据元素之间的逻辑关系。

●存储结构:

数据元素及其关系在计算机存储器内的表示,称为数据的存储结构.

●线性结构:

数据逻辑结构中的一类。

它的特征是若结构为非空集,则该结构有且只有一个开始结点和一个终端结点,并且所有结点都有且只有一个直接前趋和一个直接后继。

线性表就是一个典型的线性结构。

栈、队列、串等都是线性结构。

●非线性结构:

数据逻辑结构中的另一大类,它的逻辑特征是一个结点可能有多个直接前趋和直接后继。

数组、广义表、树和图等数据结构都是非线性结构。

 

1.2试举一个数据结构的例子、叙述其逻辑结构、存储结构、运算三个方面的内容。

答:

  例如有一张学生体检情况登记表,记录了一个班的学生的身高、体重等各项体检信息。

这张登记表中,每个学生的各项体检信息排在一行上。

这个表就是一个数据结构。

每个记录(有姓名,学号,身高和体重等字段)就是一个结点,对于整个表来说,只有一个开始结点(它的前面无记录)和一个终端结点(它的后面无记录),其他的结点则各有一个也只有一个直接前趋和直接后继(它的前面和后面均有且只有一个记录)。

这几个关系就确定了这个表的逻辑结构是线性结构。

  这个表中的数据如何存储到计算机里,并且如何表示数据元素之间的关系呢?

即用一片连续的内存单元来存放这些记录(如用数组表示)还是随机存放各结点数据再用指针进行链接呢?

这就是存储结构的问题。

  在这个表的某种存储结构基础上,可实现对这张表中的记录进行查询,修改,删除等操作。

对这个表可以进行哪些操作以及如何实现这些操作就是数据的运算问题了。

1.3常用的存储表示方法有四种:

 ●顺序存储方法:

它是把逻辑上相邻的结点存储在物理位置相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。

由此得到的存储表示称为顺序存储结构,通常借助程序语言的数组描述。

 ●链接存储方法:

它不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示。

由此得到的存储表示称为链式存储结构,通常借助于程序语言的指针类型描述。

 ●索引存储方法:

除建立存储结点信息外,还建立附加的索引表来标识结点的地址。

组成索引表的索引项由结点的关键字和地址组成。

若每个结点在索引表中都有一个索引项,则该索引表称之为稠密索引(DenseIndex)。

若一组结点在索引表中只对应一个索引项,则该索引表称为稀疏索引。

 ●散列存储方法:

就是根据结点的关键字直接计算出该结点的存储地址。

1.4分析:

  数学符号"O"的严格的数学定义:

若T(n)和f(n)是定义在正整数集合上的两个函数,则T(n)=O(f(n))表示存在正的常数C和n0,使得当n≥n0时都满足0≤T(n)≤C·f(n)。

  通俗地说,就是当n→∞时,f(n)的函数值增长速度与T(n)的增长速度同阶。

一般,一个函数的增长速度与该函数的最高次阶同阶。

 即:

   O(f(n))=n3

   O(g(n))=n3

   O(h(n))=n1.5

  所以答案为:

答:

   ●

(1)成立。

 

   ●

(2)成立。

   ●(3)成立。

   ●(4)不成立。

1.5分析:

  要使前者快于后者,即前者的时间消耗低于后者,即:

    100n2<2n

  求解上式,可得

答:

  n=15

1.6

(1)i=1;k=0; 

  while(i

   {k=k+10*i;i++;

   } 

分析:

  i=1;//1

  k=0;//1

  while(i

   {k=k+10*i;//n-1

    i++;//n-1

   } 

由以上列出的各语句的频度,可得该程序段的时间消耗:

   T(n)=1+1+n+(n-1)+(n-1)=3n

可表示为T(n)=O(n)

(2)i=0;k=0;

  do{

    k=k+10*i;i++; 

   }

  while(i

分析:

  i=0;//1

  k=0;//1

  do{//n

    k=k+10*i;//n

    i++;//n

   }

  while(i

由以上列出的各语句的频度,可得该程序段的时间消耗:

  T(n)=1+1+n+n+n+n=4n+2

可表示为T(n)=O(n)

(3)i=1;j=0; 

  while(i+j<=n) 

   {

    if(i>j)j++;

    elsei++;

   }

分析:

  通过分析以上程序段,可将i+j看成一个控制循环次数的变量,且每执行一次循环,i+j的值加1。

该程序段的主要时间消耗是while循环,而while循环共做了n次,所以该程序段的执行时间为:

    T(n)=O(n)

(4)x=n;//n>1 

 while(x>=(y+1)*(y+1))

  y++;

分析:

  由x=n且x的值在程序中不变,又while的循环条件(x>=(y+1)*(y+1))可知:

当(y+1)*(y+1)刚超过n的值时退出循环。

  由(y+1)*(y+1)

y

  所以,该程序段的执行时间为:

   向下取整(n^0.5-1)

(5)x=91;y=100; 

   while(y>0)

    if(x>100)

     {x=x-10;y--;}

    elsex++;

分析:

  x=91;//1

  y=100;//1

  while(y>0)//1101

    if(x>100)//1100

     { x=x-10;//100

      y--;//100

     }

    else 

     x++;//1000

  以上程序段右侧列出了执行次数。

该程序段的执行时间为:

T(n)=O

(1)

1.7算法的时间复杂度不仅与问题的规模相关,还与输入实例中的初始状态有关。

但在最坏的情况下,其时间复杂度就是只与求解问题的规模相关的。

我们在讨论时间复杂度时,一般就是以最坏情况下的时间复杂度为准的。

1.8常见的时间复杂度按数量级递增排列,依次为:

常数阶0

(1)、对数阶0(log2n)、线性阶0(n)、线性对数阶0(nlog2n)、平方阶0(n2)、立方阶0(n3)、k次方阶0(nk)、指数阶0(2n)。

先将题中的函数分成如下几类:

常数阶:

2100

对数阶:

lgn

K次方阶:

n0.5、n(3/2)

指数阶(按指数由小到大排):

nlgn、(3/2)n、2n、n!

、nn

注意:

(2/3)^n由于底数小于1,所以是一个递减函数,其数量级应小于常数阶。

 

根据以上分析按增长率由小至大的顺序可排列如下:

(2/3)n<2100

第二章

2.1开始结点是指链表中的第一个结点,也就是没有直接前趋的那个结点。

   链表的头指针是一指向链表开始结点的指针(没有头结点时),单链表由头指针唯一确定,因此单链表可以用头指针的名字来命名。

   头结点是在链表的开始结点之前附加的一个结点。

有了头结点之后,头指针指向头结点,不论链表否为空,头指针总是非空。

而且头指针的设置使得对链表的第一个位置上的操作与在表其他位置上的操作一致(都是在某一结点之后)。

2.3在等概率情况下,顺序表中插入一个结点需平均移动n/2个结点。

删除一个结点需平均移动(n-1)/2个结点。

具体的移动次数取决于顺序表的长度n以及需插入或删除的位置i。

i越接近n则所需移动的结点数越少。

2.4尾指针是指向终端结点的指针,用它来表示单循环链表可以使得查找链表的开始结点和终端结点都很方便,设一带头结点的单循环链表,其尾指针为rear,则开始结点和终端结点的位置分别是rear->next->next和rear,查找时间都是O

(1)。

  若用头指针来表示该链表,则查找终端结点的时间为O(n)。

2.5下面分别讨论三种链表的情况。

  1.单链表。

若指针p指向某结点时,能够根据该指针找到其直接后继,能够顺后继指针链找到*p结点后的结点。

但是由于不知道其头指针,所以无法访问到p指针指向的结点的直接前趋。

因此无法删去该结点。

  2.双链表。

由于这样的链表提供双向指针,根据*p结点的前趋指针和后继指针可以查找到其直接前趋和直接后继,从而可以删除该结点。

其时间复杂度为O

(1)。

  3.单循环链表。

根据已知结点位置,可以直接得到其后相邻的结点位置(直接后继),又因为是循环链表,所以我们可以通过查找,得到p结点的直接前趋。

因此可以删去p所指结点。

其时间复杂度应为O(n)。

2.6该算法的功能是:

将开始结点摘下链接到终端结点之后成为新的终端结点,而原来的第二个结点成为新的开始结点,返回新链表的头指针。

2.7算法如下:

#defineListSize100//假定表空间大小为100

typedefintDataType;//假定DataType的类型为int型

typedefstruct{

   DataTypedata[ListSize];//向量data用于存放表结点

   intlength;//当前的表长度

 }Seqlist;

 //以上为定义表结构

voidInsertList(Seqlist*L,Datatypex,inti)

{

//将新结点x插入L所指的顺序表的第i个结点ai的位置上,即插入的合法位置为:

0<=i<=L->length

intj;

if(i<0||i>L->length)

   Error("positionerror");//非法位置,退出,该函数定义见教材P7.

if(L->length>=ListSize)

   Error(“overflow");

for(j=L->length-1;j>=i;j--)

   L->data[j+1]=L->data[j];

L->data[i]=x;

L->length++;

}

voidDeleteList(Seqlist*L,inti)

{//从L所指的顺序表中删除第i个结点ai,合法的删除位置为0<=i<=L->length-1

intj;

if(i<0||i>=L->length)

   Error("positionerror");

for(j=i;jlength;j++)

   L->data[j]=L->data[j+1];//结点前移

L->length--;//表长减小

}

2.8

1.顺序表:

 要将该表逆置,可以将表中的开始结点与终端结点互换,第二个结点与倒数第二个结点互换,如此反复,就可将整个表逆置了。

算法如下:

 

//顺序表结构定义同上题

 voidReverseList(Seqlist*L)

  {

   DataTypetemp;//设置临时空间用于存放data

   inti;

   for(i=0;i<=L->length/2;i++)//L->length/2为整除运算

    {temp=L->data[i];//交换数据

     L->data[i]=L->data[L->length-1-i];

     L->data[L->length-1-i]=temp;

    }

  }

2.链表:

可以用交换数据的方式来达到逆置的目的。

但是由于是单链表,数据的存取不是随机的,因此算法效率太低。

可以利用指针改指来达到表逆置的目的。

具体情况入下:

  

(1)当链表为空表或只有一个结点时,该链表的逆置链表与原表相同。

  

(2)当链表含2个以上结点时,可将该链表处理成只含第一结点的带头结点链表和一个无头结点的包含该链表剩余结点的链表。

然后,将该无头结点链表中的所有结点顺着链表指针,由前往后将每个结点依次从无头结点链表中摘下,作为第一个结点插入到带头结点链表中。

这样就可以得到逆置的链表。

算法是这样的:

  结点结构定义如下:

    typedefcharDataType;//假设结点的数据域类型的字符

    typedefstructnode{//结点类型定义

      DataTypedata;//结点的数据域

      structnode*next;//结点的指针域

     }ListNode;

    typedefListNode*LinkList;

    ListNode*p;

    LinkListhead;

  LinkListReverseList(LinkListhead)

   {//将head所指的单链表(带头结点)逆置

    ListNode*p,*q;//设置两个临时指针变量

    if(head->next&&head->next->next)

     { //当链表不是空表或单结点时

      p=head->next;

      q=p->next;

      p->next=NULL;//将开始结点变成终端结点

      while(q)

       {//每次循环将后一个结点变成开始结点 

        p=q; 

        q=q->next;

        p->next=head->next;

        head->next=p;

       }

      returnhead;

     }

    returnhead;//如是空表或单结点表,直接返回head

   }

2.9因已知顺序表L是递增有序表,所以只要从顺序表终端结点(设为i位置元素)开始向前寻找到第一个小于或等于x的元素位置i后插入该位置即可。

在寻找过程中,由于大于x的元素都应放在x之后,所以可边寻找,边后移元素,当找到第一个小于或等于x的元素位置i时,该位置也空出来了。

算法如下:

   //顺序表存储结构如题2.7

    voidInsertIncreaseList(Seqlist*L,Datatypex)

     { 

      inti;

      if(L->length>=ListSize)

       Error(“overflow");

      for(i=L->length;i>0&&L->data[i-1]>x;i--)

       L->data[i]=L->data[i];//比较并移动元素

      L->data[i]=x;

      L->length++;

     }

2.10与上题相类似,只要从终端结点开始往前找到第一个比x大(或相等)的结点数据,在这个位置插入就可以了。

(边寻找,边移动)算法如下:

  voidInsertDecreaseList(Seqlist*L,Datatypex)

   {

    inti;

    if(L->length>=ListSize)

     Error(“overflow");

    for(i=L->length;i>0&&L->data[i-1]

     L->data[i]=L->data[i];//比较并移动元素

    L->data[i]=x;

    L->length++;

   }

2.11由于在单链表中只给出一个头指针,所以只能用遍历的方法来数单链表中的结点个数了。

算法如下:

  intListLength(LinkListL)

   {

    intlen=0;

    ListNode*p;

    p=L;//设该表有头结点

    while(p->next)

     {

      p=p->next;

      len++;

     }

    returnlen;

   }

2.12分析:

  由于要进行的是两单链表的连接,所以应找到放在前面的那张表的表尾结点,再将后表的开始结点链接到前表的终端结点后即可。

该算法的主要时间消耗是用在寻找第一张表的终端尾结点上。

这两张单链表的连接顺序无要求,并且已知两表的表长,则为了提高算法效率,可选表长小的单链表在前的方式连接。

具体算法如下:

  LinkListLink(LinkListL1,LinkListL2,intm,intn)

   {//将两个单链表连接在一起

    ListNode*p,*q,*s;

    //s指向短表的头结点,q指向长表的开始结点,回收长表头结点空间 

    if(m<=n)

     {s=L1;q=L2->next;free(L2);}

    else{s=L2;q=L1->next;free(L1);}

    p=s;

    while(p->next)p=p->next;//查找短表终端结点

    p->next=q;//将长表的开始结点链接在短表终端结点后

    returns;

   }

  本算法的主要操作时间花费在查找短表的终端结点上,所以本算的法时间复杂度为:

O(min(m,n))

2.13根据已知条件,A和B是两个递增有序表,所以可以先取A表的表头建立空的C表。

然后同时扫描A表和B表,将两表中最大的结点从对应表中摘下,并作为开始结点插入C表中。

如此反复,直到A表或B表为空。

最后将不为空的A表或B表中的结点依次摘下并作为开始结点插入C表中。

这时,得到的C表就是由A表和B表归并成的一个按元素值递减有序的单链表C。

并且辅助空间为O

(1)。

算法如下:

  LinkListMergeSort(LinkListA,LinkListB)

   {//归并两个带头结点的递增有序表为一个带头结点递减有序表

    ListNode*pa,*pb,*q,*C;

    pa=A->next;//pa指向A表开始结点

    C=A;C->next=NULL;//取A表的表头建立空的C表

    pb=B->next;//pb指向B表开始结点

    free(B);//回收B表的头结点空间

    while(pa&&pb)

     {

      if(pb->data<=pa->data)

       {//当B中的元素小于等于A中当前元素时,将pa表的开始结点摘下

        q=pa;pa=pa->next;

       }

      else

       {//当B中的元素大于A中当前元素时,将pb表的开始结点摘下

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

      q->next=C->next;C->next=q;//将摘下的结点q作为开始结点插入C表

     }

    //若pa表非空,则处理pa表

    while(pa){

      q=pa;pa=pa->next;

      q->next=C->next;C->next=q;}

    //若pb表非空,则处理pb表

    while(pb){

      q=pb;pa=pb->next;

      q->next=C->next;C->next=q;}

    return(C);

   } 

  该算法的时间复杂度分析如下:

  算法中有三个while循环,其中第二个和第三个循环只执行一个。

每个循环做的工作都是对链表中结点扫描处理。

整个算法完成后,A表和B表中的每个结点都被处理了一遍。

所以若A表和B表的表长分别是m和n,则该算法的时间复杂度O(m+n)

2.14要解这样的问题,我们首先想到的是拿链表中的元素一个个地与max和min比较,然后删除这个结点。

由于为已知其是有序链表,则介于min和max之间的结点必为连续的一段元素序列。

所以我们只要先找到所有大于min结点中的最小结点的直接前趋结点*p后,依次删除小于max的结点,直到第一个大于等于max结点*q位置,然后将*p结点的直接后继指针指向*q结点。

 算法如下:

  voidDeleteList(LinkListL,DataTypemin,DataTypemax)

   {

    ListNode*p,*q,*s;

    p=L;

    while(p->next&&p->next->data<=min) 

    //找比min大的前一个元素位置

     p=p->next;

    q=p->next;//p指向第一个不大于min结点的直接前趋,q指向第一个大于min的结点

    while(q&&q->data

     {s=q;q=q->next;

      free(s);//删除结点,释放空间

     }

    p->next=q;//将*p结点的直接后继指针指向*q结点

   }

2.15本题可以这样考虑,先取开始结点中的值,将它与其后的所有结点值一一比较,发现相同的就删除掉,然后再取第二结点的值,重复上述过程直到最后一个结点。

具体算法:

   voidDeleteList(LinkListL)

    { 

     ListNode*p,*q,*s;

     p=L-next;

     while(p->next&&p->next->next)

      {

       q=p;//由于要做删除操作,所以q指针指向要删除元素的直接前趋

       while(q->next)

        if(p->data==q->next->data)

         {s=q->next;q->next=s->next;free(s);//删除与*p的值相同的结点

         }

        elseq=q->next;

       p=p->next;

      }

    }  

第三章

3.

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 金融投资

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1