剑指 Offer II 077. 链表排序(Java实现、理解归并排序)

先看LeetCode上面的题目:

给定链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

示例:

 输入:head = [4,2,1,3]

输出:[1,2,3,4]

思考:

这道题的解决办法有许多,网上都可以找到,那么在这里,我们就来使用 归并排序 把它做一下。

先来了解下什么是 归并排序:

归并排序的一个重要思想就是:分而治之

比如我有一组无序的整形数组,我想使用归并排序给它排个序,首先我会将这个整形数组分割成一个一个小的数组,一直分隔到小数组的元素个数为1为止。

然后从元素个数最少的数组开始将它们合并起来。最终全部合并完成之后,就是一个排序完成的数组。

当然我们这里的题目是对链表操作,但做法是一样的,没什么区别。

大体结构框架:


下面便来解决这道题:

为了方便,我们就在 IDEA 上编写这道题的代码,但为了使代码写完后能直接在 LeetCode 上运行,所用我们就使用 LeetCode 上的排版 。

整体框架:

 当框架搭建完成后,具体步骤如下:

1.先判断是不是空链表。

2.再判断是不是只有一个节点(若只有一个节点,那么不用排了)

3.然后使用快慢指针进行链表的第一次分割。

4.分割好的两个链表又分别调用 sortList(ListNode head)函数,这样使用递归一层一层的往下分割,直至链表节点个数为1。

5.当分割完成之后,就需要进行合并操作,这里直接写一个合并的静态方法,然后return这个方法就行了。

 具体代码:

package com.learn.java.test1;
/**
 * 剑指offer - 077
 * 给定链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
 */
public class Solution {
    /**
     * 合并方法的具体业务逻辑
     * 这里使用递归,代码显得更加简洁,也更容易理解
     * @param l1 第一条链表的头节点
     * @param l2 第二条链表的头节点
     * @return 合并后的链表头节点
     */
    public ListNode merge(ListNode l1,ListNode l2){
        //判断第一条链表为空链表的情况
        if(l1 == null){
            return l2;
        }
        //判断第二条链表为空链表的情况
        if(l2 == null){
            return l1;
        }
        //如果l1直线的节点的val值小于l2指向的val值
        if(l1.val < l2.val){
            //将头节点是l1.next的新链表与头节点为l2的原来的链表进行合并,
            //合并后的链表同样连到l1节点后面
            l1.next = merge(l1.next,l2);
            //返回l1链表的头节点
            return l1;
        }else{
            //反之,将l2节点后面一个节点开始的新链表与原l1链表合并,
            //合并后同样连到l2节点后面
            l2.next = merge(l1,l2.next);
            //返回l2链表的头节点
            return l2;
        }
    }

    public ListNode sortList(ListNode head) {
        //先判断是否是一个空链表
        if(head == null){
            return null;
        //然后判断是否只有一个节点
        }else if(head.next == null){
            return head;
        }else{
            //使用快慢指针进行分割操作
            ListNode slow = head;  //慢指针
            ListNode fast = head;  //快指针
        /*
         这里注意我们在使用while循环时,里面的条件:
         在分割时,我们不仅要拿到两条链表的头节点,还要将原来的链表真正的断开,
         所以我们在拿到第二条链表的头节点时,也要拿到头节点的前一个节点(因为这是一个单链表)
         最后将前一个节点的next置空(这一步很重要)
         故fast指针需要满足两个条件:
         1.fast为尾结点时停下来。
         2.fast的下一个节点为尾结点时也要停下来。
         (这里由于我们提前就判断了,所以不需要判断 fast != null)
         */
            while( fast.next != null && fast.next.next != null){
                fast = fast.next.next;
                slow = slow.next;
            }
            //分割完成,l1和l2两条链表
            ListNode l1 = head;
            ListNode l2 = slow.next;
            //将原本链表的联系给断开
            slow.next = null;
            //将l1和l2又分别进行递归分割
            l1 = sortList(l1);
            l2 = sortList(l2);
            //最后将链表合并起来,返回头节点
            return merge(l1,l2);
        }
    }
}

将代码放到 LeetCode 上进行测试:

 明显测试通过。

如果想看一下代码的执行过程,那么我们也可以使用 IDEA 自己来测试一下:

这里注意想要用IDEA来测试的话,我们需要创建一个单链表。

单链表实体类代码:

package com.learn.java.test1;
/**
* 节点类
*/
class ListNode{
    public final int val;
    public ListNode next;
    public ListNode(int data){
        this.val = data;
    }
}
/**
 * 单链表
 * 这里我们只需要稍微使用一下单链表,所以里面的功能不全
 */
public class MyLinkedList {
    public ListNode head;

    //尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);
        //第一个节点
        if(this.head == null){
            this.head = node;
        }else{
            //第二个节点
            ListNode cur = this.head;
            while(cur.getNext() != null){
                cur = cur.getNext();
            }
            cur.setNext(node);
        }
    }

    //显示链表
    public void display(){
        ListNode cur = this.head;
        while(cur != null){
            System.out.print(cur.getVal()+" ");
            cur = cur.getNext();
        }
        System.out.println();
    }

    //给定头节点节点显示链表
    public void displayMyLinkedList(ListNode cur){
        while(cur != null){
            System.out.print(cur.getVal()+" ");
            cur = cur.getNext();
        }
        System.out.println();
    }
}

然后我们编写一个测试类来进行测试:

package com.learn.java.test1;
/**
* 测试类
*/
public class Test {
    public static void main(String[] args) {
        MyLinkedList m = new MyLinkedList();
        m.addLast(4);
        m.addLast(2);
        m.addLast(1);
        m.addLast(3);
        m.display();
        System.out.println("--------------------");
        Solution s = new Solution();
        ListNode tmp = s.sortList(m.head);
        m.displayMyLinkedList(tmp);
    }
}

运行结果:

 排序成功。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值