跳转至

AB16-删除带头结点单链表中唯一的最小值结点

代码实现

C
/**
 * 删除带头结点单链表中唯一的最小值结点
 * @param L 指向链表头结点的指针
 */
void del_min(LinkList L) {
    // 边界条件检查:空链表或只有一个头结点
    if (L == NULL || L->next == NULL) {
        return;  // 无需删除
    }

    // p指向当前结点的前驱结点(从头结点开始)
    LNode p = L;

    // 指向最小值结点的前驱结点
    LNode pre_min = L;

    // 遍历链表查找最小值结点
    while (p->next != NULL) {
        // 如果当前结点的值小于已知最小值
        if (p->next->data < pre_min->next->data) {
            pre_min = p;  // 更新最小值结点的前驱
        }
        p = p->next;  // 继续向后查找
    }

    // 找到最小值结点u
    LNode temp = pre_min->next;

    // 删除操作:绕过最小值结点
    pre_min->next = temp->next;  // 前驱结点指向最小值结点的后继

    // 释放最小值结点的内存
    free(temp);
}

算法执行流程图解

查找最小值过程:

Text Only
链表:L→[ ]→[5]→[2]→[8]→[3]→[7]→NULL

初始化:
p=L, pos=L
p->next=[5], pos->next=[5]

查找过程:
1. p=[5]: p->next=[2], 2<5? 是 → pos=[5]
   p=[2]: p->next=[8], 8<2? 否
   p=[8]: p->next=[3], 3<2? 否
   p=[3]: p->next=[7], 7<2? 否
   p=[7]: p->next=NULL, 结束

最终:pos指向[5],最小值结点[2]是pos->next

删除操作图解:

Text Only
删除前:
L→[ ]→[5]→[2]→[8]→[3]→[7]→NULL
     ↑    ↑    ↑
    pos   u   u->next

删除操作:
1. pos->next = u->next
   L→[ ]→[5]→[8]→[3]→[7]→NULL
     ↑         ↑
    pos      u->next

2. free(u)
   L→[ ]→[5]→[8]→[3]→[7]→NULL

复杂度分析

时间复杂度:O(n)

  • 查找时间:需要遍历整个链表一次,O(n)
  • 删除时间:常数时间O(1)
  • 总体时间复杂度O(n)

空间复杂度:O(1)

  • 额外空间:只使用了常数个指针变量:pposu
  • 无辅助空间:没有使用额外的数据结构
  • 算法额外空间复杂度O(1)

优化版本1:更清晰的逻辑和变量命名

C
/**
 * 优化版本1:更清晰的逻辑和变量命名
 */
void del_min_Optimized1(LinkList L) {
    // 边界检查
    if (L == NULL || L->next == NULL) {
        return;
    }

    // current指向当前结点的前驱
    LinkList current = L;

    // min_prev指向最小值结点的前驱
    LinkList min_prev = L;

    // 遍历链表查找最小值结点
    while (current->next != NULL) {
        if (current->next->data < min_prev->next->data) {
            min_prev = current;
        }
        current = current->next;
    }

    // 执行删除
    LinkList min_node = min_prev->next;
    min_prev->next = min_node->next;
    free(min_node);
}

优化版本2:返回被删除的最小值

C
/**
 * 优化版本2:返回被删除的最小值
 */
int del_min_with_return(LinkList L) {
    if (L == NULL || L->next == NULL) {
        return INT_MIN;  // 返回错误值
    }

    LinkList p = L;
    LinkList pos = L;

    while (p->next != NULL) {
        if (p->next->data < pos->next->data) {
            pos = p;
        }
        p = p->next;
    }

    LinkList u = pos->next;
    int min_value = u->data;
    pos->next = u->next;
    free(u);

    return min_value;
}

算法特点

核心思想: 1. 单次遍历:通过一次遍历找到最小值结点 2. 前驱指针:使用前驱指针技术,便于删除操作 3. 原地删除:直接修改指针连接,不使用额外空间

关键技巧: - 双指针技术p用于遍历,pos记录最小值前驱 - 比较策略:比较p->next->datapos->next->data - 删除时机:遍历结束后立即删除

算法正确性证明: 1. 初始化posp都从头结点开始 2. 循环不变式:在每次循环开始时,pos->next始终指向当前已知的最小值结点 3. 终止条件:当p->next == NULL时,已遍历完整个链表 4. 结果正确pos->next指向全局最小值结点

易错点分析: 1. 初始条件pos必须初始化为L,而不是L->next 2. 比较对象:比较的是p->next->data而不是p->data 3. 指针更新:只有找到更小值时才更新pos

这种算法效率很高,只需要一次遍历就能完成查找和删除操作,是链表操作中的经典算法。