【469、求数组中位数】

92 阅读2分钟

Java 求数组中位数的常见方式有以下几种:

  1. 排序法:将数组进行排序,然后根据数组的长度分为奇数和偶数两种情况,如果是奇数,则中位数为排序后的数组的中间数,如果是偶数,则中位数为排序后的数组中间两个数的平均值。
import java.util.Arrays;

public class Median {
    public static double findMedian(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        if (n % 2 == 0) {
            // 如果数组长度为偶数,中位数为中间两个数的平均值
            return (nums[n / 2 - 1] + nums[n / 2]) / 2.0;
        } else {
            // 如果数组长度为奇数,中位数为中间的那个数
            return nums[n / 2];
        }
    }

    public static void main(String[] args) {
        int[] nums = {3, 1, 4, 2, 5};
        System.out.println(findMedian(nums)); // 输出 3.0
    }
}

  1. 快速选择法:这是一种基于快速排序思想的算法,每次选择一个主元,将数组分为左右两个部分,根据主元所在的位置与数组长度的比较来决定继续在左边或右边继续查找中位数。这个方法的时间复杂度为O(n)。
public static int findMedian(int[] nums) {
    int len = nums.length;
    if (len % 2 == 1) { // 如果数组长度为奇数,则中位数为第 (len+1)/2 小的数
        return quickSelect(nums, 0, len - 1, (len + 1) / 2);
    } else { // 如果数组长度为偶数,则中位数为第 len/2 小和第 len/2+1 小的数的平均值
        return (quickSelect(nums, 0, len - 1, len / 2) + quickSelect(nums, 0, len - 1, len / 2 + 1)) / 2;
    }
}

private static int quickSelect(int[] nums, int left, int right, int k) {
    if (left == right) {
        return nums[left];
    }
    int pivotIndex = left + (int) (Math.random() * (right - left + 1)); // 随机选择一个枢轴元素
    pivotIndex = partition(nums, left, right, pivotIndex); // 将数组分成左右两部分,并返回枢轴元素的最终位置
    if (k == pivotIndex + 1) {
        return nums[pivotIndex];
    } else if (k < pivotIndex + 1) { // 第 k 小元素在左边部分
        return quickSelect(nums, left, pivotIndex - 1, k);
    } else { // 第 k 小元素在右边部分
        return quickSelect(nums, pivotIndex + 1, right, k);
    }
}

private static int partition(int[] nums, int left, int right, int pivotIndex) {
    int pivotValue = nums[pivotIndex];
    swap(nums, pivotIndex, right);
    int storeIndex = left;
    for (int i = left; i <= right - 1; i++) {
        if (nums[i] < pivotValue) {
            swap(nums, i, storeIndex);
            storeIndex++;
        }
    }
    swap(nums, storeIndex, right);
    return storeIndex;
}

private static void swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

  1. 堆排序法:使用两个堆,一个大根堆和一个小根堆,将数组中的元素平均分配到这两个堆中,大根堆中的元素都小于小根堆中的元素,然后根据两个堆的大小关系来决定中位数的位置。如果两个堆的大小相等,则中位数为两个堆顶元素的平均值,否则中位数为大小更大的那个堆的堆顶元素。
import java.util.PriorityQueue;

public static double findMedian(int[] nums) {
    int len = nums.length;
    PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a); // 最大堆,存储前半部分元素
    PriorityQueue<Integer> minHeap = new PriorityQueue<>(); // 最小堆,存储后半部分元素
    for (int i = 0; i < len; i++) {
        if (maxHeap.isEmpty() || nums[i] <= maxHeap.peek()) { // 如果最大堆为空,或者当前元素小于等于最大堆的堆顶元素
            maxHeap.offer(nums[i]); // 将元素插入最大堆中
            if (maxHeap.size() > minHeap.size() + 1) { // 如果最大堆的大小超过最小堆的大小1,则将最大堆的堆顶元素弹出并插入最小堆中
                minHeap.offer(maxHeap.poll());
            }
        } else { // 如果最小堆为空,或者当前元素大于等于最小堆的堆顶元素
            minHeap.offer(nums[i]); // 将元素插入最小堆中
            if (minHeap.size() > maxHeap.size()) { // 如果最小堆的大小超过最大堆的大小,则将最小堆的堆顶元素弹出并插入最大堆中
                maxHeap.offer(minHeap.poll());
            }
        }
    }
    if (len % 2 == 1) { // 如果数组长度为奇数,则中位数为最大堆的堆顶元素
        return maxHeap.peek();
    } else { // 如果数组长度为偶数,则中位数为最大堆和最小堆堆顶元素的平均值
        return (maxHeap.peek() + minHeap.peek()) / 2.0;
    }
}

这些方法都可以用来求解数组的中位数,具体选择哪种方法取决于具体的情况和需要的时间复杂度。