数据结构(C语言版 第2版)第二章课后习题答案 严蔚敏 等 编著

第 2 章线性表
1.选择题
( 1)顺 序表中 第一个 元 素的存储 地址 是 100 ,每 个元素的 长度 为 2,则 第 5 个 元 素 的 地 址 是 ( )。 A. 110 B . 108 C. 100 D. 120
答案: B
解释:顺序表中的数据连续存储,所以第 5 个元素的地址为: 100+2*4=108 。

( 2)在 n 个结点的顺序表中,算法的时间复杂度是 O(1) 的操作是() 。 A .访问第 i 个结点( 1≤ i≤ n)和求第 i 个结点的直接前驱( 2≤ i≤ n) B .在第 i 个结点后插入一个新结点( 1≤ i≤ n) C.删除第 i 个结点( 1≤ i≤ n) D .将 n 个结点从小到大排序
答案: A
解释: 在顺序表中插入一个结点的时间复杂度都是 O(n 2) ,排序的时间复杂度为 O(n 2 ) 或 O(nlog 2n)。顺序表是一种随机存取结构,访问第 i 个结点和求第 i 个结点的直接前驱都可
以直接通过数组的下标直接定位,时间复杂度是 O(1) 。

( 3)向一个有 127 个元素的顺序表中插入一个新元素并保持原来顺序不变, 平均要移动的元素个数为() 。 A. 8 B . 63.5 C . 63 D. 7
答案: B
解释:平均要移动的元素个数为: n/2 。

( 4)链接存储的存储结构所占存储空间() 。
A .分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针
B .只有一部分,存放结点值
C.只有一部分,存储表示结点间关系的指针
D .分两部分,一部分存放结点值,另一部分存放结点所占单元数
答案: A

( 5)线性表若采用链式存储结构时,要求内存中可用存储单元的地址() 。
A .必须是连续的 B.部分地址必须是连续的
C.一定是不连续的 D.连续或不连续都可以
答案: D

( 6)线性表L在()情况下适用于使用链式结构实现。
A .需经常修改L中的结点值B.需不断对L进行删除插入
C.L中含有大量的结点D.L中结点结构复杂
答案: B
解释:链表最大的优点在于插入和删除时不需要移动数据,直接修改指针即可。

( 7)单链表的存储密度() 。
A .大于 1 B .等于 1 C.小于 1 D.不能确定
答案: C
解释:存储密度是指一个结点数据本身所占的存储空间和整个结点所占的存储空
间之比,假设单链表一个结点本身所占的空间为 D,指针域所占的空间为 N,则存储密
度为: D/(D+N) ,一定小于 1。

( 8)将两个各有 n 个元素的有序表归并成一个有序表,其最少的比较次数是() 。
A . n B . 2n-1 C . 2n D . n-1
答案: A
解释:当第一个有序表中所有的元素都小于(或大于)第二个表中的元素,只需
要用第二个表中的第一个元素依次与第一个表的元素比较,总计比较 n 次。

( 9)在一个长度为 n 的顺序表中,在第 i 个元素( 1≤ i≤ n+1 )之前插入一个新元素时
须向后移动()个元素。
A . n-i B . n-i+1 C. n-i-1 D . I
答案: B

(10) 线性表 L=(a 1, a2 , an) ,下列说法正确的是() 。 A .每个元素都有一个直接前驱和一个直接后继
B .线性表中至少有一个元素
C.表中诸元素的排列必须是由小到大或由大到小
D .除第一个和最后一个元素外,其余每个元素都有一个且仅有一个直接前驱和直接
后继。
答案: D

(11) 创建一个包括 n 个结点的有序单链表的时间复杂度是() 。 A . O(1) B . O(n) C. O(n 2
) D . O(nlog 2n)
答案: C
解释:单链表创建的时间复杂度是 O(n) ,而要建立一个有序的单链表,则每生成一个新结点时需要和已有的结点进行比较,确定合适的插入位置,所以时间复杂度是O(n2) 。

(12) 以下说法错误的是() 。
A .求表长、定位这两种运算在采用顺序存储结构时实现的效率不比采用链式存储结
构时实现的效率低
B .顺序存储的线性表可以随机存取
C.由于顺序存储要求连续的存储区域,所以在存储管理上不够灵活
D .线性表的链式存储结构优于顺序存储结构
答案: D
解释: 链式存储结构和顺序存储结构各有优缺点,有不同的适用场合。

(13) 在单链表中,要将 s 所指结点插入到 p 所指结点之后,其语句应为() 。
A . s->next=p+1;p->next=s;
B . (*p).next=s;(*s).next=(*p).next;
C. s->next=p->next;p->next=s->next;
D . s->next=p->next;p->next=s;
答案: D

(14) 在双向链表存储结构中,删除 p 所指的结点时须修改指针() 。
A . p->next->prior=p->prior;p->prior->next=p->next;
B . p->next=p->next->next;p->next->prior=p;
C. p->prior->next=p;p->prior=p->prior->prior;
D . p->prior=p->next->next;p->next=p->prior->prior;
答案: A

(15) 在双向循环链表中,在 p 指针所指的结点后插入 q 所指向的新结点,其修改指针的
操作是() 。
A . p->next=q; q->prior=p;p->next->prior=q;q->next=q;
B . p->next=q;p->next->prior=q;q->prior=p;q->next=p->next;
C. q->prior=p;q->next=p->next;p->next->prior=q;p->next=q;
D . q->prior=p;q->next=p->next;p->next=q;p->next->prior=q;
答案: C

2.算法设计题
( 1)将两个递增的有序链表合并为一个递增的有序链表。 要求结果链表仍使用原来两个
链表的存储空间 , 不另外占用其它的存储空间。表中不允许有重复的数据。
[ 题目分析 ]
合并后的新表使用头指针 Lc 指向, pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为
相应链表的第一个结点,从第一个结点开始进行比较,当两个链表 La 和 Lb 均为到达表尾结
点时,依次摘取其中较小者重新链接在 Lc 表的最后。如果两个表中的元素相等,只摘取 La
表中的元素,删除 Lb 表中的元素,这样确保合并后表中无重复的元素。当一个表 到达表尾结
点,为空时,将非空表的剩余元素直接链接在 Lc 表的最后。
[ 算法描述 ]

void MergeList(LinkList &La,LinkList &Lb,LinkList &Lc) 
{// 合并链表 La 和 Lb,合并后的新表使用头指针 Lc 指向
    pa=La->next; pb=Lb->next; 
    //pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为相应链表的第一个结点
    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 指向, pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为
相应链表的第一个结点,从第一个结点开始进行比较,当两个链表 La 和 Lb 均为到达表尾结
点时,依次摘取其中较小者重新链接在 Lc 表的表头结点之后,如果两个表中的元素相等,只
摘取 La 表中的元素,保留 Lb 表中的元素。当一个表到达表尾结点,为空时,将非空表的剩
余元素依次摘取,链接在 Lc 表的表头结点之后。
[ 算法描述 ]

void MergeList(LinkList& La, LinkList& Lb, LinkList& Lc, ) 
{// 合并链表 La 和 Lb,合并后的新表使用头指针 Lc 指向
     pa=La->next; pb=Lb->next; 
    //pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为相应链表的第一个结点
     Lc=pc=La; // 用 La 的头结点作为 Lc 的头结点
     Lc->next=NULL; 
     while(pa||pb ) 
    {// 只要存在一个非空表,用 q 指向待摘取的元素
         if(!pa) 
             {q=pb; pb=pb->next;} 
    //La 表为空,用 q 指向 pb , pb 指针后移
         else if(!pb) 
             {q=pa; pa=pa->next;} 
    //Lb 表为空,用 q 指向 pa , pa 指针后移
         else if(pa->data<=pb->data) 
             {q=pa; pa=pa->next;} 
    // 取较小者(包括相等) La 中的元素,用 q 指向 pa, pa 指针后移
        else 
            {q=pb; pb=pb->next;} 
    // 取较小者 Lb 中的元素,用 q 指向 pb, pb 指针后移
     q->next = Lc->next; Lc->next = q; 
    // 将 q 指向的结点插在 Lc 表的表头结点之后
    } 
    delete Lb; // 释放 Lb 的头结点
} 


( 3)已知两个链表 A 和 B 分别表示两个集合,其元素递增排列。请设计算法求出 A 与 B
的交集,并存放于 A 链表中。
[ 题目分析 ]
只有同时出现在两集合中的元素才出现在结果表中 , 合并后的新表使用头指针 Lc 指向。
pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为相应链表的第一个结点, 从第一个结点开
始进行比较,当两个链表 La 和 Lb 均为到达表尾结点时,如果两个表中相等的元素时,摘取
La 表中的元素,删除 Lb 表中的元素;如果其中一个表中的元素较小时,删除此表中较小的
元素,此表的工作指针后移。当链表 La 和 Lb 有一个到达表尾结点,为空时,依次删除另一
个非空表中的所有元素。
[ 算法描述 ]

void Mix(LinkList& La, LinkList& Lb, LinkList& Lc) 
{pa=La->next;pb=Lb->next; 
    pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为相应链表的第一个结点
    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; delete u;} 
        else 
            {u=pb; pb=pb->next; delete u;} 
    } 
    while(pa){u=pa; pa=pa->next; delete u;} ∥ 释放结点空间
    while(pb) {u=pb; pb=pb->next; delete u ;} ∥释放结点空间
    pc- >next=null; ∥置链表尾标记。
    delete Lb; // 释放 Lb 的头结点
 }


( 4)已知两个链表 A 和 B 分别表示两个集合,其元素递增排列。请设计算法求出两个集
合 A 和 B 的差集(即仅由在 A 中出现而不在 B 中出现的元素所构成的集合) ,并以同样的形
式存储,同时返回该集合的元素个数。
[ 题目分析 ]
求两个集合 A 和 B 的差集是指在 A 中删除 A 和 B 中共有的元素,即删除链表中的相应结
点 , 所以要保存待删除结点的前驱,使用指针 pre 指向前驱结点。 pa 和 pb 分别是链表 La 和
Lb 的工作指针 , 初始化为相应链表的第一个结点,从第一个结点开始进行比较,当两个链表
La 和 Lb 均为到达表尾结点时,如果 La 表中的元素小于 Lb 表中的元素, pre 置为 La 表的工
作指针 pa 删除 Lb 表中的元素;如果其中一个表中的元素较小时,删除此表中较小的元素,
此表的工作指针后移。 当链表 La 和 Lb 有一个为空时, 依次删除另一个非空表中的所有元素。
[ 算法描述 ]

void Difference ( LinkList& La, LinkList& Lb,int *n ) { ∥差集的结果存储于单链表 La 中, *n 是结果集合中元素个数,调用时为 0 
    pa=La->next; pb=Lb->next; 
    ∥ pa 和 pb 分别是链表 La 和 Lb 的工作指针 , 初始化为相应链表的第一个结点
     pre=La; ∥ pre 为 La 中 pa 所指结点的前驱结点的指针
    while ( pa&&pb)
    {
        if ( pa->data<q->data ) 
            {pre=pa;pa=pa->next;*n++;} 
        ∥ A 链表中当前结点指针后移
        else if ( pa->data>q->data ) 
            q=q->next; ∥ B 链表中当前结点指针后移
        else 
        {
            pre->next=pa->next; ∥处理 A, B 中元素值相同的结点,应删除
             u=pa; pa=pa->next;delete u;
         } ∥删除结点
    } 
} 


( 5)设计算法将一个带头结点的单链表 A 分解为两个具有相同结构的链表 B、C,其中 B
表的结点为 A 表中值小于零的结点,而 C 表的结点为 A 表中值大于零的结点(链表 A 中的元
素为非零整数,要求 B、 C 表利用 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;// 遍历链表
} 
return pmax->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 的所有元素( mink
和 maxk 是给定的两个参数,其值可以和表中的元素相同,也可以不同 )。 [ 题目分析 ]
分别查找第一个值 >mink 的结点和第一个值 ≥ maxk 的结点,再修改指针,删除值大于
mink 且小于 maxk 的所有元素。
[ 算法描述 ]

void delete(LinkList &L, int mink, int maxk) { 
     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 指向双向循环链表中的一个结点, 其结点结构为 data 、prior 、next 三个域,
写出算法 change§, 交换 p 所指向的结点和它的前缀结点的顺序。
[ 题目分析 ]
知道双向循环链表中的一个结点,与前驱交换涉及到四个结点( p 结点,前驱结点,前
驱的前驱结点,后继结点)六条链。
[ 算法描述 ]

void Exchange ( LinkedList 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=1 , j=n ),从两端向中间移动,
凡遇到值 item 的数据元素时,直接将右端元素左移至值为 item 的数据元素位置。
[ 算法描述 ]

void Delete ( ElemType A[ ] , int n ) ∥ A 是有 n 个元素的一维数组,本算法删除 A 中所有值为 item 的元素。
{
    i=1 ; j=n ;∥设置数组低、高端指针(下标) 。
    while ( i<j ){
        while ( i<j && A[i]!=item ) 
            i++ ;∥若值不为 item ,左移指针。
    if ( i<j ) 
        while ( i<j && A[j]==item ) 
            j-- ;∥若右端元素为 item ,指针左移
    if ( i<j ) A[i++]=A[j--] ; 
}
  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第1章 绪论 1.1 数据结构的基本概念和术语 1.1.1 引言 1.1.2 数据结构有关概念及术语 1.1.3 数据结构和抽象数据类型(ADT) 1.2 算法描述与分析 1.2.1 什么是算法 1.2.2 算法描述工具——C语言 1.2.3 算法分析技术初步 习题一 第2章 线性表 2.1 线性表的定义及其运算 2.1.1 线性表的定义 2.1.2 各种运算简介 2.2 线性表的顺序存储结构(向量) 2.2.1 顺序存储结构(向量) 2.2.2 向量中基本运算的实现 2.3 线性表的链表存储结构 2.3.1 单链表与指针 2.3.2 单链表的基本运算 2.4 循环链表和双向链表 2.4.1 循环链表 2.4.2 双向链表 2.4.3 顺序存储结构与链表存储结构的综合分析与比较 2.5 多项式相加问题 2.5.1 多项式相加的链表存储结构 2.5.2 多项式相加的算法实现 2.6 线性表的算法实现举例 2.6.1 实现线性表顺序存储结构及运算的C语言源程序 2.6.2 单链表处理的C语言源程序 习题二 第3章 栈和队列 3.1 栈 3.1.1 栈的定义及其运算 3.1.2 栈的顺序存储结构(向量) 3.1.3 栈的链表存储结构 3.1.4 栈的应用 3.2 队列 3.2.1 队列的定义及运算 3.2.2 队列的顺序存储结构(向量) 3.2.3 队列的链表存储结构 3.3 栈和队列的算法实现举例 习题三 第4章 串 4.1 串的基本概念 4.2 串的存储结构 4.2.1 串的顺序存储 4.2.2 串的链表存储 4.2.3 串变量的存储映象 4.3 串的运算 4.3.1 串的运算简介 4.3.2 串的匹配运算 4.4 文本编辑 习题四 第5章 数组和广义表 5.1 数组的基本概念 5.1.1 数组的概念 5.1.2 数组的顺序表示 5.1.3 特殊矩阵的压缩存储 5.2 稀疏矩阵的三元组存储 5.2.1 三元组表 5.2.2 稀疏矩阵的运算 5.3 稀疏矩阵的十字链表存储 5.3.1 十字链表的组成 5.3.2 十字链表的有关算法 5.4 广义表 5.4.1 广义表的概念和特性 5.4.2 广义表的存储结构 5.4.3 求广义表的深度 5.4.4 广义表的输出 5.4.5 建立广义表的存储结构 5.5 迷宫问题 习题五 第6章 树 6.1 树的基本概念和术语 6.1.1 树的定义 6.1.2 树的常用术语 6.1.3 树的表示方法 6.2 二叉树 6.2.1 二叉树的定义 6.2.2 二叉树的重要性质 6.2.3 二叉树的存储结构 6.2.4 二叉树二叉链表的一个生成算法 6.3 遍历二叉树 6.3.1 先根遍历 6.3.2 中根遍历 6.3.3 后根遍历 6.3.4 二叉树遍历算法的应用 6.4 线索二叉树 6.4.1 线索二叉树的基本概念 6.4.2 线索二叉树的逻辑表示图 6.4.3 中根次序线索化算法 6.4.4 在中根线索树上检索某结点的前趋或后继 6.4.5 在中根线索树上遍历二叉树 6.5 二叉树、 树和森林 6.5.1 树的存储结构 6.5.2 树与二叉树之间的转换 6.5.3 森林与二叉树的转换 6.5.4 一般树或森林的遍历 6.6 树的应用 6.6.1 二叉排序树 6.6.2 哈夫曼树及其应用 6.7 二叉树的建立和遍历C语言源程序示例 习题六 第7章 图 7.1 图的基本概念和术语 7.1.1 图的基本概念 7.1.2 路径和回路 7.1.3 连通图 7.1.4 顶点的度 7.2 图的存储结构 7.2.1 邻接矩阵 7.2.2 邻接链表 7.3 图的遍历和求图的连通分量 7.3.1 图的建立 7.3.2 图的遍历 7.3.3 求图的连通分量 7.4 图的生成树 7.4.1 生成树的概念 7.4.2 最小生成树 7.4.3 普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法 7.5 最短路径 7.5.1 单源顶点最短路径问题求解 7.5.2 求有向网中每对顶点间的路径 7.6 有向无环图及应用 7.6.1 拓扑排序 7.6.2 关键路径 7.7 图的算法C语言程序实现举例 7.7.1 无向图的邻接表的建立和遍历 7.7.2 有向无环图的拓扑排序和求关键路径 习题七 第8章 查找 8.1 基本概念

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值