数据结构实验十一:插入排序+期末复习

本节课实验课总体安排:

1、讲解上节课布置的3道课后习题

2、设计1道算法题:折半插入排序

3、布置1道力扣题

4、布置下次课后作业:学习通,期末习题复习一

一、第七章课后习题讲解

【P286第4题】

        折半查找有序表(4,6,10,12,20,30,50,70,88,100)。若查找表中元素 58,则它将依次与表中( )比较大小,查找结果是失败。

A.20,70,30,50         B.30,88,70,50

C.20,50                         D.30,88,50

答案:A

解释:表中共 10 个元素,第一次向下取整(1+10)/2=5,与第五个元素 20 比较,58 大于20,再向下取整(6+10)/2=8,与第八个元素 70 比较,依次类推再与 30、50 比较,最终查找失败。

【第7题】

        分别以下列序列构造二叉排序树,与用其它三个序列所构造的结果不同的是( )

                A.(100,80, 90, 60, 120,110,130)

                B.(100,120,110,130,80, 60, 90)

                C.(100,60, 80, 90, 120,110,130)

                D.(100,80, 60, 90, 120,130,110)

答案:C

解释:A、B、C、D 四个选项构造二叉排序树都以 100 为根,易知 A、B、D 三个序 列中第一个比 100 小的关键字为 80,即 100 的左孩子为 80,而 C 选项中 100 的左孩子为 60,故选 C。

这道题考察 二叉排序树(BST, Binary Search Tree) 的构造和序列的等价性。关键是:

  • 二叉排序树的性质:对于每个节点,左子树上所有节点都小于它,右子树上所有节点都大于它。

  • 等价二叉排序树:如果插入顺序不同,但构造出的树形状相同,就算等价。


步骤一:分析每个序列

序列 A: 100, 80, 90, 60, 120, 110, 130

  1. 插入 100 → 根节点

  2. 插入 80 → 左子节点

  3. 插入 90 → 80 的右子节点(80 < 90 < 100)

  4. 插入 60 → 80 的左子节点

  5. 插入 120 → 100 的右子节点

  6. 插入 110 → 120 的左子节点

  7. 插入 130 → 120 的右子节点

A 树形状

        100
       /   \
     80    120
    /  \   / \
   60  90 110 130

序列 B: 100, 120, 110, 130, 80, 60, 90

  1. 100 → 根节点

  2. 120 → 100 右子节点

  3. 110 → 120 左子节点

  4. 130 → 120 右子节点

  5. 80 → 100 左子节点

  6. 60 → 80 左子节点

  7. 90 → 80 右子节点

B 树形状

        100
       /   \
     80     120
    /  \    / \
   60  90 110 130

✅ 和 A 完全相同。


序列 C: 100, 60, 80, 90, 120, 110, 130

  1. 100 → 根节点

  2. 60 → 100 左子节点

  3. 80 → 60 右子节点

  4. 90 → 80 右子节点

  5. 120 → 100 右子节点

  6. 110 → 120 左子节点

  7. 130 → 120 右子节点

C 树形状

        100
       /   \
     60     120
       \    / \
       80 110 130
         \
         90

✅ 和 A、B 不同(左子树结构不同)。


序列 D: 100, 80, 60, 90, 120, 130, 110

  1. 100 → 根

  2. 80 → 100 左

  3. 60 → 80 左

  4. 90 → 80 右

  5. 120 → 100 右

  6. 130 → 120 右

  7. 110 → 120 左

D 树形状

        100
       /   \
     80     120
    /  \    / \
   60  90 110 130

【第14题】

        设哈希表长为 14,哈希函数是 H(key)=key%11,表中已有数据的关键字为 15, 38,61,84 共四个,现要将关键字为 49 的元素加到表中,用二次探测法解决冲突,则放入的位置是( )。

        A.8         B.3         C.5         D.9

答案:D

解释:关键字 15 放入位置 4,关键字 38 放入位置 5,关键字 61 放入位置 6,关键字 84 放入位置 7,再添加关键字 49,计算得到地址为 5,冲突,用二次探测法解决冲突得到新 地址为 6,仍冲突,再用用二次探测法解决冲突,得到新地址为 4,仍冲突,再用用二次探测法解决冲突,得到新地址为 9,不冲突,即将关键字 49 放入位置 9。

二、折半插入排序算法题

        给定一个长度为 10 的整数序列:{12, 2, 16, 30, 28, 10, 16, 20, 6, 18},请你使用 折半插入排序(Binary Insertion Sort) 对该序列进行排序。要求按照折半插入排序的思想:

1、对序列从左到右逐个处理;

2、对当前元素在已排序区间中利用折半查找(二分查找)确定插入位置;

3、最终输出一个从小到大排列的序列。

【输入示例】:

12 2 16 30 28 10 16 20 6 18

【输出示例】:

2 6 10 12 16 16 18 20 28 30

#include <stdio.h>

void BinaryInsertSortWithSentinel(int a[], int n) {
    int i, j, low, high, mid, temp;

    // 数组 a 的有效数据放在 a[1..n],a[0] 用作哨兵
    for (i = 2; i <= n; i++) {
        temp = a[i];

        // 折半查找在已排序区间 a[1..i-1] 中确定插入位置 low(1..i)
        low = 1;
        high = i - 1;
        while (low <= high) {
            mid = (low + high) / 2;
            if (a[mid] > temp)
                high = mid - 1;
            else
                low = mid + 1;
        }
        // 此时 low 为插入位置

        // 将待插入元素放入哨兵 a[0]
        a[0] = temp;

        // 从 i-1 开始向左搬移大于哨兵的元素(哨兵保证不越界)
        j = i - 1;
        while (j >= low) {     // 因为通过折半已算出 low,仍用 low 限制搬移范围
            a[j + 1] = a[j];
            j--;
        }

        // 把哨兵(即 temp)放到正确位置
        a[low] = a[0];
    }
}

int main() {
    // 使用 1-based 存放输入,a[0] 保留给哨兵
    int a[11] = {0, 12, 2, 16, 30, 28, 10, 16, 20, 6, 18};
    int n = 10;
    int i;

    BinaryInsertSortWithSentinel(a, n);

    // 输出排序后的序列(从 a[1] 到 a[n])
    for (i = 1; i <= n; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");

    return 0;
}

三、算法题讲解

  1. void BinaryInsertSortWithSentinel(int a[], int n) {

    • 定义一个函数,接收一个整型数组 a 和元素个数 n。约定数组 a 使用 1-based 下标 存放实际数据(也就是有效元素在 a[1] 到 a[n]),a[0] 用作哨兵(sentinel)

    • 教学点:说明为何使用 1-based(哨兵更直观),以及哨兵的作用是帮助保存临时值、简化边界判断或减少某些比较。

  2. int i, j, low, high, mid, temp;

    • 局部变量说明:

      • i:外层循环索引,表示当前要插入的元素位置(从第 2 个元素开始)。

      • j:用于元素后移时的索引(从右向左移动)。

      • low, high, mid:用于在已排序区间做折半(binary)查找,确定插入位置。

      • temp:暂存当前待插入元素的值(将放到哨兵 a[0])。

    • 教学点:讲解每个变量的含义及取值范围(low 在 [1, i]high 在 [1, i-1])。

  3. for (i = 2; i <= n; i++) {

    • 从第二个元素开始,对数组中每一个元素执行“插入”操作(因为第 1 个元素 a[1] 初始时视为已排序)。

    • 教学点:说明插入排序的外层循环为什么从 2 开始,强调“已排序区间”和“待插入元素”的概念。

  4. temp = a[i];

    • 把当前要插入的值保存到 temp(暂存),后续将先通过折半找到合适位置,再搬移元素,最后把 temp 插入。

    • 教学点:这样做保留原值,方便后移元素覆盖位置时不会丢失待插入值。

  5. low = 1;

    • 折半查找的左界初始化为 1(已排序区间左端)。

  6. high = i - 1;

    • 折半查找的右界初始化为 i - 1(已排序区间右端)。

  7. while (low <= high) {

    • 标准的二分查找循环,以在 a[low..high] 中寻找插入点。

    • 教学点:这里的目标不是寻找元素是否存在,而是确定 插入位置(第一个大于 temp 的位置),通常二分返回 low 作为插入位置。

  8. mid = (low + high) / 2;

    • 取中间位置 mid。课堂上可指出用 (low+high)/2 会有溢出风险(在 C 中可以改成 low + (high-low)/2),但对于小规模教学数组问题可以直接使用。

  9. if (a[mid] > temp)

  10. high = mid - 1;

  11. else

  12. low = mid + 1;

    • 如果中点的值大于 temp,说明插入点在左半区(包含 mid),因此把 high 移到 mid - 1;否则插入点在右半区(mid+1..high),把 low 移到 mid + 1

    • 循环结束后,low 就是应该插入的位置(插入到 low,使得 a[1..i] 保持有序)。

    • 教学点:强调返回的 low 含义 —— 它指向第一个大于 temp 的位置;如果 temp 最大,则 low == i(即插入到末尾)。

  13. // 此时 low 为插入位置

    • 注释说明(课堂可以在板书写出 low 的几种典型情形)。

  14. a[0] = temp;

    • 把待插入值放入 a[0],作为哨兵(临时存放),这样下面的搬移或比较可以方便引用 a[0]

    • 教学点:说明哨兵的优势(有时可避免边界检查或将 temp 暂存在数组中便于后续写入)。同时说明这里我们仍然先计算 low,搬移循环使用 low 作为边界(保持逻辑清晰)。

  15. j = i - 1;

    • 设置 j 从已排序区的右端开始(准备往左搬移),注意 j 初值为 i-1

  16. while (j >= low) {

  17. a[j + 1] = a[j];

  18. j--;

  19. }

    • 将 a[low..i-1] 中的所有元素整体向右移动一位,为在 a[low] 放置 temp 腾出空间。

    • 终止条件是 j < low(即所有大于等于 low 的元素都已右移)。

    • 教学点:

      • 解释为什么从右向左搬移(避免覆盖尚未搬移的元素)。

      • 虽然使用了哨兵,但这里我们没有用哨兵来代替 low,而是把 low 仍作为终止条件,这能保证稳定性并避免过度依赖哨兵的比较方式。

      • 若不使用 low,而仅基于 while (a[j] > a[0]) 进行移动,逻辑也可成立,但那是“直接插入 + 哨兵”的变体,不再保留折半查找的明确插入位置信息。

  20. a[low] = a[0];

    • 把保存在哨兵 a[0] 的 temp 放到正确位置 low,插入完成。

    • 此时 a[1..i] 变为有序。

  21. }(for 循环结束)

    • 继续处理下一个 i,直到 i == n 为止,排序完成。


【main 中要点(快速讲解)】

  • int a[11] = {0, 12, 2, 16, 30, 28, 10, 16, 20, 6, 18};

    • 使用长度为 11 的数组(0..10),其中 a[0] 初始置 0(将被当作哨兵使用),输入数据放在 a[1]..a[10]

  • BinaryInsertSortWithSentinel(a, n);

    • 调用排序函数。

  • 输出循环从 a[1] 到 a[n] 打印排序结果。

四、期末习题复习

下节课再说

五、力扣题

35、搜索插入位置

https://leetcode.cn/problems/search-insert-position/description/

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值