leetcode学习记录_链表


链表

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

来源:力扣(LeetCode)
这题思路很简单,实现也很简单

第一种:遍历
遍历链表取得长度size,然后在让指针从头,循环size-k次就好

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* p = head;
        int size = 0;
        while(p != nullptr)
        {
        	size++;
        	p = p->next;
        }
        if(size < k) return nullptr;
        p = head;
        for(int i = 0;i < size - k;i++)
        {
        	p = p->next;
        }
		return p;
    }
};

花费2n - k;

第二种:快慢指针法
让快指针fast始终比慢指针slow快k步,当fast到达尾部,slow自然就在size - k处

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* fast = head , *slow = head;
        int i = 0;
        while(i != k && fast != nullptr)
        {
            fast = fast->next;
            i++;
        }
        while(fast != nullptr)
        {
            fast = fast->next;
            slow = slow->next;
        }
       return slow;
    }
};

时间花费 n ;


剑指 Offer 24. 反转链表

迭代:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
		ListNode* cur = head , *pre = nullptr;
		while(cur)
		{
			ListNode* next = cur->next;;
			cur->next = pre;
			pre = cur;
			cur = next;
		}
		return pre;
    }
};

递归

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
		if(head == nullptr || head->next == nullptr)
		return head;
		ListNode* res = reverseList(head->next);
		head->next->next = head;
		head->next = nullptr
		return head;
    }
};

92. 反转链表 II

这一题虽然本质上还是反转链表,但是是局部反转,思路由下
在这里插入图片描述

代码:

class Solution {
public:
    void reverselist(ListNode* head)//反转链表的程序
    {
        ListNode* curr = head , *prev = nullptr;
		while(curr)
		{	
			ListNode* next = curr->next;
			curr->next = prev;
			prev = curr;
			curr = next;
		}
    }
    ListNode* reverseBetween(ListNode* head, int left, int right) 
    {
        ListNode* newhead = new ListNode(0);//定义头结点
        newhead->next = head;//让头结点指向链表
        ListNode* rt , *lt , *R ,*L = newhead;
        for(int i = 0;i<left-1;i++)//移动L到被反转区间之前
        {
        	L = L->next;
        }
        lt = L->next;//让lt移动到反转区间的开头
        rt = lt;
        for(int i = 0;i<right-left;i++)//让rt移动到反转区间的末尾
        {
        	rt = rt->next;
        }
        R = rt->next;//移动R到反转区间的后面
        L->next = nullptr;//截断
        rt->next = nullptr;
        reverselist(lt);
        L->next = rt;//链接
		lt->next = R;
		return newhead->next;
    }
};

25. K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
在这里插入图片描述

来源:力扣(LeetCode)

思路是一样的,切断,反转,重连,不过会有些差别,因为上一题只要求切断一次,这题要切 lenght / k次

思路:
在这里插入图片描述
特殊情况:

特殊情况 1 :lenght 刚好被 k 整除 ,这种情况下我们用R来判断
特殊情况 2 :有剩余,用end来判断

代码:

class Solution {
public:
//这个反转链表的程序,只改了输入参数和返回值而已,其他没改
    pair<ListNode*, ListNode*> reverse(ListNode*head, ListNode*end)
    {
        ListNode*cur = head, *pre = nullptr;
        while(cur)
        {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return {end,head};
    }
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* newhead = new ListNode(-1);
        newhead->next = head;
        ListNode* L = newhead, *R = head;
        
        while(R)//解决特殊情况 1 
        {
            ListNode* end = head;//让end定位到head
            for(int i = 1;i<k;++i)
            {//然后用循环让end到合适的位置
                end = end->next;
//如果end为nullptr,就说明最后一段长度不够,只能返回了                
                if(!end) return newhead->next;//解决特殊情况 12
            }
            R = end->next;//移动R到end的后边
            L->next = nullptr;//切断
            end->next = nullptr;//同上
            tie(head,end) = reverse(head,end);//反转并更新head和end
            L->next = head;end->next = R;//接回去
            head = R;//
            L = end;//
        }
        return newhead->next;
    }
};


从头到尾打印链表

剑指 Offer 06. 从尾到头打印链表

用reverse反转数组

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> res;
        ListNode* p = head;
        while(p!=nullptr)
        {
            res.push_back(p->val);
            p = p->next;
        }
        reserve(res.begin(),res.end());
        return res;
    }
};

朴素法(纯算法,不用辅助函数)
先遍历链表求得长度,然后重新分配数组的空间,当然也可以不用resize,可以直接vector< int > res(size , 0);
但是不能用vector::reserve,因为reserve不改变size,只是预分配空间而已,后面还需要用下标赋值,如果用reserve的画程序就会崩溃,所以直接初始化或者用resize

改变size后就用下标从后面赋值就完事了;

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> res;
        ListNode* p = head;
        int size;
        while(p!=nullptr)
        {
            size++;
            p = p->next;
        }
        res.resize(size);
        p = head;
        for(;size>0;size--)
        {
        	res[size-1] = p->val;
        	p = p->next;
        }
        return res;
    }
};

改变链表结构
这种也很好理解,就是上面那题的做法,反转链表以后再按顺序添加元素进数组

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> res;
        ListNode* before = head , after = nullptr;
        while(before!=nullptr)
        {
           head = before->next;
           before->next = after;
           after = before;
           before = head;
        }
        while(after != nullptr)
        {
        	res.emplace_back(after->val);
        	after = after->next;
        }
        return res;
    }
};


剑指 Offer 25. 合并两个排序的链表

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
 		ListNode *res = new ListNode(0);//定义一个新的头结点
 		ListNode * p = res;
 //如果有一个是空指针的话,在下面的值比较的过程程序就会崩溃
 		while(l1 && l1)
 		{
 			if(l1->val < l2->val)
 			{
 				p->next = l1;
 				p = p->next;
 				l1 = l1->next;
 			}
 			if(l2->val < l1->val)
 			{
 				p->next = l2;
 				p = p->next;
 				l2 = l2->next;
 			}
 		}
//这里的if就是为了填满最后一个空缺,看最后面的两个,谁被漏了
 		if(l1 != nullptr) p->next = l1;
 		else p->next = l2;
 		return res->next;
    }
};


剑指 Offer 52. 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

思路是双指针,一个从A链开始,一个从B链开始,一直往前走,当指针A到达A链的末尾的时候,让指针A指向B的头结点,反之对指针B也是一样的操作

我一开始也是用的双指针,但是在遍历完成后没有交换到对面去,结果运行了400多毫秒在这里插入图片描述
给我吓傻了😱,还是第一次看见运行这么久的题目,赶紧去看看大佬们的题解,才发现自己的问题,如果不交换,虽然也能出结果,但是这样两个指针相遇的问题就变成了求最小公倍数的问题了,交换以后呢,就变成了加法的问题,
假设链A不公共的结点数为N,链表B不公共的结点数为M,两者公共的结点数为X,交换以后,两个指针走过的步数都是N+M+K,明显比公倍数好多了,改进后结果为
在这里插入图片描述
可以看到,差距很明显

ps:过了半个月再回来做这题,居然做不出来了,按照记忆中的思路,发现会当量链表五交点时,会死循环,然后再次研究了一下,发现是条件设置的问题,两链表不相交的话,我们可以把它们当成再最后的nullptr处相交,反正走的路径一样长,有交点的话两者就会相交,没有的话,两者还是保持了一样的步伐,直到他们都为nullptr

所以

我们必须得让两个指针有能成为nullptr的机会,我们在while的条件里面比较两指针是否相同,所以,两指针必须能以nullptr的身份到达while循环的末尾,这样才能在下一次循环开始前判断两指针是否都为nullptr

具体写法:

if(A==nullptr)  A = headB;
else		    A = A->next;

B指针也照葫芦画瓢

也可以这么写

if(A!=nullptr)  A = A->next;
else            A = headB;

反正就是两步都得有条件约束着,假如有一个没有,就会出错

比如下面的(错误)写法:

A = A->next;
if(A == nullptr) A = headB;

这么写的话,就算A指向nullptr,也会因为下面的if而指向headB,最后的结果就是死循环
代码:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *A = headA , *B = headB;
            while(A != B)
            {
                A = A != nullptr? A->next : headB;//记得交换位置
                B = B != nullptr? B->next : headA; 
            }
        return A;
    }
};


2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

来源:力扣(LeetCode)

在这里插入图片描述
此题联动:
题目
文章

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
		int carry = 0;
		ListNode* res = new ListNode(0);//头结点
		ListNode* p = res;
		while(l1 || l2)//为了能让短的能够补长
		{
//精髓所在,把短的链表后面补0,好与长的链表相加		
			int n1 = l1?l1->val:0;
			int n2 = l2?l2->val:0;
			sum = n1 + n1 + carry;
			p->next = new ListNode(sum%10);//保留个位
			p = p->next;
			carry = sum/10;//保留进位
//防止无效地址			
			if(l1) l1 = l1->next;
			if(l2) l2 = l2->next;
		}
//最后一位别忘了		
		if(carry > 0) p->next = new ListNode(carry);
		return res->next;
    }
};


141. 环形链表

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?
来源:力扣(LeetCode)

既然有进阶,那我们肯定要达到进阶的标准啦,要求空间使用度O(1)那就不能用哈希表了,老老实实快慢指针,快慢指针就怕越界,指向未知的内存,导致程序崩溃,因此得好好考虑一下,这里我用了两个判断,一个是while(fast),还有一个是while里面的 if (!fast) 有两个条件保护,就不会越界了

class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* slow = head , *fast = head;
		while(fast)//第一层保护
		{
			fast = fast->next;
			if(!fast) return false;//第二层
			fast = fast->next;
			slow = slow-<next;
			if(slow == fast) return true;//找到了就直接返回
		}
//如果跳出了循环,那只有可能是fast == nullptr的情况了,所以返回false		
		return false;
    }
};

其实我早就看过快慢指针的原理,但一直没看过代码,也没写这题,这次是用自己的思路实现的,也没看大佬的代码,发现效率还挺高,挺高兴的哈哈
在这里插入图片描述
差不多的另一题:
142. 环形链表 II

这一题要返回环的第一个结点,如果没有环就返回null
这题思路也一样,用快慢指针判断是不是有环,然后呢,如果有环,那快慢指针肯定相遇了,这时候完美让另一个指针从头开始,与快(慢也一样)指针一起往后走,最终他们两就会相遇在环的开头
具体数学分析见 👉这里

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode*slow = head , *fast = head , *res = head;
        while(fast)
        {
            fast = fast->next;
            if(!fast) return nullptr;
            fast = fast->next;
            slow = slow->next;
            if(slow == fast) break;//这里改成break;
        }
//这里可能是break跳出的,也有可能是不满足while跳出的,
//所以if一下,看是不是有环链表        
        if(!fast) return nullptr;
        while(res!=fast)
        {
            res = res->next;
            fast = fast->next;
        }
        return res;
    }
};


143. 重排链表

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

来源:力扣(LeetCode)

思路:

在这里插入图片描述

class Solution {
public:
    void reverselist(ListNode* head)//反转程序
    {
        ListNode* pre = nullptr , *cur = head;
        while(cur)
        {
            ListNode*next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
    }
    void reorderList(ListNode* head) {
        int N = 0 , left = 0 , right = 0;
        ListNode* size = head , *L = head , *rt = head , *lt = nullptr, *R = nullptr;
        while(size->next)//获得长度,并且保留size在最后一位的地方
        {
            N++;
            size = size->next;
        }
        N++;
        if(N == 2 || N == 1) return ;//长度为1 或2的话就没必要反转
        if(N%2!=0)//求得中点的范围,奇数,偶数的情况要分开处理
        {
            right = left = N/2+1;
        }
        else
        {
            left = N/2;
            right = N/2+1;
        }
        while(left>2)//移动L指针到中点范围左边
        {
            L = L->next;
            left--;
        }
        lt = L->next;//让lt指向中点开头
        while(right>1)
        {//让rt指向中点结尾
            rt = rt->next;
            right--;
        }
        R = rt->next;//让R指向要反转的子链表的开头
        L->next = nullptr;
        rt->next = nullptr;
        reverselist(R);//反转
        //这里pre2是反转后的链表的开头,size的存在就为了这里
        ListNode* pre1 = head , *pre2 = size;
     //这里的条件用啥都行,与,或,或者只用pre1和pre2也可以   
        while(pre1 && pre2)
        {//合并
        ListNode*cur1 = pre1->next , *cur2 = pre2->next;
        pre1->next = pre2;
        pre2->next = cur1;
        pre1 = cur1;
        pre2 = cur2;
        }
        ListNode* buf = head;
        while(buf->next)
        {//找到合并和的链表的尽头
            buf = buf->next;
        }
        buf->next = lt;//链接
    }
};


83. 删除排序链表中的重复元素

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。

返回同样按升序排列的结果链表。
思路很简单,和数组的一样,双指针(pre , cur)遍历,遇见两指针对应值数字不相同的话,就把pre连接搭配cur,如果相同, 就不管他,让cur继续往前走,直到遇到不重复的

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* newhead = new ListNode(-10);
        newhead->next = head;
        ListNode* pre = newhead, *cur = pre->next;
        while(cur)
        {
            if(pre->val != cur->val)
            {
                pre->next = cur;
                pre = cur;
            } 
            cur = cur->next;
        }
//这一步比较特殊,如果最后的那几个数字重复了,
//没有这步的话,就起不到删除的作用了                
        pre->next = cur;
        return newhead->next;
    }
};

82. 删除排序链表中的重复元素 II

自己的常规思路,第一次遍历,用哈希表记录重复的的值,
第二次,如果值在哈希表里能找到,就跳过,如果找不到就让pre连接到cur

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        unordered_set<int> mset;
        ListNode* newhead = new ListNode(-100);
        newhead->next = head;
        ListNode* pre = newhead, *cur = head, *buf = newhead;
        while(buf->next)
        {
            if(buf->val == buf->next->val)
            mset.insert(buf->val);//记录重复的值到哈希表里
            buf = buf->next;
        }
        while(cur)
        {
            if(mset.find(cur->val) == mset.end())
            {
            pre->next = cur;
            pre = cur;
            }
            cur = cur->next;  
        }
        pre->next = cur;//这一步和上面那一题是一样的
        return newhead->next;
    }


147. 对链表进行插入排序

插入排序大家都熟,链表版的思路也差不多,用start和end两个指针维护已排序的部分,然后cur指针每次从end后面开始判断该结点的值应该插入到什么地方,当然别忘了删除多余的cur
不过这里是链表,不用删除,改变指向就好
分三个情况:

1:cur的值小于start,这种情况就直接插在最前面

cur < start

2:cur的值大于等于end,插在最后面

cur >= end

3:cur的值大于或等于start,并小于end,就遍历已排序序列,找到合适的位置插入

start  <= cur < end

注意,为了保持插入排序的稳定性,必须这样设置条件,其实其它的条件设置也是完全没问题的,只是不保证稳定性而已

代码:

class Solution {
public:
    ListNode* insertionSortList(ListNode* head) {
        if(!head || !head->next) return head;
        ListNode* newhead = new ListNode(-1);
        newhead->next = head;
        ListNode* pend = head, *pstart = head;
        ListNode* cur = pend->next;
        while(cur)
        {
            if(cur->val < pstart->val)//小于start就插在最前面,并更新start
            {
                ListNode* temp = cur->next;
                pend->next = temp;
                newhead->next = cur;
                cur->next = pstart;
                pstart = cur;
            }
            else if(cur->val >= pend->val)//大于end的就不用管,因为本身cur就在end后面
            //直接更新end的位置就好
            {
                pend = cur;
            }
            else
            {//其余情况就要找位置插入了
                pend->next = cur->next;
                ListNode* temp = pstart;
                while(temp->next->val<=cur->val)
                {
                    temp = temp->next;
                }
                ListNode* temp2 = temp->next;
                temp->next = cur;
                cur->next = temp2;
            }
            cur = pend->next;
        }
        pend->next = nullptr;
        return newhead->next;
    }
};


23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

先说说最容易想到的
合并两个有序链表很简单,如果我们不追求最高效率,那么写出这题也很简单,直接遍历数组中的链表,先将第一个和第二个合并,然后再将第三个合并进去…依次类推

代码:()

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0) return nullptr;
        ListNode* newhead = new ListNode(-1);
        newhead->next = lists[0];
        for(int i = 1;i<lists.size();++i)
        {//让头结点始终指向被合并的新链表
           newhead->next = Merge(lists[i], newhead->next);
        }
        return newhead->next;
    }

这是合并两个链表的程序,需要在上面调用

ListNode* Merge(ListNode*head1, ListNode*head2)
    {
        if(!head1 || !head2) return head1?head1:head2;
        ListNode* newhead = new ListNode(-1);
        ListNode* cur = newhead, *p1 = head1, *p2 = head2;
        while(head1 && head2)
        {
            p1 = head1->next;p2 = head2->next;
            if(head1->val < head2->val)
            {
                cur->next = head1; head1 = p1;
            }
            else
            {
                cur->next = head2; head2 = p2;
            }   
            cur = cur->next;
        }
        cur->next = head1?head1:head2;
        return newhead->next;
    }

然后就是效率较高的分而治之,类似归并排序,可以说是非常像,但是由于给出的是数组形式的链表,所以写起来怪怪的,但是总体思路和归并排序差不多

在这里插入图片描述
这里的注意一下,再递归的进行分割的时候,使用数组的下标,就像数组的归并排序里的分割一样
但是
合并的时候就不能用数组下标了

为什么呢?想一想在数组的归并排序时,我们把排过序的数组重新填回原数组了,所以用下标合并时合并的就是两个有序的序列,但是在这个链表排序里,我们写的合并的程序没有这一步,所以合并的时候不能用数组下标了,但是我们合并的时候返回了新链表的结点,所以直接用这两个返回的结点继续进行合并就行,当然如果你不信邪,硬要在合并程序里填回原数组,应该也是可行的,但是没必要

代码:

class Solution {
    ListNode* Merge(ListNode*head1, ListNode*head2)
    {
        if(!head1 || !head2) return head1?head1:head2;
        ListNode* newhead = new ListNode(-1);
        ListNode* cur = newhead, *p1 = head1, *p2 = head2;
        while(head1 && head2){
            p1 = head1->next;p2 = head2->next;
            if(head1->val < head2->val){
                cur->next = head1; head1 = p1;
            }
            else{
                cur->next = head2; head2 = p2;
            }   
            cur = cur->next;
        }
        cur->next = head1?head1:head2;
        ListNode* res = newhead->next;
        delete newhead;
        return res;
    }
    ListNode* MSort(vector<ListNode*>& lists, int start, int end)
    {
        if(start == end) return lists[end];//如果要合并的链表数量为一,那就直接返回原链表
        else if(start > end) return nullptr;
        else
        {
            int mid = start + ((end-start)>>1);      
            //这里在合并时别用下标,别用下标,别用下标
            return Merge(MSort(lists,start,mid), MSort(lists,mid+1,end));
        }
    }
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0) return nullptr;
        ListNode* newhead = new ListNode(-1);
        int start = 0, end = lists.size()-1;
        newhead->next = MSort(lists, start,end);
        ListNode*res = newhead->next;
        delete newhead;
        return res;
    }
};

61. 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。在这里插入图片描述

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

这题思路很简单,没啥难度:

找到链表的尾部并同时计算链表长度,然后就是改变头结点而已了,将头节点移动x位,
然后就有了一个以新头结点开始的环形链表,再以size为依据,找到尾部,断开环,就OK了

,唯一需要思考一下的地方就是x的计算,x是由size和k决定的,我们可以直到,旋转一次,其实就是将当前链表的为结点作为新的头结点而已,而且如果k大于了size,那么旋转k次和旋转k%size次没有区别
因为我们从头结点开始移动来确定新的头结点,于是得出

x = size-(k%size)

最后注意一下特殊情况:

k为0,链表长度为0或1
k为0直接返回head,长度为1也直接返回head,长度为0返回nullptr,但是因为此时head也是nullptr,所以三种情况就统一了,所以这三种情况可以直接在开头判断

x = size
一个环形链表,如果头结点移动size次的话,并不是到达末尾,而是返回原位,所以直接返回head,这个需要size,所以不能在开头判断

代码:

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (k == 0 || head == nullptr || head->next == nullptr)  return head;//特殊情况
        ListNode*cur = head; int size = 1;
        while(cur->next)
        {//找到末尾并求出size
            cur = cur->next;
            ++size;
        }
        k = (size)-(k%size);
        if(k == size)return head;//特殊情况
        cur->next = head;//让链表成环
        for(int i = 0;i<k;++i)
        {//移动到新的头结点处
            head = head->next;
        }
        ListNode*end = head;
        for(int i = 1;i<size;++i)
        {//找到当前的末尾
            end = end->next;
        }
        end->next = nullptr;//断开环
        return head;
    }
};


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

你应当 保留 两个分区中每个节点的初始相对位置。

来源:力扣(LeetCode)
在这里插入图片描述

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

思路:
很简单,分割链表再组合就行了,如果想着在原链表上操作,那就太难了

简单来说就是,用cur遍历当前链表,发现cur->val小于x,就接到新链表1后面,大于或等于x,就接到新链表2后面

就这么简单,只需注意一点

记得已经被分配过的结点,记得切断它与后续结点的链接

不过,这一题其实也可以直接用原链表头 + 一个新链表头完成了,不过我懒了,直接两个新链表思路简单,实现也简单,就不纠结了

代码:

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(!head)return nullptr;//特殊情况
        ListNode* head1 = new ListNode(-999);
        ListNode* head2 = new ListNode(-999);
        ListNode* p1 = head1, *p2 = head2;
        ListNode*cur = head;
        while(cur)
        {
            if(cur->val < x)
            {
                p1->next = cur;
                p1 = p1->next;
            }
            else
            {
                p2->next = cur;
                p2 = p2->next;
            } 
            //这里别忘了,已经分配过的结点得切断它与后续得链接,不然只会碍事
            ListNode*temp = cur;
            cur = cur->next;
            temp->next = nullptr;//切断链接
        }
        p1->next = head2->next;//连上两个链表
        return head1->next;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timathy33

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值