C语言链表分区问题

C语言链表分区问题

在链表相关的问题中,分区问题是一个典型的算法场景。本文将以 partition 函数为例,讲解如何实现将链表分为两部分,其中一部分的节点值小于指定值 x x x,另一部分的节点值大于或等于 x x x。


问题描述

给定一个单链表和一个值 x x x,我们需要重新排列链表,使得:

  • 所有小于 x x x 的节点位于链表前部分;
  • 所有大于或等于 x x x 的节点位于链表后部分;
  • 两部分中的节点相对顺序保持不变。

例如:

输入链表为:
1 → 4 → 3 → 2 → 5 → 2 1 \rightarrow 4 \rightarrow 3 \rightarrow 2 \rightarrow 5 \rightarrow 2 1→4→3→2→5→2

分区值为 x = 3 x=3 x=3,则输出为:
1 → 2 → 2 → 4 → 3 → 5 1 \rightarrow 2 \rightarrow 2 \rightarrow 4 \rightarrow 3 \rightarrow 5 1→2→2→4→3→5


分析与实现

核心思路

  1. 使用两个虚拟链表:
    • 一个链表 small 保存所有小于 x x x 的节点;
    • 一个链表 big 保存所有大于或等于 x x x 的节点。
  2. 遍历原链表:
    • 对每个节点,根据其值将其添加到 smallbig 链表中。
  3. 拼接链表:
    • small 链表的尾部与 big 链表的头部相连。
  4. 返回结果:
    • 返回新的链表头。

代码实现

以下是具体代码实现:

c 复制代码
#include <stdio.h>
#include <stdlib.h>

struct ListNode {
    int val;
    struct ListNode* next;
};

struct ListNode* partition(struct ListNode* head, int x) {
    // 创建两个虚拟头节点
    struct ListNode* small = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* big = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* temp_small = small;
    struct ListNode* temp_big = big;

    // 初始化虚拟链表
    small->next = NULL;
    big->next = NULL;

    // 遍历原链表
    while (head != NULL) {
        if (head->val < x) {
            // 创建新节点并添加到 small 链表
            temp_small->next = (struct ListNode*)malloc(sizeof(struct ListNode));
            temp_small = temp_small->next;
            temp_small->val = head->val;
        } else {
            // 创建新节点并添加到 big 链表
            temp_big->next = (struct ListNode*)malloc(sizeof(struct ListNode));
            temp_big = temp_big->next;
            temp_big->val = head->val;
        }
        head = head->next;
    }

    // 结束 big 链表
    temp_big->next = NULL;

    // 拼接 small 和 big 链表
    temp_small->next = big->next;

    // 释放 big 的头节点
    free(big);

    // 返回新链表的头节点
    struct ListNode* result = small->next;
    free(small);
    return result;
}

详细解释

虚拟头节点的作用

虚拟头节点的引入使得链表操作更为简洁,避免了单独处理链表头的复杂逻辑。以下是两个虚拟头节点的初始化:

c 复制代码
struct ListNode* small = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* big = (struct ListNode*)malloc(sizeof(struct ListNode));
small->next = NULL;
big->next = NULL;

虚拟头节点使得我们可以从任意位置开始构建链表,而无需额外判断是否是第一个节点。


遍历原链表

通过遍历原链表,将节点分配到对应的 smallbig 链表中:

c 复制代码
while (head != NULL) {
    if (head->val < x) {
        temp_small->next = (struct ListNode*)malloc(sizeof(struct ListNode));
        temp_small = temp_small->next;
        temp_small->val = head->val;
    } else {
        temp_big->next = (struct ListNode*)malloc(sizeof(struct ListNode));
        temp_big = temp_big->next;
        temp_big->val = head->val;
    }
    head = head->next;
}

在每次分配时,记得使用 malloc 创建新的节点,否则会破坏链表的结构。


拼接链表

完成遍历后,将 smallbig 两部分链表拼接起来:

c 复制代码
temp_big->next = NULL;      // 确保 big 链表以 NULL 结尾
temp_small->next = big->next; // 拼接 small 和 big 链表
free(big);                  // 释放 big 的虚拟头节点

注意在拼接之前,我们需要确保 big 链表以 NULL 结尾。


内存管理

在使用动态内存分配时,要注意避免内存泄漏。本文代码在最后释放了 smallbig 的虚拟头节点:

c 复制代码
struct ListNode* result = small->next; // 获取结果链表
free(small);                          // 释放虚拟头节点
return result;

时间与空间复杂度

  1. 时间复杂度:

    遍历原链表一次,时间复杂度为 O ( n ) O(n) O(n)。

  2. 空间复杂度:

    额外分配了两个链表(smallbig),每个节点仅分配一次,空间复杂度为 O ( n ) O(n) O(n)。


测试用例

以下是测试用例及其输出结果:

输入链表

plaintext 复制代码
1 -> 4 -> 3 -> 2 -> 5 -> 2

分区值

plaintext 复制代码
x = 3

输出链表

plaintext 复制代码
1 -> 2 -> 2 -> 4 -> 3 -> 5
相关推荐
九河云2 分钟前
TOS + 数字孪生:集装箱码头的智能进化密码
大数据·服务器·网络·数据库·数字化转型
MonkeyKing_sunyuhua13 分钟前
python线程间怎么通信
android·网络·python
沐浴露z21 分钟前
一篇文章讲清 UPD协议 与 TCP协议
网络·网络协议·tcp/ip·计算机网络
kyle~28 分钟前
计算机系统---CPU的进程与线程处理
linux·服务器·c语言·c++·操作系统·计算机系统
云飞云共享云桌面38 分钟前
广东某模具制造工厂用一台云服务器供8个研发设计同时用
linux·运维·服务器·网络·自动化·制造
电鱼智能的电小鱼40 分钟前
服装制造企业痛点解决方案:EFISH-SBC-RK3588 预测性维护方案
网络·人工智能·嵌入式硬件·算法·制造
小此方1 小时前
C语言自定义变量类型结构体理论:从初见到精通(下)
c语言·数据结构·算法
IPIDEA全球IP代理2 小时前
跨境卖家该选静态IP还是动态IP?
网络·网络协议·tcp/ip
东风西巷2 小时前
MyLanViewer(局域网IP扫描软件)
前端·网络·网络协议·tcp/ip·电脑·软件需求
Yupureki2 小时前
从零开始的C++学习生活 7:vector的入门使用
c语言·c++·学习·visual studio