0
点赞
收藏
分享

微信扫一扫

算法训练——归并排序算法(LeetCodeHOT100)


摘要

包括了的归并的排序算法的原理。如果涉及到数组 链表有序同时时间复杂度为nlog(n) 那就需要考虑到到归并排序算法。

一、算法练习题

剑指 Offer 51. 数组中的逆序对

归并排序与逆序对是息息相关的。归并排序体现了 “分而治之” 的算法思想,具体为:

分: 不断将数组从中点位置划分开(即二分法),将整个数组的排序问题转化为子数组的排序问题;
治: 划分到子数组长度为 1 时,开始向上合并,不断将 较短排序数组 合并为 较长排序数组,直至合并至原数组时完成排序;

算法训练——归并排序算法(LeetCodeHOT100)_子数组

合并阶段本质上是合并两个排序数组 的过程,而每当遇到左子数组当前元素>右子数组当前元素 时,意味着 左子数组当前元素 至 末尾元素与右子数组当前元素构成了若干「逆序对」 。 因此,考虑在归并排序的合并阶段统计「逆序对」数量,完成归并排序时,也随之完成所有逆序对的统计。

算法训练——归并排序算法(LeetCodeHOT100)_子数组_02

算法训练——归并排序算法(LeetCodeHOT100)_子数组_03

class Solution {
    int count = 0;
    public int reversePairs(int[] array) {
         // 长度小于2则无逆序对
        if (array.length < 2) {
            return 0;
        }
        // 进入归并
        mergeSort(array, 0, array.length - 1);
        return count;
    }
    private void mergeSort(int[] array, int left, int right) {
        // 找分割点
        int mid = (left + right) >> 1;
        if (left < right) {
            // 左子数组
            mergeSort(array, left, mid);
            // 右子数组
            mergeSort(array, mid + 1, right);
            // 并
            merge(array, left, mid, right);
        }
    }
    private void merge(int[] array, int left, int mid, int right) {
        // 创建临时数组,长度为此时两个子数组加起来的长度
        int[] arr = new int[right - left + 1];
        // 临时数组的下标起点
        int index = 0;

        // 保存在原数组的起点下标值
        int s = left;

        // 左子数组的起始指针
        int l = left;
        // 右子数组的起始指针
        int r = mid + 1;

        while (l <= mid && r <= right) {
            // 当左子数组的当前元素小的时候,跳过,无逆序对
            if (array[l] <= array[r]) {
                // 放入临时数组
                arr[index] = array[l];
                // 临时数组下标+1
                index++;
                // 左子数组指针右移
                l++;
            } else { // 否则,此时存在逆序对
                // 放入临时数组
                arr[index] = array[r];
                // 逆序对的个数为 左子数组的终点- 当前左子数组的当前指针
                count += mid - l + 1;
                // 临时数组+1
                index++;
                // 右子数组的指针右移
                r++;
            }
        }
        // 左子数组还有元素时,全部放入临时数组
        while (l <= mid){
            arr[index++] = array[l++];}
        // 右子数组还有元素时,全部放入临时数组
        while (r <= right) {
            arr[index++] = array[r++];
        }
        // 将临时数组中的元素放入到原数组的指定位置
        for (int num : arr) {
            array[s++] = num;
        }
    }
}

315. 计算右侧小于当前元素的个数

(这个题目没有理解怎么实现)

package 归并算法;

import java.util.Arrays;
import java.util.List;

/**
 * @Classname countSmaller315
 * @Description TODO
 * @Date 2022/2/15 20:59
 * @Created by xjl
 */
public class countSmaller315 {
    class TreeNode {
        int value;//
        int count;// 表示的左子树元素的个数
        TreeNode left;
        TreeNode right;

        public TreeNode(int value) {
            this.value = value;
        }
    }

    public List<Integer> countSmaller(int[] nums) {
        Integer[] res = new Integer[nums.length];
        Arrays.fill(res, 0);
        TreeNode root = null;
        for (int i = nums.length - 1; i >= 0; i--) {
            root = create(root, new TreeNode(nums[i]), res, i);
        }
        return Arrays.asList(res);
    }

    private TreeNode create(TreeNode root, TreeNode node, Integer[] res, int i) {
        if (root == null) {
            root = node;
            return root;
        }
        if (root.value >= node.value) {
            root.count++;
            root.left = create(root.left, node, res, i);
        } else {
            res[i] += 1 + root.count;
            root.right = create(root.right, node, res, i);
        }
        return root;
    }
}

方法二:归并排序

package 归并算法;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Classname countSmaller315
 * @Description TODO
 * @Date 2022/2/15 20:59
 * @Created by xjl
 */
public class countSmaller315 {
    
    List<Integer> ans = new ArrayList<>(); //记录最终的结果
    int[] index; //原数组的索引数组,存储着原数组中每个元素对应的下标
    int[] count; //记录题目中所求的count[i]

    public List<Integer> countSmaller2(int[] nums) {
        int len = nums.length;
        index = new int[len];
        count = new int[len];
        for (int i = 0; i < nums.length; i++) {
            index[i] = i;
        }
        mergeSort(nums, 0, nums.length - 1);
        return Arrays.stream(count).boxed().collect(Collectors.toList());
    }

    public void mergeSort(int[] a, int l, int r) {
        if (l >= r) {
            return;
        }
        int mid = (l + r) >> 1;
        mergeSort(a, l, mid);
        mergeSort(a, mid + 1, r);
        merge(a, l, mid, r);
    }

    private void merge(int[] nums, int start, int mid, int end) {
        int P1 = start;
        int P2 = mid + 1;
        int cur = 0;

        int[] tmp = new int[end - start + 1]; //临时数组用于存储一次归并过程中排序好的元素,

        int[] tmpIndex = new int[end - start + 1];//临时数组的索引数组,存储这临时数组中每个元素对应的下标

        while (P1 <= mid && P2 <= end) {
            if (nums[P1] > nums[P2]) {
                count[index[P1]] += end - P2 + 1; //右半部分小于nums[P1]元素的数目
                tmpIndex[cur] = index[P1]; //记录元素位置的改变
                tmp[cur] = nums[P1];
                P1++;
            } else {
                tmp[cur] = nums[P2];
                tmpIndex[cur] = index[P2];
                P2++;
            }
            cur++;
        }
        while (P1 <= mid) {
            tmp[cur] = nums[P1];
            tmpIndex[cur] = index[P1];
            P1++;
            cur++;
        }
        while (P2 <= end) {
            tmp[cur] = nums[P2];
            tmpIndex[cur] = index[P2];
            P2++;
            cur++;
        }
        for (int i = 0; i < end - start + 1; i++) {
            nums[i + start] = tmp[i];
            index[i + start] = tmpIndex[i];
        }
    }
}

327. 区间和的个数

493. 翻转对

package 归并算法;

import org.junit.Test;

/**
 * @Classname reversePairs493
 * @Description TODO
 * @Date 2022/2/16 22:05
 * @Created by xjl
 */
public class reversePairs493 {

    @Test
    public void test() {
        int i = reversePairs(new int[]{2, 4, 3, 5, 1});
        System.out.println(i);
    }

    int count = 0;

    public int reversePairs(int[] nums) {
        mergeSort(nums, 0, nums.length - 1);
        return count;
    }

    void mergeSort(int[] nums, int l, int r) {
        if (l >= r) {
            return;
        }
        int mid = (l + r) / 2;
        mergeSort(nums, l, mid);
        mergeSort(nums, mid + 1, r);
        calculate(nums, l, mid, r);
        sort(nums, l, mid, r);
    }

    void calculate(int[] nums, int l, int mid, int r) {
        for (int i = l, j = mid + 1; i <= mid; i++) {
            while (j <= r && nums[i] > 2L * nums[j]) {
                j++;
            }
            count += j - (mid + 1);
        }
    }

    void sort(int[] nums, int l, int mid, int r) {
        int[] temp = new int[r - l + 1];
        int i = l, j = mid + 1;
        int index = 0;

        while (i <= mid && j <= r) {
            temp[index++] = nums[i] > nums[j] ? nums[j++] : nums[i++];
        }

        while (i <= mid) {
            temp[index++] = nums[i++];
        }
        while (j <= r) {
            temp[index++] = nums[j++];
        }

        for (int k = 0; k < temp.length; k++) {
            nums[l + k] = temp[k];
        }
    }
}

剑指 Offer 25. 合并两个排序的链表

算法训练——归并排序算法(LeetCodeHOT100)_子数组_04

package 归并算法;

import java.util.List;

/**
 * @Classname mergeTwoLists25
 * @Description TODO
 * @Date 2022/2/16 23:33
 * @Created by xjl
 */
public class mergeTwoLists25 {
    public class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
        }
    }

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dumy = new ListNode(-1);

        ListNode curr=dumy;
        ListNode l = l1;
        ListNode r = l2;

        while (l != null && r != null) {
            if (l.val>=r.val){
                curr.next=r;
                r=r.next;
            }else {
                curr.next=l;
                l=l.next;
            }
            curr=curr.next;
        }
        while (l!=null){
            curr.next=l;
            l=l.next;
            curr=curr.next;
        }
        while (r!=null){
            curr.next=r;
            r=r.next;
            curr=curr.next;
        }
        return dumy.next;
    }
}

88. 合并两个有序数组

算法训练——归并排序算法(LeetCodeHOT100)_数组_05

package 归并算法;

import org.junit.Test;

/**
 * @Classname merge88
 * @Description TODO
 * @Date 2022/2/16 23:02
 * @Created by xjl
 */
public class merge88 {
    /**
     * @description 倒序的数组合并 从后面向前开始
     * @param: nums1
     * @param: m
     * @param: nums2
     * @param: n
     * @date: 2022/2/16 23:02
     * @return: void
     * @author: xjl
     */
    public void merge(int[] nums1, int m, int[] nums2, int n) {

        int index = m + n - 1;

        int l = m - 1;
        int r = n - 1;

        while (l >= 0 && r >= 0) {
            if (nums1[l] >= nums2[r]) {
                nums1[index--] = nums1[l--];
            } else {
                nums1[index--] = nums2[r--];
            }
        }
        while (l >= 0) {
            nums1[index--] = nums1[l--];
        }
        while (r >= 0) {
            nums1[index--] = nums2[r--];
        }
    }


    @Test
    public void test(){
        merge(new int[]{0},0,new int[]{1},1);
    }
}

23. 合并K个升序链表

/**
 * 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 mergeKLists(ListNode[] lists) {
         if (lists.length == 0) {
            return null;
        }
        return merge(lists, 0, lists.length - 1);
    }
    public ListNode merge(ListNode[] lists, int l, int r) {
        if (l == r) {
            return lists[l];
        }
        if (l > r) {
            return null;
        }
        int mid = (l + r) >> 1;
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    }

    /**
     * @description 两个链表的合并
     * @param: a
     * @param: b
     * @date: 2022/2/17 0:13
     * @return: 归并算法.mergeKLists23.ListNode
     * @author: xjl
     */
    public ListNode mergeTwoLists(ListNode a, ListNode b) {
        if (a == null || b == null) {
            return a != null ? a : b;
        }
        ListNode head = new ListNode(0);
        ListNode tail = head, aPtr = a, bPtr = b;
        while (aPtr != null && bPtr != null) {
            if (aPtr.val < bPtr.val) {
                tail.next = aPtr;
                aPtr = aPtr.next;
            } else {
                tail.next = bPtr;
                bPtr = bPtr.next;
            }
            tail = tail.next;
        }
        tail.next = (aPtr != null ? aPtr : bPtr);
        return head.next;
    }
}

148. 排序链表

同样的也是的归并排序算法

切分

合并链表

算法训练——归并排序算法(LeetCodeHOT100)_数组_06

算法训练——归并排序算法(LeetCodeHOT100)_逆序对_07

package 归并排序算法;

public class sortList148v2 {
    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;
        }
    }

    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        //快慢指针切割
        ListNode fast = head.next, slow = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next;
        slow.next = null;
        // 合并链表
        ListNode left = sortList(head);
        ListNode right = sortList(tmp);
        ListNode result=mergetwolist(left,right);
        return result;
    }

    /**
     * 链表和合并
     *
     * @param l1
     * @param l2
     * @return
     */
    private ListNode mergetwolist(ListNode l1, ListNode l2) {
        ListNode dumy = new ListNode(-1);
        ListNode curr = dumy;
        while (l1 != null && l2 != null) {
            if (l1.val> l2.val){
                curr.next=l2;
                l2=l2.next;
            }else {
                curr.next=l1;
                l1=l1.next;
            }
            curr=curr.next;
        }
        while (l1!=null){
            curr.next=l1;
            curr=curr.next;
            l1=l1.next;
        }
        while (l2!=null){
            curr.next=l2;
            curr=curr.next;
            l2=l2.next;
        }
        return dumy.next;
    }
}

912. 排序数组

就是的一个的归并排序的原理 

class Solution {
    public int[] sortArray(int[] nums) {
         if (nums.length < 2) {
            return nums;
        }
        split_array(nums, 0, nums.length - 1);
        return nums;
    }

    public void split_array(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = (left + right) >> 1;
        split_array(nums, left, mid);
        split_array(nums, mid + 1, right);
        merge(nums, left, mid, right);
    }

    public void merge(int[] nums, int left, int mid, int right) {
        int[] array = new int[right - left + 1];
        int index = 0;

        int s = left;

        int l = left;
        int r = mid + 1;

        while (l <= mid && r <= right) {
            if (nums[l] <= nums[r]) {
                // 放入临时数组
                array[index] = nums[l];
                // 临时数组下标+1
                index++;
                // 左子数组指针右移
                l++;
            } else { // 否则,此时存在逆序对
                // 放入临时数组
                array[index] = nums[r];
                // 临时数组+1
                index++;
                // 右子数组的指针右移
                r++;
            }
        }
        // 左子数组还有元素时,全部放入临时数组
        while (l <= mid) {
            array[index++] = nums[l++];
        }
        // 右子数组还有元素时,全部放入临时数组
        while (r <= right) {
            array[index++] = nums[r++];
        }
        // 将临时数组中的元素放入到原数组的指定位置
        for (int num : array) {
            nums[s++] = num;
        }
    }
}

剑指 Offer II 077. 链表排序

package 归并排序算法;

public class sortList148v2 {
    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;
        }
    }

    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        //快慢指针切割
        ListNode fast = head.next, slow = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next;
        slow.next = null;
        // 合并链表
        ListNode left = sortList(head);
        ListNode right = sortList(tmp);
        ListNode result=mergetwolist(left,right);
        return result;
    }

    /**
     * 链表和合并
     *
     * @param l1
     * @param l2
     * @return
     */
    private ListNode mergetwolist(ListNode l1, ListNode l2) {
        ListNode dumy = new ListNode(-1);
        ListNode curr = dumy;
        while (l1 != null && l2 != null) {
            if (l1.val> l2.val){
                curr.next=l2;
                l2=l2.next;
            }else {
                curr.next=l1;
                l1=l1.next;
            }
            curr=curr.next;
        }
        while (l1!=null){
            curr.next=l1;
            curr=curr.next;
            l1=l1.next;
        }
        while (l2!=null){
            curr.next=l2;
            curr=curr.next;
            l2=l2.next;
        }
        return dumy.next;
    }
}

剑指 Offer II 078. 合并排序链表

package 归并算法;

/**
 * @Classname mergeKLists23
 * @Description TODO
 * @Date 2022/2/17 0:06
 * @Created by xjl
 */
public class mergeKLists23 {
    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;
        }
    }

    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        return merge(lists, 0, lists.length - 1);
    }

    public ListNode merge(ListNode[] lists, int l, int r) {
        if (l == r) {
            return lists[l];
        }
        if (l > r) {
            return null;
        }
        int mid = (l + r) >> 1;
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    }

    /**
     * @description 两个链表的合并
     * @param: a
     * @param: b
     * @date: 2022/2/17 0:13
     * @return: 归并算法.mergeKLists23.ListNode
     * @author: xjl
     */
    public ListNode mergeTwoLists(ListNode a, ListNode b) {
        if (a == null || b == null) {
            return a != null ? a : b;
        }
        ListNode head = new ListNode(0);
        ListNode tail = head, aPtr = a, bPtr = b;
        while (aPtr != null && bPtr != null) {
            if (aPtr.val < bPtr.val) {
                tail.next = aPtr;
                aPtr = aPtr.next;
            } else {
                tail.next = bPtr;
                bPtr = bPtr.next;
            }
            tail = tail.next;
        }
        tail.next = (aPtr != null ? aPtr : bPtr);
        return head.next;
    }
}

博文参考

举报

相关推荐

0 条评论