148. 排序链表

题目:

148. 排序链表
在这里插入图片描述

题解:

归并排序的基本思想是:
找到链表的middle节点,然后递归对前半部分和后半部分分别进行归并排序,最后对两个以排好序的链表进行Merge。

1. 题解一:归并排序(递归法)

21. 合并两个有序链表
876. 链表的中间结点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 题解二:归并排序(从底至顶直接合并)

在这里插入图片描述

current = dummy.next;
tail = dummy;
for (step = 1; step < length; step *= 2) {
	while (current) {
		// left->@->@->@->@->@->@->null
		left = current;

		// left->@->@->null   right->@->@->@->@->null
		right = cut(current, step); // 将 current 切掉前 step 个头切下来。

		// left->@->@->null   right->@->@->null   current->@->@->null
		current = cut(right, step); // 将 right 切掉前 step 个头切下来。
		
		// dummy.next -> @->@->@->@->null,最后一个节点是 tail,始终记录
		//                        ^
		//                        tail
		tail.next = merge(left, right);
		while (tail->next) tail = tail->next; // 保持 tail 为尾部
	}
}

代码:

1. 代码一:归并排序(递归法)

public class code148_1 {

    // 时间复杂度: O(nlogn)
    // 空间复杂度: O(logn)
    // 自顶向下
    public static ListNode sortList(ListNode head) {
        // 1、递归结束条件
        // 当链表为空或者链表只剩下一个结点时,直接返回 head
        if(head == null || head.next == null)
        {
            return head;
        }
        // 2、找到链表中间节点并断开链表 & 递归下探
        ListNode midNode = middleNode(head);
        // 得到中间节点 midNode,从中间cut 
        ListNode rightHead = midNode.next;
        // 将第一个链表的结尾(midNode位置的下一个节点)置为空,实现切断
        midNode.next = null; 

        // 递归对cut后的子链表排序
        ListNode left = sortList(head);
        ListNode right = sortList(rightHead);

        // 3、当前层业务操作(合并有序链表)
        // 比较合并两个有序链表
        return mergeTwoLists(left, right);
    }

    //  找到链表中间节点(876. 链表的中间结点)
    public static ListNode middleNode(ListNode head) {
        if(head == null)
        {
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
    
        while(fast.next != null && fast.next.next != null)
        {
            // fast 一次前进两个元素,slow 一次前进一个元素
            slow = slow.next;
            fast = fast.next.next;
        }
        // 链表元素为奇数个时,slow 指向链表的中点
        // 链表元素为偶数个时,slow 指向链表两个中点的左边一个        
        return slow;
    }

    // 合并两个有序链表(21. 合并两个有序链表)
    public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    	// 类似归并排序中的合并过程
    	ListNode dummy = new ListNode(0);
    	ListNode cur = dummy;
    	while(l1 != null && l2 != null)
    	{
    		if(l1.val < l2.val)
    		{
    			cur.next = l1;
    			cur = cur.next;
    			l1 = l1.next;
    		}
    		else
    		{
    			cur.next = l2;
    			cur = cur.next;
    			l2 = l2.next;
    		}
    	}

    	// 任一为空,直接连接另一条链表
    	if(l1 == null)
    	{
    		cur.next = l2;
    	}
    	else
    	{
    		cur.next = l1;
    	}
    	return dummy.next;
	}

    private static ListNode createLinkedList(int[] arr) {// 将输入的数组输入到链表中
        if (arr.length == 0) {
            return null;
        }
        ListNode head = new ListNode(arr[0]);
        ListNode current = head;
        for (int i = 1; i < arr.length; i++) {// 过程
            current.next = new ListNode(arr[i]);
            current = current.next;
        }
        return head;
    }

    private static void printLinkedList(ListNode head) {// 将链表结果打印
        ListNode current = head;
        while (current != null) {
            System.out.printf("%d -> ", current.val);
            current = current.next;
        }
        System.out.println("NULL");
    }

    public static void main(String[] args) {
        int[] x1 = { 4, 2, 1, 3 };
        ListNode list1 = createLinkedList(x1);
        printLinkedList(list1);
        ListNode res1 = sortList(list1);
        printLinkedList(res1);

        System.out.println("***************************************");

        int[] x2 = { -1, 5, 3, 4, 0 };
        ListNode list2 = createLinkedList(x2);
        printLinkedList(list2);
        ListNode res2 = sortList(list2);
        printLinkedList(res2);
    }
}

2. 代码二:归并排序(从底至顶直接合并)

public class code148_2 {

    // 时间复杂度: O(nlogn)
    // 空间复杂度: O(1)
    // 从底至顶直接合并	
    public static ListNode sortList(ListNode head) {
        ListNode dummyHead = new ListNode(Integer.MIN_VALUE);
        dummyHead.next = head;
        // 先统计长度f
        ListNode p = dummyHead.next;
        int length = 0;
        while (p != null) {
            ++length;
            p = p.next;
        }
        // 循环开始切割和合并
        for (int size = 1; size < length; size <<= 1) {
            ListNode cur = dummyHead.next;
            ListNode tail = dummyHead;
            while (cur != null) {
                ListNode left = cur;
                ListNode right = cut(cur, size); // 链表切掉size 剩下的返还给right
                cur = cut(right, size); // 链表切掉size 剩下的返还给cur
                tail.next = merge(left, right);
                while (tail.next != null) {
                    tail = tail.next; // 保持最尾端
                }
            }
        }
        return dummyHead.next;
    }

    /**
     * 将链表L切掉前n个节点 并返回后半部分的链表头
     * 
     * @param head
     * @param n
     * @return
     */
    public static ListNode cut(ListNode head, int n) {
        if (n <= 0)
            return head;
        ListNode p = head;
        // 往前走n-1步
        while (--n > 0 && p != null) {
            p = p.next;
        }
        if (p == null)
            return null;
        ListNode next = p.next;
        p.next = null;
        return next;
    }

    /**
     * 合并list1和list2
     * 
     * @param list1
     * @param list2
     * @return
     */
    public static ListNode merge(ListNode list1, ListNode list2) {
        ListNode dummyHead = new ListNode(Integer.MIN_VALUE), p = dummyHead;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                p.next = list1;
                list1 = list1.next;
            } else {
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;
        }
        if (list1 == null) {
            p.next = list2;
        } else {
            p.next = list1;
        }
        return dummyHead.next;
    }

    private static ListNode createLinkedList(int[] arr) {// 将输入的数组输入到链表中
        if (arr.length == 0) {
            return null;
        }
        ListNode head = new ListNode(arr[0]);
        ListNode current = head;
        for (int i = 1; i < arr.length; i++) {// 过程
            current.next = new ListNode(arr[i]);
            current = current.next;
        }
        return head;
    }

    private static void printLinkedList(ListNode head) {// 将链表结果打印
        ListNode current = head;
        while (current != null) {
            System.out.printf("%d -> ", current.val);
            current = current.next;
        }
        System.out.println("NULL");
    }

    public static void main(String[] args) {
        int[] x1 = { 4, 2, 1, 3 };
        ListNode list1 = createLinkedList(x1);
        printLinkedList(list1);
        ListNode res1 = sortList(list1);
        printLinkedList(res1);

        System.out.println("***************************************");

        int[] x2 = { -1, 5, 3, 4, 0 };
        ListNode list2 = createLinkedList(x2);
        printLinkedList(list2);
        ListNode res2 = sortList(list2);
        printLinkedList(res2);
    }
}

参考:

  1. Sort List (归并排序链表)
  2. 148. 排序链表-bottom-to-up O(1) 空间
  3. Sort List——经典(链表中的归并排序)
©️2020 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值