次序选择问题

问题描述

查找数组中第k小的值,(中位数)。

算法思路

执行一次快排,然后查找下标即可,所需要的时间复杂度接近\(O(n\log n)\)

通过观察发现子问题不需要合并,只需要分解,跟快速排序的思想一致。

  1. 选取标志,使用快速排序的方法,将大于数据的放右,小于放左。

  2. 取标志的坐标,跟k比较,有如下三种情况。
    如果这个数据前有s个数据,根据如下表达式可计算第k小的数

\[f(array, k) =\left\{ \begin{matrix} array[s],\quad k = s\\ f(array[s:end], k-s),\quad k < s\\ f(array[start:s],k),\quad k > s \end{matrix}\right. \]

  1. 对于不同情况继续递归

算法的时间复杂度接近于\(O(n)\)


python伪代码表示

def findKMaxNum(array: [] as a, start: int, end: int, k: int):
	# 这里start和end都是计算机从0开始的索引
	pivot = randChooseIndex(array) # 随机选取一个下标
	quickSortSub(a, pivot, start, end) # 以主元进行一个快速排序的子程序,把大于主元的放前面,小于的放后面
	if k > pivot - start + 1:
		return findKMaxNum(a, pivot + 1, end, k-(mid-start+1))
	elif k < pivot - start + 1:
		return findKMaxNum(a, start, pivot - 1, k)
	else: return array[pivot]

实际代码演示(以C++为例)

/// @brief 随机选取坐标,然后进行初次排序
/// @param a 数组
/// @param start 开始
/// @param end 结束位置
/// @return 排序后的标志位置
int randon_choose(int array[], int start, int end)
{
    if(start == end)
        return start;
    int pivot_pos = rand() % (end - start) + start;
    swap(array[end], array[pivot_pos]);

    int left = start;
    int right = end;
    int pivot_value = array[end];
    while (left < right)
    {
        while (left < right && array[left] < pivot_value)
        {
            left++;
        }
        array[right] = array[left];
        while (left < right && array[right] > pivot_value)
        {
            right--;
        }
        array[left] = array[right];
    }
    array[right] = pivot_value;
    return right;
}

/// @brief 随机选择, 找到第k个小的元素
/// @param array 数组
/// @param start 开始位置
/// @param end 结束位置
/// @return 第k个小的元素
int randon_selection(int array[], int start, int end, int k)
{
    int pivot_index = 0;
    int mid = randon_choose(array, start, end);
    if (k == (mid - start + 1))
    {
        return array[mid];
    }
    else if (k < (mid - start + 1))
    {
        return randon_selection(array, start, mid - 1, k);
    }
    else if (k > (mid - start + 1))
    {
        return randon_selection(array, mid + 1, end, k - (mid - start + 1));
    }
    return -1;
}

int main()
{
    int a[9] = {1, 5, 4, 2, 3, 9, 10, 22, 11};
    for (int i = 0; i < 9; i++)
    {
        printf("其中第%d个小的元素为%d\n", i, randon_selection(a, 0, 8, i));
    }

    return 0;
}

代码运行的结果为

其中第0个大的元素为1
其中第1个大的元素为2
其中第2个大的元素为3
其中第3个大的元素为4
其中第4个大的元素为5
其中第5个大的元素为9
其中第6个大的元素为10
其中第7个大的元素为11
其中第8个大的元素为22

注:这段代码每次运行的结果不同,因为每次运行的时候,计算过快,会导致部分数据值没有计算出来,为了效率直接打印结果,所以需要在每次循环结束后加上system("pause"),但是这样会导致程序运行时间变长,所以这里就不加了。

部分可能的结果如下

其中第0个大的元素为1
其中第1个大的元素为-1500844437
其中第2个大的元素为2
其中第3个大的元素为3
其中第4个大的元素为4
其中第5个大的元素为5
其中第6个大的元素为9
其中第7个大的元素为10
其中第8个大的元素为11
posted @ 2024-11-06 11:01  doctordragon666  阅读(24)  评论(1)    收藏  举报