📜  使用快速选择算法的未排序数组的中位数

📅  最后修改于: 2021-09-16 11:18:21             🧑  作者: Mango

给定一个长度为N的未排序数组arr[] ,任务是找到这个数组的中位数。
大小为 N 的排序数组的中位数定义为 n 为奇数时的中间元素和 n 为偶数时中间两个元素的平均值。
例子:

天真的方法:

  • 按升序对数组 arr[] 进行排序。
  • 如果arr[] 中的元素数为奇数,则中位数为arr[n/2]
  • 如果arr[] 中的元素数是偶数,则中位数是arr[n/2] 和 arr[n/2+1] 的平均值

上述方法的实现请参考这篇文章。
有效方法:使用随机快速选择

  • arr[] 中随机选取枢轴元素,并使用快速排序算法中的分区步骤将所有小于枢轴的元素排列在其左侧,并将大于它的元素排列在其右侧。
  • 如果在上一步之后,所选主元的位置是数组的中间,那么它就是给定数组所需的中位数。
  • 如果位置在中间元素之前,则从前一个起始索引和所选主元作为结束索引对子数组重复该步骤。
  • 如果位置在中间元素之后,则从所选主元开始并在前一个结束索引处结束对子数组重复该步骤。
  • 请注意,在元素数为偶数的情况下,必须找到中间的两个元素,它们的平均值将是数组的中位数。

下面是上述方法的实现:

C++
// CPP program to find median of
// an array
 
#include "bits/stdc++.h"
using namespace std;
 
// Utility function to swapping of element
void swap(int* a, int* b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
 
// Returns the correct position of
// pivot element
int Partition(int arr[], int l, int r)
{
    int lst = arr[r], i = l, j = l;
    while (j < r) {
        if (arr[j] < lst) {
            swap(&arr[i], &arr[j]);
            i++;
        }
        j++;
    }
    swap(&arr[i], &arr[r]);
    return i;
}
 
// Picks a random pivot element between
// l and r and partitions arr[l..r]
// around the randomly picked element
// using partition()
int randomPartition(int arr[], int l,
                    int r)
{
    int n = r - l + 1;
    int pivot = rand() % n;
    swap(&arr[l + pivot], &arr[r]);
    return Partition(arr, l, r);
}
 
// Utility function to find median
void MedianUtil(int arr[], int l, int r,
                int k, int& a, int& b)
{
 
    // if l < r
    if (l <= r) {
 
        // Find the partition index
        int partitionIndex
            = randomPartition(arr, l, r);
 
        // If partion index = k, then
        // we found the median of odd
        // number element in arr[]
        if (partitionIndex == k) {
            b = arr[partitionIndex];
            if (a != -1)
                return;
        }
 
        // If index = k - 1, then we get
        // a & b as middle element of
        // arr[]
        else if (partitionIndex == k - 1) {
            a = arr[partitionIndex];
            if (b != -1)
                return;
        }
 
        // If partitionIndex >= k then
        // find the index in first half
        // of the arr[]
        if (partitionIndex >= k)
            return MedianUtil(arr, l,
                              partitionIndex - 1,
                              k, a, b);
 
        // If partitionIndex <= k then
        // find the index in second half
        // of the arr[]
        else
            return MedianUtil(arr,
                              partitionIndex + 1,
                              r, k, a, b);
    }
 
    return;
}
 
// Function to find Median
void findMedian(int arr[], int n)
{
    int ans, a = -1, b = -1;
 
    // If n is odd
    if (n % 2 == 1) {
        MedianUtil(arr, 0, n - 1,
                   n / 2, a, b);
        ans = b;
    }
 
    // If n is even
    else {
        MedianUtil(arr, 0, n - 1,
                   n / 2, a, b);
        ans = (a + b) / 2;
    }
 
    // Print the Median of arr[]
    cout << "Median = " << ans;
}
 
// Driver program to test above methods
int main()
{
    int arr[] = { 12, 3, 5, 7, 4, 19, 26 };
    int n = sizeof(arr) / sizeof(arr[0]);
    findMedian(arr, n);
    return 0;
}


Java
// JAVA program to find median of
// an array
class GFG
{
    static int a, b;
 
    // Utility function to swapping of element
    static int[] swap(int[] arr, int i, int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        return arr;
    }
 
    // Returns the correct position of
    // pivot element
    static int Partition(int arr[], int l, int r)
    {
        int lst = arr[r], i = l, j = l;
        while (j < r)
        {
            if (arr[j] < lst)
            {
                arr = swap(arr, i, j);
                i++;
            }
            j++;
        }
        arr = swap(arr, i, r);
        return i;
    }
 
    // Picks a random pivot element between
    // l and r and partitions arr[l..r]
    // around the randomly picked element
    // using partition()
    static int randomPartition(int arr[], int l, int r)
    {
        int n = r - l + 1;
        int pivot = (int) (Math.random() % n);
        arr = swap(arr, l + pivot, r);
        return Partition(arr, l, r);
    }
 
    // Utility function to find median
    static int MedianUtil(int arr[], int l, int r, int k)
    {
 
        // if l < r
        if (l <= r)
        {
 
            // Find the partition index
            int partitionIndex = randomPartition(arr, l, r);
 
            // If partion index = k, then
            // we found the median of odd
            // number element in arr[]
            if (partitionIndex == k)
            {
                b = arr[partitionIndex];
                if (a != -1)
                    return Integer.MIN_VALUE;
            }
 
            // If index = k - 1, then we get
            // a & b as middle element of
            // arr[]
            else if (partitionIndex == k - 1)
            {
                a = arr[partitionIndex];
                if (b != -1)
                    return Integer.MIN_VALUE;
            }
 
            // If partitionIndex >= k then
            // find the index in first half
            // of the arr[]
            if (partitionIndex >= k)
                return MedianUtil(arr, l, partitionIndex - 1, k);
 
            // If partitionIndex <= k then
            // find the index in second half
            // of the arr[]
            else
                return MedianUtil(arr, partitionIndex + 1, r, k);
        }
 
        return Integer.MIN_VALUE;
    }
 
    // Function to find Median
    static void findMedian(int arr[], int n)
    {
        int ans;
        a = -1;
        b = -1;
 
        // If n is odd
        if (n % 2 == 1)
        {
            MedianUtil(arr, 0, n - 1, n / 2);
            ans = b;
        }
 
        // If n is even
        else
        {
            MedianUtil(arr, 0, n - 1, n / 2);
            ans = (a + b) / 2;
        }
 
        // Print the Median of arr[]
        System.out.print("Median = " + ans);
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int arr[] = { 12, 3, 5, 7, 4, 19, 26 };
        int n = arr.length;
        findMedian(arr, n);
    }
}
 
// This code is contributed by 29AjayKumar


Python3
# Python3 program to find median of
# an array
import random
 
a, b = None, None;
 
# Returns the correct position of
# pivot element
def Partition(arr, l, r) :
 
    lst = arr[r]; i = l; j = l;
    while (j < r) :
        if (arr[j] < lst) :
            arr[i], arr[j] = arr[j],arr[i];
            i += 1;
         
        j += 1;
 
    arr[i], arr[r] = arr[r],arr[i];
    return i;
 
# Picks a random pivot element between
# l and r and partitions arr[l..r]
# around the randomly picked element
# using partition()
def randomPartition(arr, l, r) :
    n = r - l + 1;
    pivot = random.randrange(1, 100) % n;
    arr[l + pivot], arr[r] = arr[r], arr[l + pivot];
    return Partition(arr, l, r);
 
# Utility function to find median
def MedianUtil(arr, l, r,
                k, a1, b1) :
 
    global a, b;
     
    # if l < r
    if (l <= r) :
         
        # Find the partition index
        partitionIndex = randomPartition(arr, l, r);
         
        # If partion index = k, then
        # we found the median of odd
        # number element in arr[]
        if (partitionIndex == k) :
            b = arr[partitionIndex];
            if (a1 != -1) :
                return;
                 
        # If index = k - 1, then we get
        # a & b as middle element of
        # arr[]
        elif (partitionIndex == k - 1) :
            a = arr[partitionIndex];
            if (b1 != -1) :
                return;
                 
        # If partitionIndex >= k then
        # find the index in first half
        # of the arr[]
        if (partitionIndex >= k) :
            return MedianUtil(arr, l, partitionIndex - 1, k, a, b);
             
        # If partitionIndex <= k then
        # find the index in second half
        # of the arr[]
        else :
            return MedianUtil(arr, partitionIndex + 1, r, k, a, b);
             
    return;
 
# Function to find Median
def findMedian(arr, n) :
    global a;
    global b;
    a = -1;
    b = -1;
     
    # If n is odd
    if (n % 2 == 1) :
        MedianUtil(arr, 0, n - 1, n // 2, a, b);
        ans = b;
         
    # If n is even
    else :
        MedianUtil(arr, 0, n - 1, n // 2, a, b);
        ans = (a + b) // 2;
         
    # Print the Median of arr[]
    print("Median = " ,ans);
 
 
# Driver code
arr = [ 12, 3, 5, 7, 4, 19, 26 ];
n = len(arr);
findMedian(arr, n);
 
# This code is contributed by AnkitRai01


C#
// C# program to find median of
// an array
using System;
 
class GFG
{
    static int a, b;
 
    // Utility function to swapping of element
    static int[] swap(int[] arr, int i, int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        return arr;
    }
 
    // Returns the correct position of
    // pivot element
    static int Partition(int []arr, int l, int r)
    {
        int lst = arr[r], i = l, j = l;
        while (j < r)
        {
            if (arr[j] < lst)
            {
                arr = swap(arr, i, j);
                i++;
            }
            j++;
        }
        arr = swap(arr, i, r);
        return i;
    }
 
    // Picks a random pivot element between
    // l and r and partitions arr[l..r]
    // around the randomly picked element
    // using partition()
    static int randomPartition(int []arr, int l, int r)
    {
        int n = r - l + 1;
        int pivot = (int) (new Random().Next() % n);
        arr = swap(arr, l + pivot, r);
        return Partition(arr, l, r);
    }
 
    // Utility function to find median
    static int MedianUtil(int []arr, int l, int r, int k)
    {
 
        // if l < r
        if (l <= r)
        {
 
            // Find the partition index
            int partitionIndex = randomPartition(arr, l, r);
 
            // If partion index = k, then
            // we found the median of odd
            // number element in []arr
            if (partitionIndex == k)
            {
                b = arr[partitionIndex];
                if (a != -1)
                    return int.MinValue;
            }
 
            // If index = k - 1, then we get
            // a & b as middle element of
            // []arr
            else if (partitionIndex == k - 1)
            {
                a = arr[partitionIndex];
                if (b != -1)
                    return int.MinValue;
            }
 
            // If partitionIndex >= k then
            // find the index in first half
            // of the []arr
            if (partitionIndex >= k)
                return MedianUtil(arr, l, partitionIndex - 1, k);
 
            // If partitionIndex <= k then
            // find the index in second half
            // of the []arr
            else
                return MedianUtil(arr, partitionIndex + 1, r, k);
        }
 
        return int.MinValue;
    }
 
    // Function to find Median
    static void findMedian(int []arr, int n)
    {
        int ans;
        a = -1;
        b = -1;
 
        // If n is odd
        if (n % 2 == 1)
        {
            MedianUtil(arr, 0, n - 1, n / 2);
            ans = b;
        }
 
        // If n is even
        else
        {
            MedianUtil(arr, 0, n - 1, n / 2);
            ans = (a + b) / 2;
        }
 
        // Print the Median of []arr
        Console.Write("Median = " + ans);
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        int []arr = { 12, 3, 5, 7, 4, 19, 26 };
        int n = arr.Length;
        findMedian(arr, n);
    }
}
 
// This code is contributed by PrinciRaj1992


输出:
Median = 7

时间复杂度:

  1. 最佳案例分析:O(1)
  2. 平均案例分析:O(N)
  3. 最坏情况分析:O(N 2 )

辅助空间: O(N)

想知道如何?
参考资料: 斯坦福大学

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程