【124期】字节一面面试官问:Java 如何实现链表中归并排序?

点击上方“Java精选”,选择“设为星标”

别问别人为什么,多问自己凭什么!

下方留言必回,有问必答!

每天 08:00 更新文章,每天进步一点点...

这是一道字节跳动实习生面试时的面试题,首选先说说什么是归并排序。

归并排序可以说是链表排序中最佳的选择,能够保证最好和最坏时间复杂度都是nlogn,而且在数组排序中广受开发人反感的空间复杂度在链表排序中也从O(n)降到了O(1)。

归并排序的一般分为四步:

1)将需要排序数组(链表)取中点并一分为二,拆分为两个链表:head和second两个子链表;

2)使用递归方式对左半部分子链表进行归并排序;

3)使用递归方式对右半部分子链表进行归并排序;

4)将两个半部分子链表进行合并(merge),得到结果。

首先用快慢指针(快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针恰好在链表中点)的方法找到链表中间节点,然后使用递归对两个子链表排序,把两个排好序的子链表合并成一条有序的链表。

程序代码,为了方便输出查看结果,使用Gson包,不需要可以去除相关代码:

import com.google.gson.Gson;
 
class ListNode {
    int val;
    ListNode next;
 
    ListNode(int x) {
        val = x;
        next = null;
    }
}
 
public class LinkedInsertSort {
 
    /**
     * @author 公众号:Java精选
     * @param args
     */
    public static void main(String[] args) {
        ListNode node = new ListNode(4);
        node.next = new ListNode(3);
        LinkedInsertSort lss = new LinkedInsertSort();
        System.out.println(new Gson().toJson(lss.mergeSort(node)));
    }
 
    public ListNode mergeSort(ListNode head) {
        if (head == null || head.next == null)
            return head;
 
        ListNode mid = getMid(head);// 获取链表中间节点
 
        // 把链表从之间拆分为两个链表:head和second两个子链表
        ListNode second = mid.next;
        mid.next = null;
 
        // 对两个子链表排序
        ListNode left = mergeSort(head);
        ListNode right = mergeSort(second);
 
        return merge(right, left);
    }
 
    // 两个有序链表的归并
    private ListNode merge(ListNode l1, ListNode l2) {
        // 辅助节点,排好序的节点将会链接到dummy后面
        ListNode dummy = new ListNode(0);
 
        ListNode tail = dummy;// tail指向最后一个排好序的节点
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                tail.next = l1;
                l1 = l1.next;
            } else {
                tail.next = l2;
                l2 = l2.next;
            }
            tail = tail.next; // 移动tail指针
        }
 
        if (l1 != null)
            tail.next = l1;
        else
            tail.next = l2;
 
        return dummy.next;
 
    }
 
    // 返回链表之间节点
    private ListNode getMid(ListNode head) {
        if (head == null || head.next == null)
            return head;
 
        ListNode slow = head;
        ListNode faster = head.next;
        while (faster != null && faster.next != null) {
            slow = slow.next;
            faster = faster.next.next;
        }
        return slow;
    }
}

输出结果,更多面试微信搜索小程序“Java精选面试题”,或者扫描下方二维码,回复Java面试,内涵3000+道初级、中级、高级面试题,大厂面试题,架构面试题。

{"val":3,"next":{"val":4}}

精品资料,超赞福利!

>>Java精选面试题<< - 小程序,3000+ 道面试题在线刷,最新、最全 Java 面试题!

c923bf8584fdd10b72669534e19aecc0.png

期往精选  点击标题可跳转

【116期】面试官问:谈谈 Spring Cloud 与 Dubbo 有什么区别?

【117期】推荐 2021 下半年常见 15 道 ConcurrentHashMap 面试题!

【118期】淘宝二面:说一说二维码登录的原理?我懵了。。。

【119期】面试官问:了解过 JDK8 中常量池吗?说说运行时的常量池!

【120期】阿里大佬开源 easyexcel,史上最全实现 Excel 导入导出!

【121期】面试官问:线程池执行过程中遇到异常会发生什么,如何处理?

【122期】如何画出一张优秀的架构图(老鸟必备)

【123期】字节三面:toString()、String.valueOf、String 强转,有啥区别?

文章有帮助的话,在看,转发吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值