归并排序

归并算法

思路:

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

统一格式:

  • 先递归、后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;
    }
}


hshuo的面试之路 文章被收录于专栏

作者目标是找到一份Java后端方向的工作 此专栏用来记录从Bilibili、书本、其他优质博客上面学习的内容 用于巩固、总结内容 主要包含Docker、Dubbo、Java基础、JUC、Maven、MySQL、Redis、SpringBoot、SpringCloud、数据结构、杂文、算法、计算机网络、操作系统、设计模式等相关内容

全部评论

相关推荐

秋盈丶:后续:我在宿舍群里和大学同学分享了这事儿,我好兄弟气不过把他挂到某脉上了,10w+阅读量几百条评论,直接干成精品贴子,爽
点赞 评论 收藏
分享
05-07 17:58
门头沟学院 Java
wuwuwuoow:1.简历字体有些怪怪的,用啥写的? 2.Redis 一主二从为什么能解决双写一致性? 3.乐观锁指的是 SQL 层面的库存判断?比如 stock > 0。个人认为这种不算乐观锁,更像是乐观锁的思想,写 SQL 避免不了悲观锁的 4.奖项证书如果不是 ACM,说实话没什么必要写 5.逻辑过期时间为什么能解决缓存击穿问题?逻辑过期指的是什么 其实也没什么多大要改的。海投吧
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务