跳转至

AB12-在带头结点的递增有序单链表中插入新结点,保持有序性

代码实现

C
/**
 * 在带头结点的递增有序单链表中插入新结点,保持有序性
 * @param L 指向链表头结点的指针
 * @param x 要插入的元素值
 */
void InsertNode(LinkList L, int x) {
    // 申请新结点空间并初始化
    LNode* p = (LNode*)malloc(sizeof(LNode));
    if (p == NULL) {
        printf("内存分配失败\n");
        return;
    }
    p->data = x;
    p->next = NULL;

    // 查找插入位置:r指向插入位置的前驱结点
    LNode* r = L;  // 从头结点开始查找

    // 遍历链表,找到第一个大于x的结点的前驱位置
    while (r->next != NULL) {
        // 如果下一个结点的值大于x,则找到插入位置
        if (r->next->data > x) {
            break;  // 找到插入位置,跳出循环
        }
        r = r->next;  // 继续向后查找
    }

    // 在r指向的位置后插入新结点p
    p->next = r->next;  // 新结点指向原r的后继
    r->next = p;        // r指向新结点
}

算法执行流程图解

查找插入位置过程:

Text Only
1
2
3
4
5
6
7
8
有序链表:L→[ ]→[1]→[3]→[7]→[9]→NULL  插入x=5

查找过程:
r=L: r->next=[1], 1>5? 否 → r=[1]
r=[1]: r->next=[3], 3>5? 否 → r=[3]  
r=[3]: r->next=[7], 7>5? 是 → 找到插入位置,跳出

插入位置:r指向[3],在[3]和[7]之间插入5

插入操作过程:

Text Only
1
2
3
4
5
6
7
8
9
插入前:L→[ ]→[1]→[3]→[7]→[9]→NULL
               ↑     ↑
               r   r->next

插入操作:
1. p->next = r->next;  → p→[5]→[7]→[9]→NULL
2. r->next = p;        → [3]→[5]→[7]→[9]→NULL

插入后:L→[ ]→[1]→[3]→[5]→[7]→[9]→NULL

完整执行示例

Text Only
原链表:L→[ ]→[2]→[4]→[6]→[8]→NULL

插入x=1:
查找:r=L, r->next=[2], 2>1 → 找到位置
插入:L→[ ]→[1]→[2]→[4]→[6]→[8]→NULL

插入x=5:
查找:r=L→[2]→[4], r->next=[6], 6>5 → 找到位置
插入:L→[ ]→[1]→[2]→[4]→[5]→[6]→[8]→NULL

插入x=10:
查找:r=L→[1]→[2]→[4]→[5]→[6]→[8], r->next=NULL
插入:L→[ ]→[1]→[2]→[4]→[5]→[6]→[8]→[10]→NULL

复杂度分析

时间复杂度:O(n)

  • 最好情况:O(1) - 插入到链表开头
  • 最坏情况:O(n) - 插入到链表末尾
  • 平均情况:O(n) - 平均需要查找n/2个结点
  • 总体时间复杂度O(n)

空间复杂度:O(1)

  • 额外空间:只使用了常数个指针变量:pr
  • 结点空间:虽然创建了一个新结点,但这属于问题本身要求
  • 算法额外空间复杂度O(1)

进阶-递归版本实现

C
/**
 * 递归版本:在有序链表中插入元素
 */
void InsertNode_Recursive(LinkList L, int x) {
    if (L == NULL) return;

    // 创建新结点
    LinkList p = (LinkList)malloc(sizeof(LNode));
    if (p == NULL) return;
    p->data = x;

    // 递归查找插入位置
    LinkList pos = findInsertPos(L, x);
    p->next = pos->next;
    pos->next = p;
}

// 辅助函数:查找插入位置
LinkList findInsertPos(LinkList head, int x) {
    if (head->next == NULL || head->next->data > x) {
        return head;
    }
    return findInsertPos(head->next, x);
}

算法特点

核心思想: 利用链表的有序性,找到第一个大于待插入元素值的结点的前驱位置,然后执行标准的插入操作。

关键步骤: 1. 创建结点:分配新结点空间并设置数据 2. 查找位置:遍历链表找到合适的插入位置 3. 执行插入:在指定位置插入新结点

重要性质: - 保持链表的有序性 - 插入位置唯一确定 - 时间复杂度与链表长度成正比

与顺序表插入的对比

特性 顺序表 链表
查找时间 O(n) O(n)
插入时间 O(n) O(1)
总体时间 O(n) O(n)
空间复杂度 O(1) O(1)

链表的优势在于插入操作本身是O(1)的,但查找插入位置仍需O(n)时间。