算法题-NC2 重排链表(Java实现)

描述
将给定的单链表 L\ L L: L0→L1→…→Ln−1→LnL_0→L_1→…→L_{n-1}→L_ nL0​→L1​→…→Ln−1​→Ln​
重新排序为:L0→Ln→L1→Ln−1→L2→Ln−2→…L_0→L_n →L_1→L_{n-1}→L_2→L_{n-2}→…L0​→Ln​→L1​→Ln−1​→L2​→Ln−2​→…
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。

方法一

解法1:创建一个集合 简单粗暴

package Test;

/*
    @CreateTime 2021/9/26 11:05
    @CreateBy cfk
    重排链表
    
    参考:https://blog.nowcoder.net/n/6eeeca0a37344b1dbb5a9ac82381812e
*/

import java.util.ArrayList;
import java.util.List;

public class Demo01A {
    public static void main(String[] args) {
        Demo01A demo01A = new Demo01A();
        ListNodeA listNode1 = new ListNodeA(1);
        ListNodeA listNode2 = new ListNodeA(2);
        ListNodeA listNode3 = new ListNodeA(3);
        ListNodeA listNode4 = new ListNodeA(4);
        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4;
        demo01A.reorderList(listNode1);

    }

    public void reorderList(ListNodeA head) {
        if (head == null) return;

        List<ListNodeA> list = new ArrayList<>(); //创建一个数组存储结点方便操作

        while (head != null) {
            list.add(head);
            head = head.next;  //遍历所有的结点 存储在数组中
        }

        int i = 0;
        int j = list.size()-1;

        //两个指针 一个从头开始 一个从尾开始遍历
        while (i < j) {
            list.get(i).next = list.get(j);
            i++;
            if (i == j) break; //当两个指针指向同一个位置 说明元素已经全部遍历完了 (这一步可以省略 没必要)
            list.get(j).next = list.get(i);
            j--;
        }

        //把最后一个结点指向null 否则它会指向4
        //因为前面 它会再赋值一次 然后退出循环
        list.get(i).next = null;

        head = list.get(0);


    }


}


class ListNodeA {
    int val;
    ListNodeA next;
    ListNodeA(int x) {
        val = x;
        next = null;
    }

    @Override
    public String toString() {
        return "ListNodeA{" +
                "val=" + val +
                '}';
    }
}

方法二

解法2: 分链表分成两半, 左边从小到大,右边从大到小 然后再依次读取组成新的链表

package Test;

/*
    @CreateTime 2021/9/26 11:05
    @CreateBy cfk
    重排链表
   
    参考:https://blog.nowcoder.net/n/6eeeca0a37344b1dbb5a9ac82381812e
*/



public class Demo01B {
    public static void main(String[] args) {
        Demo01B demo01B = new Demo01B();
        ListNodeB listNode1 = new ListNodeB(1);
        ListNodeB listNode2 = new ListNodeB(2);
        ListNodeB listNode3 = new ListNodeB(3);
        ListNodeB listNode4 = new ListNodeB(4);
        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4;
        demo01B.reorderList(listNode1);

    }


    public void reorderList(ListNodeB head) {
        //如果遇到没有元素或者只有俩个元素程序直接返回
        if (head == null || head.next ==null) {
            return;
        }

        //否则把链表分成两半,左边从小到大 右边从大到小
        //设置两个指针一个一次移动一位 一个一次移动两位
        //这样当快的指针移动到最后时 通过慢指针的位置确定链表的中间值
        ListNodeB slow = head;
        ListNodeB fast = head.next;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNodeB mid = slow.next; //把链表分为左右两份 左边比右边大1
        slow.next = null;

        System.out.println("没有反转的链表="+head);
        ListNodeB newHead = reverseList(mid);//把后半截链表反转


        //====================重点 方法一:合并两个数组================
        //此方法比较好理解

       /* ListNodeB temp = new ListNodeB(0); //创建一个第三方数组 最后把数据保存在这里面
        ListNodeB tail = temp;
        boolean flag = true;
        //合并两个链表
        while (head != null && newHead != null) {
            if (flag) {
                tail.next = head;
                head = head.next;
                flag = false;
            }else {
                tail.next = newHead;
                newHead = newHead.next;
                flag = true;
            }
            tail = tail.next;

        }
        //把head中剩下的全部元素添加到链表
        while (head != null) {
            tail.next = head;
            head = head.next;
            tail = tail.next;
        }

        //把newHead中剩下的全部元素添加到链表
        while (newHead != null) {
            tail.next = newHead;
            newHead = newHead.next;
            tail = tail.next;
        }


        System.out.println(temp);*/


        //===================重点==================



        //===================重点方法二==================
        ListNodeB tmp = head; //保存头结点 否则后面找不到了
        while(newHead != null){
            ListNodeB temp = newHead.next; //保存右边数据的下一位数据
            newHead.next = tmp.next; //让右边链表第一位指向左边链表第二位
            tmp.next = newHead; //让左边链表第一位指向右边链表第一位
            tmp = newHead.next; //把指针指向下一位
            newHead = temp; //指针向后移动
        }
        // System.out.println(head);

        //===================重点==================



    }


    //反转链表
    public static ListNodeB reverseList(ListNodeB head) {
        if (head.next == null) {
            return head;
        }//如果链表只有一个元素直接返回链接即可

        ListNodeB tail = head;  //创建一个尾节点保存原链表头节点

        head = head.next;  //让头指针后移一位

        tail.next = null;  //把后面的链表置空

        while (head != null) {
            ListNodeB tmp = head.next; //创建一个临时变量存储头结点的下一个节点 避免在操作时被删除了
            head.next = tail; //让头结点下一位指向尾结点
            tail = head; //把尾结点作为头结点
            head = tmp; //最后重新定位头结点
        }
        System.out.println("被反转的链表为="+tail);

        return tail;
    }


}


class ListNodeB {
    int val;
    ListNodeB next;
    ListNodeB(int x) {
        val = x;
        next = null;
    }

    @Override
    public String toString() {
        return "ListNodeB{" +
                "val=" + val +
                ", next=" + next +
                '}';
    }
}

注意:希望能看到本文的人, 都能耐心看懂,本人算法也是小白菜,在刚开始看这道题的时候,我做题思路是创建两个新的链表,先把旧链表反转保存在一个新的链表中,然后再读一个原链表一个反转链表这样执行,最后直到两个链表的元素相等停止。自己琢磨了很久也没能实现,就去看网上别人写的答案理解。

最后硬着头发看了很久才能理解,对于这些指针使用,感觉挺陌生了,但是等你真正理解了,你就会发现不过如此,不要照搬代码,搬运别人的代码一定要加上自己的理解,学习的人加油啊!!!感谢您的阅读!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据目要求,我们需要将给定的单链表重新排列为Ln→L1→Ln−1→L2→⋯的形式。可以使用两个数组来实现这个目标。首先,我们可以遍历链表,将链表中的每个节点的地址按顺序存储到一个数组中。接下来,我们可以利用这个数组来构建我们需要的排列后的链表。具体步骤如下: 1. 定义一个结构体,用于存储链表节点的数值和下一个节点的地址。 2. 遍历链表,将每个节点的地址按顺序存储到一个数组中。 3. 定义一个新的数组,利用第一个数组中的节点地址来构建排列后的链表。具体方法是,按照Ln→L1→Ln−1→L2→⋯的顺序,依次将第一个数组中的节点地址赋值给新数组中的每个元素。 4. 按照排列后的链表顺序,依次输出新数组中的节点数值即可。 总结一下,我们可以按照上述方法实现目要求的重排链表的功能。引用提供了一个类似的思路,可以作为参考。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [L2-022 重排链表 【STL】](https://blog.csdn.net/wangws_sb/article/details/88644986)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [L2-022 重排链表](https://blog.csdn.net/weixin_45897672/article/details/104703349)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值