【D23】对链表进行插入排序 & 合并两个有序链表 &排序链表 (LC 147&21&148)

今日主题:链表。

147. 对链表进行插入排序

问题描述

对链表进行插入排序。

解题思路

  • 添加虚拟头节点,保证对链表节点操作的一致性
  • 注意切断头节点与后续节点的指针,不然会形成环形链表

代码实现

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode insertionSortList(ListNode head) {
        if(head == null){
            return head;
        }

        //添加虚拟头节点,指向新的已排好序的链表
        ListNode dummyHead = new ListNode(0);
        //将头节点的值加入已排好序的链表;避免形成环形链表
        dummyHead.next = new ListNode(head.val);

        ListNode cur = head.next;
        while(cur != null){
            //temp保存当前节点的下一个节点
            ListNode temp = cur.next;

            //从头开始遍历已经排好序的链表,找到插入位置
            ListNode pre = dummyHead, index = dummyHead.next;;
            while(index != null && index != cur){
                if(cur.val < index.val){
                    break;
                }
                pre = pre.next;
                index = index.next;
            }

            //如果退出循环时,index==null,表示当前值比之前所有的值都大,不需要进行插入
            //index != null, 需要进行节点插入
            if(index != cur){
                pre.next = cur;
                cur.next = index;
            }
            cur = temp;   
        }
        return dummyHead.next;   
    } 
}

21. 合并两个有序链表

问题描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

解题思路

应用归并排序合并数组的思想。

代码实现

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(0);
        ListNode tail = head;

        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                tail.next = new ListNode(l1.val);
                l1 = l1.next;
            }else {
                tail.next = new ListNode(l2.val);
                l2 = l2.next;
            }
            tail = tail.next;
        }
        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        tail.next = l1 == null ? l2 : l1;
        return head.next;
        
    }
}

148. 排序链表

问题描述

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

解题思路

时间复杂度为O(n log n)的常见排序算法有:快排、堆排、归并。
其中快排和堆排设计到的元素交换操作较多,而对单链表中的两个任意节点进行交换,操作比较复杂。正好,归并排序算法中合并左右有序数组的操作有用到双指针。因此,采用归并排序实现。

代码实现

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        //将链表拆分为左右两段
        ListNode mid = getMid(head);
        ListNode rightHead = mid .next;
        mid.next = null;

        //得到有序左、右子链表
        ListNode sortedLeft = sortList(head);
        ListNode sortRight = sortList(rightHead);

        //合并左右子链表
        return mergeTwoLists(sortedLeft,sortRight);
    }

    //采用快慢指针找到链表的中点(偶数节点返回偏左节点)
    public ListNode getMid(ListNode head){
        ListNode dummyHead = new ListNode(0,head);
        ListNode slow = dummyHead, fast = dummyHead;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    //合并两个有序链表
     public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(0);
        ListNode tail = head;

        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                tail.next = new ListNode(l1.val);
                l1 = l1.next;
            }else {
                tail.next = new ListNode(l2.val);
                l2 = l2.next;
            }
            tail = tail.next;
        }
        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        tail.next = l1 == null ? l2 : l1;
        return head.next;    
    }
}

你可能感兴趣的:(【D23】对链表进行插入排序 & 合并两个有序链表 &排序链表 (LC 147&21&148))