4. Find the median of two ordinal arrays

Description of topic

Given two positively ordered (from small to large) arrays nums1 and nums2 of sizes m and n respectively. Please find and return the median of these two ordinal arrays.

The time complexity of the algorithm should be O(log (m + n)) .

Link to the topic

Find the median of two ordinal arrays

Test case

Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] , median 2

Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: Combined array = [1,2,3,4] , median (2 + 3) / 2 = 2.5

Solution 1: Combine arrays

Thoughts

Merge the two arrays directly, and find the median according to the parity of the number of elements in the merged array

Problem solving code

class Solution {<!-- -->
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {<!-- -->
    // merged array
        int[] sum = new int[nums1. length + nums2. length];
        //i, j are the array subscripts of nums1 and nums2 respectively, and n1, n2 correspond to the values of the array subscripts
        //k represents the array subscript of the merged array
        int i=0, j=0, k=0, n1, n2;
        while(i<nums1.length || j<nums2.length){<!-- -->
        //Note that the smaller data is put into the sum array, so if an array element is empty, it should be the maximum value
            n1 = i<nums1.length ? nums1[i] : (int)Math.pow(10,6);
            n2 = j<nums2.length ? nums2[j] : (int)Math.pow(10,6);
            if(n1 < n2){<!-- -->
                sum[k] = n1;
                i + + ;
                k++;
            }else{<!-- -->
                sum[k] = n2;
                j + + ;
                k++;
            }
        }
        //double type! avoid division with remainder
        double result;
        if(sum.length%2 == 0){<!-- -->
            // Pay attention to cast to doble type
            result = ((double)sum[sum. length/2] + sum[sum. length/2-1])/2;
        }else{<!-- -->
            result = sum[sum. length/2];
        }
        return result;
    }
}

There is a problem

Simple merging arrays, the time complexity is O(m + n), which does not meet the requirements of O(log(m + n)) in the title

Solution 2

Remove elements that cannot be the median at each judgment

Thoughts

  1. solve by recursion
  2. Find the kth largest element in the array nums1 and nums2, you can find the k/2th element of the two arrays, and then judge the size of the two elements
  3. If the k/2th element of nums1 is small, it means that the first k/2 elements in the array nums1 must not meet the requirements! Then the problem is transformed into finding (nums1-first k/2 elements) and nums2’s (k-number of elements deleted by nums1) large elements
  4. Note that the comparison here is not necessarily the k/2th element in nums1 and nums2, you need to consider whether there are k/2 elements in the two arrays, if not, then judge the last element of the array
  5. Conditions for recursive exit
    (1) There is an array with a length of 0, and the kth element of another array is returned
    (2) k=1, returns the minimum value of the first element in the current two arrays
    Note: The order of these two conditions cannot be reversed! Avoid array out of bounds
  6. Tip: The sum of the number of elements in the two arrays is an odd or even number, which affects the solution to the median, and it is converted into a situation for easy solution: both are averaged

Problem solving code

class Solution {<!-- -->
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {<!-- -->
        //Record the length of the two arrays
        int nums1Length = nums1. length;
        int nums2Length = nums2. length;
        //Merge odd or even array numbers, and finally find the average value of the left and right numbers
        //The number of elements in the merged array has an odd number: 12345, left=right=3 [The number of elements in the array to be merged is 2 and 3 respectively]
        //The number of elements in the merged array has an even number: 123456, left=3, right=4 [The number of elements in the array to be merged is 3 3 respectively]
        int left = (nums1Length + nums2Length + 1) / 2;
        int right = (nums1Length + nums2Length + 2) / 2;//default rounded down
        //The final result is the average value of the right and left array elements
        return (findKth(nums1, 0, nums1Length-1, nums2, 0, nums2Length-1, left) +
                findKth(nums1, 0, nums1Length-1, nums2, 0, nums2Length-1, right))/2;
    }
    /**
    Find the kth largest element in the two arrays.
    If the k/2th largest element in num1 <the k/2th largest element in num2, it means that the first k/2 elements in num1 do not meet the requirements
    The topic becomes (remove num1 of the first k/2 elements in num1) and the (k/2-number of removed elements) element of len2
    Exit recursion condition: (1) k=1, compare the size of the start value of the two arrays, and return the smallest one (2) if the element in one array is empty, directly return the kth element in the other array

    Parameters to be passed in: two arrays, the start and end of the two arrays (the comparison is the number between the array subscripts in the two arrays from start to end), k (need to find the kth element)
     */
    public double findKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k){<!-- -->
        int length1 = end1 - start1 + 1;//The array length of num1
        int length2 = end2 - start2 + 1;//num2 array length

        //If the lengths of the two arrays are different, nums1 is always an array with a smaller length (easy to write exit recursion conditions)
        if(length1 > length2){<!-- -->
            return findKth(nums2, start2, end2, nums1, start1, end1, k);
        }

        //The condition for exiting recursion, pay attention to the order of 1 and 2! avoid crossing the line
        //1. One array is empty, that is, find the kth element of another array
        if(length1 == 0){<!-- -->
            // Return the kth element in num2 after the change (currently the subscript of nums2 starts from start2)
            return nums2[start2 + k - 1];
        }
        //2. k=1, that is, find the first smallest value in the two arrays
        if(k == 1){<!-- -->
            //Returns which of the two arrays' starting subscripts corresponds to the smaller element
            return Math.min(nums1[start1], nums2[start2]);
        }
        

        //Loop recursion, if the k/2th element of one array num1 is smaller than another array num2, then the first k/2 elements in num1 are not the minimum value
        //You can remove the first k/2 elements in num1 (modify the start of num1), and then find the modified num1 and the initial num2's (k-number of elements to delete num1) element
        //Note that judging the length of the array and the size of k/2, a smaller value should be taken to avoid crossing the boundary

        int i = Math.min(start1 + length1 -1, start1 + k/2 -1);//Record the array subscript of num1 elements to be judged
        int j = Math.min(start2 + length2 -1, start2 + k/2 -1);//Record the array subscript of num2 elements to be judged
        //-1 is because the array subscript starts from 0

        //The first j elements of num2 are deleted
        if(nums1[i] > nums2[j]){<!-- -->
            // A total of j-start2 + 1 elements have been deleted
            return findKth(nums1, start1, end1, nums2, j + 1, end2, k-(j - start2 + 1));
        }else{<!-- -->
            return findKth(nums1, i + 1, end1, nums2, start2, end2, k-(i - start1 + 1));
        }

    }
}

Java while learning

Index

Find the exponent Math.pow

double pow(double base, double exponent)
//The returned result is in double form, pay attention to format conversion
(int)Math.pow(10,6)

Division operation

In the division operation, two integers are divided. If you want to get double type data, you need to convert one of the divisors to double type first, otherwise you will always get integer type data.
eg: (3 + 2)/2=2, ((double)3 + 2)/2=2.5