归并算法

思路:

  • 分解成两个待排序的区间;
  • 使用递归将两个子序列分别排好序;
  • 申请空间,定义两个指针指向两个序列的起始位置,比较指针所指向的元素,选择相对小的元素放入合并空间。

统一格式:

  • 先递归、后merge;
  • 递归的跳出条件是:
if(right == left){
    return 0;
}

稳定性:

主要取决于合并的过程,也就是两个有序子数组合并成一个有序数组的时候,将左侧的优先放在数组中,即可实现稳定性
public void merge(int[] nums, int left, int mid, int right){
    int[] temp = new int[nums.length];
    System.arraycopy(nums, left, temp, left, right - left + 1);
    int i = left;
    int j = mid + 1;
    
    for(int k = 0; k <= right; k++){
        边界问题
        if(i == mid + 1){
            nums[k] = temp[j++];
        }else if(j == right + 1){
            nums[k] = temp[i++];
        }else if(temp[i] <= temp[j]){
            加上等号、遇到相同元素的时候优先放入左侧的数据,保证稳定性
            nums[k] = temp[i++];
        }else{
            nums[k] = temp[j++];
        }
    }
}

时间复杂度:

  • 归并排序的执行效率与要排序的原始数组的有序程度无关;无论数据如何,都是根据中间位置分两半递归之后合并;
  • 最好时间复杂度、最坏时间复杂度、平均时间复杂度均为:O(nlogn);

例题

例题:912. 排序数组

class Solution {
    public int[] sortArray(int[] nums) {
        // 归并排序
        if(nums.length == 0){
            return new int[]{};
        }
        reverse(nums, 0, nums.length - 1);
        return nums;
    }

    public void reverse(int[] nums, int left, int right){
        if(left == right){
            return;
        }
        // 排好序
        int mid = (right - left) / 2 + left;
        reverse(nums, left, mid);
        reverse(nums, mid + 1, right);

        // 合并
        merge(nums, left, mid, right);
    }

    public void merge(int[] nums, int left, int mid, int right){
        int[] temp = new int[nums.length];
        System.arraycopy(nums, left, temp, left, right - left + 1);
        int i = left;
        int j = mid + 1;

        for(int k = left; k <= right; k++){
            if(i == mid + 1){
                nums[k] = temp[j];
                j++;
            }else if(j == right + 1){
                nums[k] = temp[i];
                i++;
            }else if(temp[i] <= temp[j]){
                nums[k] = temp[i];
                i++;
            }else{
                nums[k] = temp[j];
                j++;
            }
        }
    }


}

例题:剑指 Offer II 077. 链表排序

/**
 * 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 fast = head, slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }

        ListNode mid = slow.next;
        // 将前面断开
        slow.next = null;

        ListNode l1 = sortList(head);
        ListNode l2 = sortList(mid);
        return merge(l1, l2);

    }

    // 合并
    public ListNode merge(ListNode l1, ListNode l2){
        ListNode head = new ListNode();
        ListNode node = head;

        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                node.next = l1;
                l1 = l1.next;
            }else{
                node.next = l2;
                l2 = l2.next;
            }

            node = node.next;
        }

        if(l1 != null){
            node.next = l1;
        }else if(l2 != null){
            node.next = l2;
        }

        return head.next;
    }
}

例题:剑指 Offer 51. 数组中的逆序对
class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length < 2){
            return 0;
        }

        return reversePairs(nums, 0, nums.length - 1, new int[nums.length]);
    }

    public int reversePairs(int[] nums, int left, int right, int[] temp) {
        if(left == right){
            return 0;
        }

                //排序
        int mid = (right - left) / 2 + left;
        int leftPairs = reversePairs(nums, left, mid, temp);
        int rightPairs = reversePairs(nums, mid + 1, right, temp);

        if(nums[mid] <= nums[mid + 1]){
            return leftPairs + rightPairs;
        }

                //合并两个子序列的过程
        int crossPairs = reversePairs(nums, left, mid, right, temp);
        return leftPairs + rightPairs + crossPairs;
    }

    public int reversePairs(int[] nums, int left, int mid, int right, int[] temp) {
        System.arraycopy(nums, left, temp, left, right - left + 1);
        int i = left;
        int j = mid + 1;
        int count = 0;

        for(int k = left; k <= right; k++){
            if(i == mid + 1){
                nums[k] = temp[j];
                j++;
            }else if(j == right + 1){
                nums[k] = temp[i];
                i++;
            }else if(temp[i] <= temp[j]){
                nums[k] = temp[i];
                i++;
            }else{
                nums[k] = temp[j];
                j++;
                count += mid - i + 1;
            }
        }
        
        return count;
    }
}