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
相关推荐
九河云10 小时前
华为云 GaussDB:金融级高可用数据库,为核心业务保驾护航
网络·数据库·科技·金融·华为云·gaussdb
独行soc10 小时前
2025年渗透测试面试题总结-66(题目+回答)
java·网络·python·安全·web安全·adb·渗透测试
码农101号11 小时前
运维安全05 - iptables规则保存与恢复
运维·网络·安全
范纹杉想快点毕业11 小时前
ZYNQ PS 端 UART 接收数据数据帧(初学者友好版)嵌入式编程 C语言 c++ 软件开发
c语言·笔记·stm32·单片机·嵌入式硬件·mcu·51单片机
_OP_CHEN12 小时前
数据结构(C语言篇):(十二)实现顺序结构二叉树——堆
c语言·数据结构·算法·二叉树·学习笔记··顺序结构二叉树
sukalot13 小时前
window显示驱动开发—视频呈现网络的术语
网络
杨福瑞13 小时前
C语⾔内存函数
c语言·开发语言
千鼎数字孪生-可视化14 小时前
WebSocket实时推送技术:PLC状态监控大屏的高效实现路径
网络·websocket·网络协议
李白你好14 小时前
这是一个用于扫描Zabbix系统中常见漏洞的工具,提供了图形界面(GUI)和命令行(CLI)两种使用方式。
网络·安全·zabbix
蒋星熠14 小时前
破壁者指南:内网穿透技术的深度解构与实战方法
网络·数据库·redis·python·websocket·网络协议·udp