链表调整问题

本文探讨了如何根据特定规律调整链表。当链表长度为奇数时,调整为L1R1L2R2...;偶数时,调整为L1R1L2R2...。提供了两种解决方案:一是利用数组辅助,二是使用快慢指针法直接调整。通过这种方法,可以解决链表的复杂调整问题。
摘要由CSDN通过智能技术生成

问题描述:

  • 给定一个链表list,
    如果:
    list = 1 调整之后1。
    list = 1->2 调整之后1->2
    list = 1->2->3 调整之后1->2->3
    list = 1->2->3->4 调整之后1->3->2->4
    list = 1->2->3->4->5 调整之后1->3->2->4->5
    list = 1->2->3->4->5->6 调整之后1->4->2->5->3->6
    list = 1->2->3->4->5->6->7 调整之后1->4->2->5->3->6->7
    根据上面的规律, 调整一个任意长度的链表。

问题分析:

  • 链表调整问题有两大做法
    • 借助于数组等集合,来作第三方调整,然后将调整后的数组串成链表
      该法适用于笔试中,因为不涉及复杂的指针操作,便于想象与实现,有种作弊的嫌疑,但显然面试官并不想要这个答案,
    • 调整指针,无需额外空间,这是用于面试中的答案。
  • 分析题意,可知有两种情况:
    • 当链表长度为奇数时,L1L2L3L4R1R2R3R4 应调整成 L1R1L2R2L3R3L4R4 的形式
    • 当链表长度为偶数时,L1L2L3L4R1R2R3R4R5 应调整成 L1R1L2R2L3R3L4R4R5 的形式
  • 所以对于该题,有两种方法:
    • 方法一:先将整条链表存入数组中,然后根据调整规律,对于 前 n/2个元素,i 索引处的元素应存放在调整后数组的 2*i 处 ,对于后 n/2 个元素,i索引处的元素应存放在调整后数组的 (i-n/2)* 2+1处。而奇数长度的链表最后一个元素直接加在最后即可。
      举例:原数组为{1,2,3,4,5,6},调整后数组应为{1,3,2,4,5,6},每个元素对应的索引由{0,1,2,3,4,5}变成了{0,2,4,1,3,5}
      最后将调整后的数组串成链表
    • 方法二:用快慢指针法找到中间位置,分成左右两个链表,然后合并两个链表。具体合并方法见代码,有详细解释。

经验教训:

* 对于链表问题,两种通用解法的适用场景
* 方法一中调整数组也可不用额外数组,这便是**完美洗牌**问题
* 方法二链表的具体调整方式

代码实现

  • 方法一:
public static void relocate(Node head) {
        if (head == null || head.next == null) {
            return;
        }
        ArrayList<Integer> list = new ArrayList<>();
        Node curNode = head;
        while (curNode != null) {
            list.add(curNode.value);
            curNode = curNode.next;
        }
        int[] num = new int[list.size()];
        if ((list.size() & 1) == 0) {
            for (int i = 0; i < list.size() / 2; i++) {
                num[2 * i] = list.get(i);
            }
            for (int i = list.size() / 2; i < list.size(); i++) {
                num[(i - list.size() / 2) * 2 + 1] = list.get(i);
            }
        }else {
            for (int i = 0; i < list.size() / 2; i++) {
                num[2 * i] = list.get(i);
            }
            for (int i = list.size() / 2; i < list.size() - 1; i++) {
                num[(i - list.size() / 2) * 2 + 1] = list.get(i);
            }
            num[list.size() - 1] = list.get(list.size() - 1);
        }
        head.value = num[0];
        Node cur = head;
        for (int i = 1; i < num.length; i++) {
            Node newNode = new Node(num[i]);
            cur.next = newNode;
            cur = newNode;
        }
    }
  • 方法二:
public static void relocate(Node head) {
        if (head == null || head.next == null) {
            return;
        }
        //定义快慢指针找到中间位置
        // 1 -> 2 -> 3 -> 4,最终mid停在2处
        // 1 -> 2 -> 3 -> 4 -> 5,最终mid依旧停在2处
        Node slow = head;
        Node fast = head.next;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        Node mid = slow;
        //分成左右两个链,如 左链是  1 -> 2,右链是3 -> 4 -> 5 
        Node rHead = slow.next;
        mid.next = null;
        Node lHead = head;
        //一个一个合并两链,最终形成 1 -> 3 -> 2 -> 4 -> 5 
        mergeList(lHead, rHead);
    }

    //合并两链函数,注意:两链不是空链表,至少一个元素(适用于 两链长度关系什么都行的情况,该题中左链 <= 右链)
    /*
     *  1 2 3
     *  4 5 6 7
     *  连接顺序为 4连2,1连4,这是一次循环,然后5连3,2连5,这又是一次循环
     *  然后退出循环,3连6
     */
    public static void mergeList(Node lHead, Node rHead) {
        //起初lCur指向1,rCur指向4
        Node lCur = lHead;
        Node rCur = rHead;
        while (lCur.next != null) {//例如上栗,当lCur指向3时便跳出循环
            //rNext指向5
            Node rNext = rCur.next;
            //4连2
            rCur.next = lCur.next;
            //1连4
            lCur.next = rCur;
            //lCur指向2,迭代
            lCur = rCur.next;
            //rCur指向5,迭代
            rCur = rNext;
        }
        lCur.next = rCur;

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值