第二章 线性表——课后习题解练【数据结构(c语言版 第2版)】

第二章 线性表

1.选择题

(1)(2)

答案:BA

(3)

(4)(5)(6)

答案:ADB

(7)

(8)

答案:A

(9)

(10)

答案:D

(11)

(12)(13)(14)

答案:DDA

(15)

2.算法设计题

(1)

cpp 复制代码
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) {
    // 初始化指针
    pa = La->next;  // pa 指向 La 的第一个数据结点
    pb = Lb->next;  // pb 指向 Lb 的第一个数据结点
    
    // 使用 La 的头结点作为 Lc 的头结点
    Lc = pc = La;
    
    // 遍历两个链表,直到其中一个遍历完
    while (pa && pb) {
        // 如果 pa 的数据较小,将 pc 移动到新的尾结点,pa 后移指向下一个结点
        if (pa->data < pb->data) {
            pc->next = pa;
            pc = pa;         // pc 移动到新的尾结点
            pa = pa->next;   // pa 后移
        } 
        // 如果 pb 的数据较小,将 pb 结点链接到 pc 后面(新的尾结点),pb 后移指向下一个结点
        else if (pa->data > pb->data) {
            pc->next = pb;
            pc = pb;         // pc 移动到新的尾结点
            pb = pb->next;   // pb 后移
        }
        // 如果数据相等,取 La 的元素,删除 Lb 的元素
        else {
            pc->next = pa;
            pc = pa;         // pc 移动到新的尾结点
            pa = pa->next;   // pa 后移
            
            q = pb->next;    // 保存 pb 的下一个结点
            delete pb;       // 释放当前 pb 结点
            pb = q;          // pb 移动到下一个结点
        }
    }
    
    // 将剩余部分链接到结果链表尾部
    if (pa != NULL) {
        pc->next = pa;  // 如果 pa 不为空,链接 pa 剩余部分
    } else {
        pc->next = pb;  // 否则链接 pb 剩余部分
    }
    
    // 释放 Lb 的头结点
    delete Lb;
}

(2)

cpp 复制代码
// 函数功能:合并两个有序链表La和Lb,结果存储在Lc中(使用头插法,结果为降序)
// 参数说明:
//   La, Lb: 待合并的两个有序链表(带头结点)
//   Lc: 合并后的结果链表
void union(LinkList& La, LinkList& Lb, LinkList& Lc) {
    // 初始化指针
    pa = La->next;  // pa指向La的第一个数据结点
    pb = Lb->next;  // pb指向Lb的第一个数据结点
    
    // 使用La的头结点作为Lc的头结点
    Lc = pc = La;
    Lc->next = NULL;  // 初始化Lc为空链表
    
    // 循环处理,直到两个链表都遍历完
    while (pa || pb) {
        // 选择较小的结点(或剩余结点)
        if (!pa) {          // 如果La已遍历完//if (!pa)等价于 if (pa == NULL)
            q = pb;         // 取Lb的当前结点
            pb = pb->next;  // pb指针后移
        }
        else if (!pb) {     // 如果Lb已遍历完
            q = pa;         // 取La的当前结点
            pa = pa->next;  // pa指针后移
        }
        // 比较两个链表的当前结点
        else if (pa->data <= pb->data) {  // La的结点值较小或相等
            q = pa;         // 取La的当前结点
            pa = pa->next;  // pa指针后移
        }
        else {              // Lb的结点值较小
            q = pb;         // 取Lb的当前结点
            pb = pb->next;  // pb指针后移
        }
        
        // 将选中的结点插入到Lc的头部(头插法)
        q->next = Lc->next;  // 新结点的next指向原Lc的第一个结点
        Lc->next = q;        // Lc的头结点指向新结点
    }
    
    // 释放Lb的头结点
    delete Lb;
}

(3)

cpp 复制代码
// 函数功能:求两个有序链表La和Lb的交集,结果存储在Lc中
// 参数说明:
//   La, Lb: 输入的两个有序链表(带头结点)
//   Lc: 输出的结果链表
void Mix(LinkList& La, LinkList& Lb, LinkList& Lc) {
    // 初始化工作指针
    pa = La->next;  // pa指向La的第一个数据结点
    pb = Lb->next;  // pb指向Lb的第一个数据结点

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

    // 遍历两个链表,直到其中一个遍历完
    while (pa && pb) {
        if (pa->data == pb->data) {
            // 情况1:当前结点值相等,将pa结点加入结果链表
            pc->next = pa;   // 将pa链接到结果链表
            pc = pa;         // pc移动到新的尾结点
            pa = pa->next;   // pa后移

            // 释放pb的当前结点(因为值相等,只需保留一个)
            q = pb;
            pb = pb->next;
            delete q;
        }
        else if (pa->data < pb->data) {
            // 情况2:pa结点值较小,直接释放pa结点
            q = pa;
            pa = pa->next;
            delete q;
        }
        else {
            // 情况3:pb结点值较小,直接释放pb结点
            q = pb;
            pb = pb->next;
            delete q;
        }
    }

    // 释放La的剩余结点(如果有)
    while (pa) {
        q = pa;
        pa = pa->next;
        delete q;
    }

    // 释放Lb的剩余结点(如果有)
    while (pb) {
        q = pb;
        pb = pb->next;
        delete q;
    }

    // 设置结果链表的尾结点
    pc->next = NULL;

    // 释放Lb的头结点(可选)
    delete Lb;
}

(4)

cpp 复制代码
// 函数功能:求两个递增有序单链表的差集 A - B,结果存储在链表 A 中
// 参数说明:
//   A, B: 两个带头结点的递增有序单链表,分别存储一个集合
//   n: 结果集合元素个数的引用,调用时传入一个整型变量
void Difference(LinkedList A, LinkedList B, int &n) {
    // 初始化工作指针
    p = A->next;        // p 指向链表 A 的第一个数据结点
    q = B->next;        // q 指向链表 B 的第一个数据结点  
    head = A;           // head 指向 p 的前驱结点(即 A 的头结点)
    
    n = 0;              // 初始化计数器(引用可以直接赋值)

    // 同步遍历两个链表,直到其中一个遍历完
    while (p != NULL && q != NULL) {
        if (p->data < q->data) {
            // 情况1:A 的当前结点值小于 B 的当前结点值
            // 该结点属于差集,保留并继续处理
            head = p;           // head 指针后移,保持指向 p 的前驱
            p = p->next;        // p 指针后移
            n++;                // 差集元素个数加1(直接使用 n,不需要解引用)
        }
        else if (p->data > q->data) {
            // 情况2:A 的当前结点值大于 B 的当前结点值
            // B 需要继续向后查找可能相等的结点
            q = q->next;        // q 指针后移
        }
        else {
            // 情况3:A 和 B 的当前结点值相等
            // 该结点不属于差集,需要从 A 中删除
            head->next = p->next;    // 将前驱结点的 next 指向 p 的后继结点
            
            // 删除当前结点 p
            u = p;              // 保存要删除的结点
            p = p->next;        // p 指针后移
            delete u;           // 释放被删除结点的内存
        }
    }
    
    // 循环结束后,如果 A 还有剩余结点,它们都属于差集
    while (p != NULL) {
        n++;                    // 差集元素个数加1
        p = p->next;           // 继续遍历 A 的剩余结点
    }
}

(6)

cpp 复制代码
// 函数功能:在单链表中查找最大值
// 参数说明:L是带头结点的单链表
// 返回值:链表中最大的元素值,若链表为空则返回NULL
int Max(LinkList L) {
    // 检查链表是否为空(只有头结点)
    if (L->next == NULL) {
        return NULL;
    }

    // 初始化指针
    LNode *pmax = L->next;    // 假设第一个结点为最大值
    LNode *p = L->next->next; // 从第二个结点开始遍历

    // 遍历链表查找最大值
    while (p != NULL) {
        // 如果当前结点值大于已知最大值
        if (p->data > pmax->data) {
            pmax = p;  // 更新最大值指针
        }
        p = p->next;   // 移动到下一个结点
    }

    // 返回最大值结点的数据
    return pmax->data;
}

(7)

cpp 复制代码
// 函数功能:逆置带头结点的单链表
// 参数说明:L - 带头结点的单链表(引用传递)
void inverse(LinkList &L) {
    // 初始化指针
    LNode *p = L->next;  // p指向链表第一个结点
    L->next = NULL;       // 将头结点的next置空,准备重建链表

    // 遍历原链表,逐个结点逆置
    while (p != NULL) {
        LNode *q = p->next;  // q临时保存p的后继结点
        
        // 将当前结点p插入到新链表的头部(头结点之后)
        p->next = L->next;   // 将p的next指向原头结点的后继
        L->next = p;         // 头结点指向新的首元结点

        p = q;  // p移动到原链表的下一个结点
    }
}

(8)

cpp 复制代码
void delete(LinkList &L, int mink, int maxk) {
    if (L == NULL || L->next == NULL) return; // 空链表直接返回

    LNode *pre = L;       // pre始终指向当前结点的前驱
    LNode *p = L->next;   // p从首元结点开始遍历

    while (p != NULL) {
        if (p->data > mink && p->data < maxk) {
            // 找到需要删除的结点
            pre->next = p->next; // 跳过当前结点
            LNode *temp = p;     // 临时保存要删除的结点
            p = p->next;         // p指针后移
            free(temp);          // 释放内存
        } else {
            // 不需要删除的结点
            pre = p;
            p = p->next;
        }
    }
}