算法题学习---链表的奇偶重排

174 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

题目

描述

给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。

注意是节点的编号而非节点的数值。

数据范围:节点数量满足0≤n≤10^5,节点中的值都满足0≤val≤1000

要求:空间复杂度 O(n),时间复杂度 O(n)

示例1

输入:
{1,2,3,4,5,6}
复制
返回值:
{1,3,5,2,4,6}
复制
说明:
1->2->3->4->5->6->NULL
重排后为
1->3->5->2->4->6->NULL

示例2

输入:
{1,4,6,3,7}
复制
返回值:
{1,6,7,4,3}
复制
说明:
1->4->6->3->7->NULL
重排后为
1->6->7->4->3->NULL
奇数位节点有1,6,7,偶数位节点有4,3。重排后为1,6,7,4,3

备注:

链表长度不大于200000。每个数范围均在int内。

分析

分析特殊情况

由题意,我们容易得出如下的结论:

  • 如果链表为空,直接返回头指针即可;
  • 如果链表只有一个元素或只有两个元素,我们也无需排序,直接返回头指针即可。

分析一般情况

一种比较容易想到的思路如下:

在开始处理时,我们知道链表头指向的是第一个奇数链的末尾,接下来我们每次遍历两个元素,我们只需要将下一个奇数位置的元素插入到这个奇数链末尾即可,然后更新新的奇数链末尾,等待下一次插入,等到处理结束,从表头到奇数链末尾就是所有奇数位置的元素,奇数链末尾到链表末尾就是所有偶数链的元素。

使用了pOddEnd和pNextOdd。

另一种双指针法:

第一个节点是奇数位,第二个节点是偶数,第二个节点后又是奇数位,因此可以断掉节点1和节点2之间的连接,指向节点2的后面即节点3,如红色箭头。如果此时我们将第一个节点指向第三个节点,就可以得到那么第三个节点后为偶数节点,因此我们又可以断掉节点2到节点3之间的连接,指向节点3后一个节点即节点4,如蓝色箭头。那么我们再将第二个节点指向第四个节点,又回到刚刚到情况了。

  • step 1:判断空链表的情况,如果链表为空,不用重排。
  • step 2:使用双指针odd和even分别遍历奇数节点和偶数节点,并给偶数节点链表一个头。
  • step 3:上述过程,每次遍历两个节点,且even在后面,因此每轮循环用even检查后两个元素是否为NULL,如果不为再进入循环进行上述连接过程。
  • step 4:将偶数节点头接在奇数最后一个节点后,再返回头部。

代码示例

示例一:

pOddEnd初始化为头节点,pCur为当次的NextOdd,找到当前的pNextOdd,然后将pCur插入到pOddEnd处,使用pNextOdd更新pCur,直到pCur为空,即到达链表末尾。

/**
 * struct ListNode {
 *        int val;
 *        struct ListNode *next;
 *        ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* oddEvenList(ListNode* head) {
        // write code here
        if(nullptr == head || nullptr == head->next || nullptr == head->next->next){
            return head;
        }

        ListNode* pOddEnd = head;
        ListNode* pPre = head->next;
        ListNode* pCur = pPre->next;
        ListNode* pNextOdd = nullptr;

        while(nullptr != pCur){
            // update the pPre and pNextOdd
            pPre->next = pCur->next;
            pPre = pPre->next;
            if(nullptr != pPre){
                pNextOdd = pPre->next;
            }else{
                pNextOdd = nullptr;
            }
            // insert thr pCur to Odd list rear
            pCur->next = pOddEnd->next;
            pOddEnd->next = pCur;
            pOddEnd = pOddEnd->next;

            pCur = pNextOdd;
        }

        return head;
    }
};

示例二:

如上面解答中所说,这里需要多注意一点,要预先保存一下偶数链的头节点head2,之后需要将奇数链的末尾连接过来。

/**
 * struct ListNode {
 *        int val;
 *        struct ListNode *next;
 *        ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* oddEvenList(ListNode* head) {
        // write code here
        if(nullptr == head || nullptr == head->next || nullptr == head->next->next){
            return head;
        }

        ListNode* pOdd = head;
        ListNode* pEven = pOdd->next;
        ListNode* head2 = pEven;

        while(nullptr != pEven && nullptr != pEven->next){
            pOdd->next = pEven->next;
            pOdd = pOdd->next;
            pEven->next = pOdd->next;
            pEven = pEven->next;
        }
        pOdd->next = head2;

        return head;
    }
};