【分割链表】

本文介绍了一种分割链表的方法,使所有小于x的节点出现在大于或等于x的节点之前。探讨了两种主要策略:值交换和重构链表,并提供了详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在这里插入图片描述
大家好,今天我们来了解一下leetcode中比较简单的单链表问题。

一、题目描述

题目描述如下: 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,
使得所有 小于 x 的节点都出现在 大于或等于 x
的节点之前。

注意:你不需要 保留 每个分区中各节点的初始相对位置。

在这里插入图片描述

点击跳转:分割链表


二、算法思想

(一)值交换

1、题目解析

本题要求我们将链表中的数值进行重构,大于x的值都在小于x的值之前。
我们之前学习数组的时候使用过双指针的解法,一个front指针从前往后遍历,找到一个大于x的值就停下来,之后让back指针从后往前遍历,遇到小于x的值就和front中的值进行交换,之后front指针继续遍历,直到两指针相遇为止。
这里题目没有限制不可以开辟额外空间,那么我们就可以将链表中的数据放到数组中,交换结束后在拷贝回链表中。
此时的时间复杂度为O(n),空间复杂度为O(n)。

动画演示如下:

双指针-- 数组

虽然借用数组来进行交换非常方便,
但是由于开辟数组这种方法空间复杂度较高,在很多情况下会受到限制,所以
这里我们不使用这种方法,不过可以使用双指针的思想:
设置两个指针,一个指针从头开始遍历,找到了数据大于x的节点后就让另一个节点从此处开始遍历,找到一个数据小于x的节点,之后交换两节点的数据。

动画演示如下:

双指针--链表

2、代码实现

代码如下:


typedef struct ListNode LN;

LN* partition(LN* head, int x) {
    if(!head || !head->next)
    {
        return head;
    }

   LN*less=head;
   LN*grea=head;
   while(less)
   {
       if(less->val>=x)
       {
           grea=less->next;
           while(grea)
           {
               if(grea->val<x)
               {
                   int tmp=grea->val;
                   grea->val=less->val;
                   less->val=tmp;
                   break;
               }
               grea=grea->next;
           }
       }
       if(!grea)
       break;
           less=less->next;
   }
   return head;
}

运行实例:
在这里插入图片描述
时间复杂度O(n),空间复杂度O(1),时间复杂度虽然看起来是O(n),但如果遇到前面都是大于x的数,后面都是小于x的数,
那么指针grea就需要遍历n/2次,时间复杂度近似与O(n^2)了已经,复杂度也很高的。


(二)重构链表

1、题目解析

上面我们交换的是节点中的数据,可是一个节点里不仅有数据域还有指针域,
那么我们能否通过改变指针的指向来改变链表呢?
下面让我们来尝试一下:
1.我们可以直接在原链表中进行更改;
2.创建一个新的链表,记录下小于x的最后一个节点和大于x的最后一个节点,之后分别进行尾插;
3.我们也可以创建两个新的链表,小于x节点连到一起,大于x的节点连到一起,
之后再将两个链表进行连接就可以了。(我们使用这种)

动画演示如下:

重构链表

2、代码实现

示例:


typedef struct ListNode LN;

LN* partition(LN* pHead, int x) {
	if (!pHead)
		return pHead;

	LN* lessHead = (LN*)malloc(sizeof(LN)); // 小于x的新链表
	lessHead->next = NULL;
	LN* greaHead = (LN*)malloc(sizeof(LN)); // 大于等于x的新链表
	greaHead->next = NULL;

	LN* pless = lessHead;
	LN* pgrea = greaHead;
	LN* cur = pHead;

	while (cur)
	{
		if (cur->val < x)
		{
			pless->next = cur;
			pless = pless->next;
		}
		else
		{
			pgrea->next = cur;
			pgrea = pgrea->next;
		}

		cur = cur->next;
	}

	pgrea->next = NULL; // 最后一个节点的next置空
	pless->next = greaHead->next; // 链接
	pHead = lessHead->next;

	free(lessHead);
	free(greaHead);

	return pHead;
}

运行实例:
在这里插入图片描述
此方法的时间复杂度为O(n),空间复杂度为O(1),并且不论是哪一种情况都只会遍历一次。
视频中只演示了既存在大于x的值也存在小于x的值的情况,
这里我们还需要注意:
所有值都大于x,和所以值都小于x这两种特殊情况,如果感兴趣的话大家之后可以自行画图测试。


总结

以上就是今天分割链表题目的全部内容,题目不难,也不难讲。
不过如果总结一下我们会发现:
这简简单单的一道题我们一共就说了5种方法,而且我想大家肯定也会有自己的一些特殊的想法的,所以
我们不能仅限于解决问题,一个简单的题目我们可以通过各种各样的方法来解决,
我们还需要考虑使用什么方法可以使问题解决更加优雅、更加高效。
如果有什么疑问或者建议都可以在评论区留言,感谢大家对在这里插入图片描述的支持。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值