【C/C++】剑指 Offer II 029. 排序的循环链表

199 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情


题目链接:剑指 Offer II 029. 排序的循环链表

题目描述

给定 循环单调非递减 列表中的一个点,写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。

给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。

如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。

如果列表为空(给定的节点是 null),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。

提示:

  • 0NumberofNodes51040 \leqslant Number of Nodes \leqslant 5 * 10^4
  • 106Node.val106-10^6 \leqslant Node.val \leqslant 10^6
  • 106 insertVal106-10^6 \leqslant insertVal \leqslant 10^6

示例 1:

example_1_before_65p.jpg

输入:head = [3,4,1], insertVal = 2
输出:[3,4,1,2]
解释:在上图中,有一个包含三个元素的循环有序列表,你获得值为 3 的节点的指针,我们需要向表中插入元素 2 。新插入的节点应该在 1 和 3 之间,插入之后,整个列表如上图所示,最后返回节点 3 。

example_1_after_65p.jpg

示例 2:

输入: head = [], insertVal = 1
输出: [1]
解释: 列表为空(给定的节点是 null),创建一个循环有序列表并返回这个节点。

示例 3:

输入: head = [1], insertVal = 0
输出: [1,0]

整理题意

题目给定 循环单调非递减 链表中的一个点,要求在该链表中插入一个新元素,新元素值为 insertVal ,需要使得插入后的链表任然是 循环单调非递减链表

  • 这里需要注意题目中描述有不当的地方,“升序”和“非递减”是不同的。
  • 该链表为循环链表,也就是首尾是相互连接的。

解题思路分析

  • 由于题目给定的是 循环单调非递减链表,说明链表中可能存在相同元素;
  • 由于给定的点并非一定为链表的头节点,所以我们需要通过遍历找到该链表首尾相连的地方,由于是非递减链表,所以只需要找到最后一个最大的元素即为链表的尾节点,而尾节点的下一个节点即为头节点;
  • 对边界和特殊情况进行判断和处理;
  • 然后遍历链表,将元素 insertVal 插入即可,即链表的插入操作。

需要注意边界和特殊情况的判断和处理,包括空链表、链表中只有一个元素、链表中存在相同元素等情况的判断和处理。

具体实现

  1. 首先判断给定节点是否为空,如果链表为空,创建一个节点,值为 insertVal ,并让该节点指向自己,返回该节点。
  2. 如果链表不为空,遍历链表找到链表中的尾节点,也就是找到了链表首尾相连的地方。

在找尾节点时,需要注意链表中是否只存在一个节点,当循环再次回到题目给定的节点时,说明已经遍历完一遍链表了。

  1. 判断插入的元素是否插在首尾相连处,有两种情况插在首尾相连处:
    • 插入值比头节点还要小;
    • 插入值比尾节点还要大;
  2. 如果并非在首尾相连处插入,就遍历链表,找到一个当前节点小于等于当前插入值,当前节点的下一个节点大于等于插入值的位置,将其插入即可。

复杂度分析

  • 时间复杂度:O(n)O(n),其中 n 是链表的节点数。需要遍历链表一次寻找插入节点的位置,插入节点的时间是 O(1)O(1)
  • 空间复杂度:O(1)O(1)

代码实现

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;

    Node() {}

    Node(int _val) {
        val = _val;
        next = NULL;
    }

    Node(int _val, Node* _next) {
        val = _val;
        next = _next;
    }
};
*/

class Solution {
public:
    Node* insert(Node* head, int insertVal) {
        if(head == NULL){
            head = new Node(insertVal);
            head->next = head;
            return head;
        }
        Node* res = head;
        Node* last;
        //找到尾节点
        while(1){
            //如果链表只有一个元素,或者链表中节点值都相同
            if(head->next == res){
                last = head;
                break;
            }
            //当前节点值大于下一个节点值
            if(head->val > head->next->val){
                last = head;
                break;
            }
            head = head->next;
        }
        //内置函数,插入操作
        auto op = [&](Node* temp){
            Node* now = new Node(insertVal);
            now->next = temp->next;
            temp->next = now;
        };
        //特判插入值大于尾节点值或小于首节点值
        if(insertVal <= last->next->val || insertVal >= last->val){
            op(last);
            return res;
        }
        //不在链表首尾连接处插入,在链表当中插入
        else{
            while(1){
                //当前节点小于等于当前插入值,下一个节点大于等于插入值
                if(head->val <= insertVal && head->next->val >= insertVal){
                    op(head);
                    return res;
                }
                head = head->next;
            }
        }
    }
};

总结

  • 该题考察链表的插入操作,具体操作为先更新插入节点的下一个链接的节点 next,再将原链表断开,这样可以避免丢失后面链表的信息,所以需要先记录即将断开处的下一个节点。
  • 同时需要注意特殊情况以及边界情况的处理,当链表中只有一个元素或者没有元素时的特殊判断和处理,以及链表中存在多个相同元素时的处理。
  • 测试结果:

微信截图_20220620222336.png

结束语

你所做的事情,也许暂时看不到结果,但请不要灰心,你并不是没有成长,而是正在扎根。从来没有一蹴而就的成功,只有日复一日的坚持。愿你对每件热爱的事物都能沉淀执着,全力以赴又满载而归。