• 什么是选择问题
  • 划分的思路
  • Lomuto 划分
  • 利用划分求第k小元素
  • C语言实现
  • 改进
  • 参考资料

什么是选择问题

选择问题(selection problem)是求一个n个数列表的第k个最小元素的问题。这个数字被称为第k个顺序统计量(order statistic)。当然,对于k=1或者k=n的情况,我们可以扫描整个列表,找出最小或者最大的元素。对于其他情况,我们可以对列表进行排序,然后返回第k个元素。

可是,对于整个列表进行排序是不是小题大做?因为该问题仅仅是要找出第k小的元素,而不是要求把列表从小到大排列。

划分的思路

我们可以将给定的列表根据某个值p(例如列表的第一个元素)进行划分。一般来说,这是对列表元素的重新整理,使左边部分包含所有小于等于p的元素,紧接着是中轴(pivot)p本身,再接着是所有大于等于p的元素。如下图所示

有两种主要的划分方法,本文讨论 Lomuto 划分,以后会介绍更有名的 Hoare 划分。

Lomuto 划分

Lomuto(洛穆托)划分的伪代码如下:

// 算法:Lomuto_Partition(A[l..r])
// 用第一个元素作为中轴对子数组进行划分
// 输入:数组A[0..n-1]的一个子数组A[l..r],它由左右两边的索引l和r(l<=r)定义
// 输出:A[l..r]的划分和中轴的新位置p = A[l]
s = l
for i=l+1 to r doif A[i] < ps = s+1;swap(A[s], A[i])
swap(A[l], A[s])
return s

利用划分求第k小元素

我们如何利用划分列表来寻找其第k小元素呢?

假设列表是以数组实现的,其元素索引从0开始,那么第k小的元素就是把此列表从小到大排序后,索引在k-1位置上的元素。

假设首次划分此列表,s是分割位置,也就是划分后中轴元素的索引。我们分3种情况进行讨论:

[1]. 当s=k-1 ,那么中轴p本身显然就是第k小的元素;

[2]. 如果s>k-1,那么整个列表的第k小元素就是左边部分的第k小元素;

[3]. 如果s<k-1,那么问题就转换为求右边部分的第(k-s-1)小元素;推导过程是这样的:本来是求第k小,通过划分,筛除了最前面的(s+1)个元素,所以只用求右边部分(蓝色)的第 k-(s+1)小。

可以看出,第2种情况和第3种情况虽然没有彻底解决问题,但是使问题的实例变小了。对于这个较小的实例可以用同样的方法来解决,即递归求解。这个算法被称为“快速选择”,在算法思想中属于减可变规模算法(减治法的一种)。

C语言实现

// 交换*a和*b
void swap(int* a, int* b)
{int temp = *a;*a = *b;*b = temp;
}
// a是数组首地址,l和r分别是两端的索引,要求l<=r
int Lomuto_partition(int a[], int l, int r)
{int pivot = a[l];int j = l;for(int i = l + 1; i <= r; ++i){if( a[i] < pivot ){swap( &a[++j], &a[i]);}}swap(&a[l], &a[j]);return j;   // j是下标
}// 返回第k小的元素值。
// 注意:k不是下标,表示第k个
int __quick_select(int a[], int l, int r, int k)
{// s是分裂点,中轴的下标int s = Lomuto_partition(a, l, r);if(s == l+k-1)return a[s];else if( s > (l+k-1) )// 处理左边的部分return __quick_select(a, l, s-1, k);else // 处理右边的部分return __quick_select(a, s+1, r, k-(s-l+1));
}// 快速选择主函数
int quick_select_min(int a[], int len, int k)
{return __quick_select(a, 0, len-1, k);
}

测试代码如下:

#include <stdio.h>
#include <stdlib.h>int main(int argc,char *argv[])
{int k = atoi(argv[1]); //把命令行输入的字符串转为整数printf("k = %d \n",k);int array[] = {9,9,8,7,6,5,4,3,3,3,2,2,2,1,1,0,}; int len = sizeof array/sizeof array[0];printf("%dth min = %d\n", k, quick_select_min(array,len,k) );return 0;
}

运行截图:

改进

该算法也可以不用递归实现。在非递归版本中,甚至不需要调整k的值,只要一直调用Lomuto_partition,直到s=k-1为止。

代码如下:

int quick_select_min_2(int a[], int len, int k)
{int left = 0;int right = len - 1;int s;// 直到返回的下标是 k-1 为止while( (s = Lomuto_partition(a, left, right)) != (k-1) ){if(s < k-1)left = s+1; //处理右边的部分elseright = s-1; // 处理左边的部分}return a[s];
}

参考资料

《算法设计与分析基础(第3版)》(清华大学出版社)

选择问题(求第k个最小元素)相关推荐

  1. C语言在二叉搜索树找到第k个最小元素(附完整源码)

    C语言在二叉搜索树找到第k个最小元素 C语言在二叉搜索树找到第k个最小元素完整源码(定义,实现,main函数测试) C语言在二叉搜索树找到第k个最小元素完整源码(定义,实现,main函数测试) #in ...

  2. POJ 1442 Black Box(大小堆,求第K小的元素)

    文章目录 1. 题目链接 2. 题目解读 3. 代码 3.1 Runtime Error 代码 1. 题目链接 http://poj.org/problem?id=1442 2. 题目解读 可以利用大 ...

  3. 二叉搜索树中第k大元素_二叉搜索树中第K个最小元素

    二叉搜索树中第k大元素 Problem statement: 问题陈述: Find the k-th smallest element in a given binary search tree (B ...

  4. 【运筹学】表上作业法 ( 求初始基可行解 | 最小元素法 )

    文章目录 一.表上作业法 第一步 : 确定初始基可行解 二.最小元素法 一.表上作业法 第一步 : 确定初始基可行解 运输问题如下 : 下面的表格代表 333 个产地 , 444 个销地 的运输规划问 ...

  5. 求旋转数组的最小元素

    (把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个排好序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个 ...

  6. 【运筹学】运输规划、表上作业法总结 ( 运输规划模型 | 运输规划变量个数 | 表上作业法 | 最小元素法 | 差额 Vogel 法 ★ | 闭回路法 ) ★★★

    文章目录 一.运输规划模型 1.产销平衡模型 2.产销不平衡模型 二.运输规划数学模型变量个数 三.表上作业法 四.表上作业法 : 求初始基可行解 1.最小元素法 2.差额法 ( Vogel ) 推荐 ...

  7. 【运筹学】表上作业法 ( 示例 | 使用 “ 最小元素法 “ 找初始基可行解 )

    文章目录 一.运输规划问题 二.找初始基可行解 一.运输规划问题 运输规划问题 : B1\rm B_1B1​ B1\rm B_1B1​ B1\rm B_1B1​ B1\rm B_1B1​ 产量 A1\ ...

  8. 【运筹学】表上作业法 ( 最小元素法分析 | Vogel 方法 )

    文章目录 一." 最小元素法 " 分析 二.Vogel 方法 ( 差额法 ) 一." 最小元素法 " 分析 在上一篇博客 [运筹学]表上作业法 ( 求初始基可行 ...

  9. 73. Leetcode 230. 二叉搜索树中第K小的元素 (二叉搜索树-中序遍历类)

    给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数).示例 1:输入:root = [3,1,4,null,2], k = 1 输出 ...

最新文章

  1. jq修改iframe html代码,jQuery控制iFrame(实例代码)
  2. git是航空母舰:ScrumBoard
  3. qml 自定义消息框_Qt qml 自定义消息提示框
  4. React创建组件的三种方式及其区别
  5. 安卓逆向工具apkide安装
  6. java setter_java – 如何获得@getter和@setter?
  7. java高分面试指南:javamvc模式简单案例
  8. python 字典添加元素乱序了_Python有序字典的两个小“惊喜”
  9. mysql addslashes c_addslashes()用途与php怎样防止mysql注入?
  10. [导入]CommunityServer 1.1 源码及汉化文件
  11. ac9560不支持承载_工程人还在为松软土层地质问题处理而烦恼?不,这里告诉你!...
  12. 向量的范数(有例子,简单好理解)
  13. 3DMAX全景漫游制作渲染教程,超详细的教程。
  14. 【软件测试基础】软件测试模式
  15. 支撑向量机作业(SVM)
  16. 乱世王者服务器维护,乱世王者微信541区风平浪静开服时间表_乱世王者新区开服预告_第一手游网手游开服表...
  17. 微信改微信号连接服务器,微信修改不了微信号是为什么 微信号怎么修改第二次最新强行修改教程...
  18. 【二叉树】用python实现AVL树
  19. 中药材经营需要哪些证件
  20. opencv 去除背景

热门文章

  1. nyoj-469--擅长排列的小明 II
  2. NYOJ 456 邮票分你一半
  3. 【转】ssh服务器启动和客户端常用操作
  4. 【HNOI模拟By YMD】move
  5. 关于 CSS3 backface-visiable 与 overflow 属性的冲突
  6. 如何屏蔽TCP 报头中的内容-位置字段中显示的 IP 地址
  7. 方差为平方的均值减去均值的平方
  8. 机器学习导论(张志华):正定核性质
  9. Mathematica常用命令
  10. RHEL6 64bit下更改YUM配置。yum this system is not registered with rhn的解决办法