选择问题(求第k个最小元素)
- 什么是选择问题
- 划分的思路
- 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个最小元素)相关推荐
- C语言在二叉搜索树找到第k个最小元素(附完整源码)
C语言在二叉搜索树找到第k个最小元素 C语言在二叉搜索树找到第k个最小元素完整源码(定义,实现,main函数测试) C语言在二叉搜索树找到第k个最小元素完整源码(定义,实现,main函数测试) #in ...
- POJ 1442 Black Box(大小堆,求第K小的元素)
文章目录 1. 题目链接 2. 题目解读 3. 代码 3.1 Runtime Error 代码 1. 题目链接 http://poj.org/problem?id=1442 2. 题目解读 可以利用大 ...
- 二叉搜索树中第k大元素_二叉搜索树中第K个最小元素
二叉搜索树中第k大元素 Problem statement: 问题陈述: Find the k-th smallest element in a given binary search tree (B ...
- 【运筹学】表上作业法 ( 求初始基可行解 | 最小元素法 )
文章目录 一.表上作业法 第一步 : 确定初始基可行解 二.最小元素法 一.表上作业法 第一步 : 确定初始基可行解 运输问题如下 : 下面的表格代表 333 个产地 , 444 个销地 的运输规划问 ...
- 求旋转数组的最小元素
(把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个排好序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个 ...
- 【运筹学】运输规划、表上作业法总结 ( 运输规划模型 | 运输规划变量个数 | 表上作业法 | 最小元素法 | 差额 Vogel 法 ★ | 闭回路法 ) ★★★
文章目录 一.运输规划模型 1.产销平衡模型 2.产销不平衡模型 二.运输规划数学模型变量个数 三.表上作业法 四.表上作业法 : 求初始基可行解 1.最小元素法 2.差额法 ( Vogel ) 推荐 ...
- 【运筹学】表上作业法 ( 示例 | 使用 “ 最小元素法 “ 找初始基可行解 )
文章目录 一.运输规划问题 二.找初始基可行解 一.运输规划问题 运输规划问题 : B1\rm B_1B1 B1\rm B_1B1 B1\rm B_1B1 B1\rm B_1B1 产量 A1\ ...
- 【运筹学】表上作业法 ( 最小元素法分析 | Vogel 方法 )
文章目录 一." 最小元素法 " 分析 二.Vogel 方法 ( 差额法 ) 一." 最小元素法 " 分析 在上一篇博客 [运筹学]表上作业法 ( 求初始基可行 ...
- 73. Leetcode 230. 二叉搜索树中第K小的元素 (二叉搜索树-中序遍历类)
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数).示例 1:输入:root = [3,1,4,null,2], k = 1 输出 ...
最新文章
- jq修改iframe html代码,jQuery控制iFrame(实例代码)
- git是航空母舰:ScrumBoard
- qml 自定义消息框_Qt qml 自定义消息提示框
- React创建组件的三种方式及其区别
- 安卓逆向工具apkide安装
- java setter_java – 如何获得@getter和@setter?
- java高分面试指南:javamvc模式简单案例
- python 字典添加元素乱序了_Python有序字典的两个小“惊喜”
- mysql addslashes c_addslashes()用途与php怎样防止mysql注入?
- [导入]CommunityServer 1.1 源码及汉化文件
- ac9560不支持承载_工程人还在为松软土层地质问题处理而烦恼?不,这里告诉你!...
- 向量的范数(有例子,简单好理解)
- 3DMAX全景漫游制作渲染教程,超详细的教程。
- 【软件测试基础】软件测试模式
- 支撑向量机作业(SVM)
- 乱世王者服务器维护,乱世王者微信541区风平浪静开服时间表_乱世王者新区开服预告_第一手游网手游开服表...
- 微信改微信号连接服务器,微信修改不了微信号是为什么 微信号怎么修改第二次最新强行修改教程...
- 【二叉树】用python实现AVL树
- 中药材经营需要哪些证件
- opencv 去除背景