每日算法题【链表】:链表分割、链表的回文结构

(11)链表分割
  • [链表分割_牛客题霸_牛客网](https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking)\]:

    • 一个原链表,两个新链表
    • 遍历原链表,为了防止因为维护原链表(保留比x大的节点并维持顺序)而进行复杂操作,我们直接选择创建2个新链表进行大节点和小节点的维护
    • 比x大的我们尾插到大链表,比x小的我们尾插到小链表

    注意:

    1. 正确初始化尾指针:在第一次添加节点时,同时设置头指针和尾指针
    2. 断开原链表连接:在将节点添加到新链表前,先保存下一个节点,然后断开当前节点的连接,避免形成循环链表
    3. 处理空链表情况:如果小链表为空,直接返回大链表
    4. 统一处理方式:对大小链表的处理采用相同的模式,提高代码一致性
    c++ 复制代码
    /*
    struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) : val(x), next(NULL) {}
    };*/
    
    class Partition {
    public:
        ListNode* partition(ListNode* pHead, int x) {
            // 定义两个新的空链表(头节点和尾节点)
            ListNode* BigList = nullptr;
            ListNode* BigCur = nullptr;
            ListNode* SmallList = nullptr;
            ListNode* SmallCur = nullptr;
    
            // 定义一个指针遍历原链表
            ListNode* cur = pHead;
    
            if(pHead == nullptr) {
                return nullptr;
            }
    
            while(cur) {
                ListNode* next = cur->next; // 保存下一个节点
                cur->next = nullptr; // 断开当前节点的连接
    
                if(cur->val < x) {
                    // 将节点尾插到小链表中
                    if(SmallList == nullptr) {
                        SmallList = cur;
                        SmallCur = cur;
                    } else {
                        SmallCur->next = cur;
                        SmallCur = SmallCur->next;
                    }
                } else {
                    // 将节点尾插到大链表中
                    if(BigList == nullptr) {
                        BigList = cur;
                        BigCur = cur;
                    } else {
                        BigCur->next = cur;
                        BigCur = BigCur->next;
                    }
                }
                cur = next;
            }
    
            // 如果小链表为空,直接返回大链表
            if(SmallList == nullptr) {
                return BigList;
            }
    
            // 将大链表连接到小链表尾部
            SmallCur->next = BigList;
    
            return SmallList;
        }
    };
  • 第二种解法:

    用指针遍历原链表,小于x的不做处理,大于等于x的尾插到原链表上,并删除之前的节点

    1. 找到链表原始尾部
    2. 遍历链表,遇到≥x的节点就移动到尾部
    3. 如果是头节点,更新head指针
    4. 更新tail指针指向新的尾部
    5. 继续处理直到完成
    c++ 复制代码
    /*
    struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) : val(x), next(NULL) {}
    };*/
    
    class Partition {
    public:
        ListNode* partition(ListNode* pHead, int x) {
            if (pHead == nullptr) return nullptr;
    
            // 找到链表尾部
            ListNode* tail = pHead;
            while (tail->next != nullptr) {
                tail = tail->next;
            }
    
            ListNode* head = pHead;
            ListNode* prev = nullptr;
            ListNode* cur = head;
            ListNode* end = tail;  // 记录原始尾部,避免重复移动
    
            while (cur != end && cur != nullptr) {
                if (cur->val >= x) {
                    // 移动到尾部
                    if (prev == nullptr) {
                        // 头节点需要移动
                        head = cur->next;
                        tail->next = cur;
                        cur->next = nullptr;
                        tail = cur;
                        cur = head;
                    } else {
                        prev->next = cur->next;
                        tail->next = cur;
                        cur->next = nullptr;
                        tail = cur;
                        cur = prev->next;
                    }
                } else {
                    prev = cur;
                    cur = cur->next;
                }
            }
    
            // 处理最后一个节点(原始尾部)
            if (cur != nullptr && cur->val >= x && cur != tail) {
                if (prev == nullptr) {
                    head = cur->next;
                } else {
                    prev->next = cur->next;
                }
                tail->next = cur;
                cur->next = nullptr;
            }
    
            return head;
        }
    };

    关键处理点:

    1. 头节点移动的特殊处理:当需要移动头节点时,需要更新head指针
    2. prev指针的处理:prev为nullptr表示当前节点是头节点
    3. 尾部指针维护:每次移动节点后更新tail指针
    4. 循环终止条件:避免重复移动已经移动到尾部的节点

(12)链表的回文结构
  • [链表的回文结构_牛客题霸_牛客网](https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking)\]:

    算法思路:

    1. 找到链表中点:使用快慢指针法,快指针每次走两步,慢指针每次走一步,当快指针到达末尾时,慢指针正好在中间位置。
    2. 反转后半部分链表:将链表的后半部分反转,这样就可以从两端向中间比较。
    3. 比较前后两部分:从链表头部和反转后的后半部分头部开始,逐个节点比较值是否相等。
    4. 恢复链表(可选):如果需要保持原链表结构不变,可以将反转的后半部分再次反转恢复原状。

    时间复杂度: O(n) - 遍历链表3次(找中点、反转、比较)
    空间复杂度: O(1) - 只使用了固定数量的指针变量

    c++ 复制代码
    /*
    struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) : val(x), next(NULL) {}
    };*/
    
    
    class PalindromeList {
    public:
        bool chkPalindrome(ListNode* A) {
            //如果原链表是空链表或者只有一个节点的链表默认就是回文结构
            if(A == nullptr && A->next == nullptr){return true;}
    
            //第一步:使用快慢指针找到链表的中间节点
            ListNode* fast = A;
            ListNode* slow = A;
            //因为fast一次走两步,所以当fast走到最后只会是指向最后一个节点(单数节点)或者指向最后一个节点的next(双数节点)
            //所以循环的结束条件fast != nullptr && fast->next != nullptr
            while(fast != nullptr && fast->next != nullptr){
                slow = slow->next;
                fast = fast->next->next;
            }
    
            //第二步:将找到的中间节点地址传给翻转链表函数(三个指针)进行翻转
            ListNode* newList = reverseList(slow);
    
            //第三步:通过两个链表的头指针进行遍历比较来判断是否是回文结构
            ListNode* Old = A;
            ListNode* New = newList;
            while(Old != nullptr && New != nullptr){
                if(Old->val != New->val){
                    return false;
                }
                Old = Old->next;
                New = New->next;
            }
            return true;
    
        }
    
    private:
        struct ListNode* reverseList(struct ListNode* head) {
            //如果传入的链表头指针head为空,直接返回NULL,因为空链表不需要反转
            if(head == nullptr){
                return nullptr;
            }
    
            struct ListNode* n1,*n2,*n3;
            n1 = nullptr;
            n2 = head;
            n3 = head->next;
    
            while(n2){
                //翻转
                n2->next = n1;
                //迭代往后走
                n1 = n2;
                n2 = n3;
                if(n3){
                    n3 = n3->next;
                }
            }
            return n1;
        }
    };