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
相关推荐
Hacker_LaoYi7 分钟前
TCP/IP协议图--TCP/IP基础
网络·网络协议·tcp/ip
运维自动化&云计算10 分钟前
华为交换机与锐捷交换机端口链路聚合的配置
服务器·网络·华为
羊村懒哥11 分钟前
linux-安全-iptables防火墙基础笔记
linux·网络·安全
星原飞火24 分钟前
2-2-18-13 QNX系统架构之原生网络(Qnet)
网络·车载系统·系统架构·qnx·blackberry·qnet
CodeGrindstone34 分钟前
Muduo网络库剖析 --- 架构设计
网络·c++·网络协议·tcp/ip
linnux领域1 小时前
使用ensp搭建内外互通,使用路由跨不同vlan通信。
网络
夏子曦1 小时前
网络——Socket与WebSocket
网络·websocket·网络协议
一只小灿灿1 小时前
Java 网络编程从入门到精通
java·开发语言·网络
IPFoxy6663 小时前
动态代理如何加强安全性
大数据·服务器·开发语言·网络·网络协议·tcp/ip·php