算法思想:分治法
实际问题:线性时间选择
编写语言:Java
问题描述
给定线性序集中 n 个元素和一个整数 k, 1 <= k <= n, 要求找出这 n 个元素中第 k 小的元素。即如果将这 n 个元素依其线性序排列时,排在第 k 个位置的元素即为要找的元素。
本篇博文代码会用到 分治法-快速排序 博文中用到的基准选择方法。
方法使用位置:代码第 25 行
方法实现位置:代码第 37 - 68 行
Java代码
import java.util.Random;
public class RandomizedSelect
{
public static void main(String[] args)
{
int[] a = new int[] {1, 3, 2, 6, 5, 8, 4, 9, 7, 0};
int t = select(a, 0, a.length - 1, 4); //选出第四大的数
System.out.println(t);
}
//参数含义:a为待查询的数组,low为起点,high为终点,target为带查询的目标
public static int select(int[] a, int low, int high, int target)
{
if(low == high)
return a[low];
//将数组以i为基准分为两部分,左边的都小于i,右边的都大于i
//此处会用到快速排序算法中的划分方法来找基准
int i = randPart(a, low, high);
int length = i - low + 1; //数组左半部分的长度
//如果第target小的数小于等于左半部分的长度,则这个数在此部分内
if(target <= length)
return select(a, low, i, target);
//如果第target小的数大于左半部分的长度,则这个数在右半部分内,
//且左半部分的数都小于第target小的数
else
return select(a, i + 1, high, target - length);
}
//划分寻找基准-随机化优化
public static int randPart(int[] a, int low, int high)
{
Random r = new Random();
//随机产生一个 low 到 high 的整数
int flag = low + r.nextInt(high - low + 1);
int pivot = a[flag];
//实际还是相当于以a[0]为基准
a[flag] = a[low];
a[low] = pivot;
while(low < high)
{
//1、从右往左找比基准小的数
while(low < high && a[high] > pivot)
high--;
if(low < high)
a[low] = a[high];
//2、从左往右找比基准大的数
while(low < high && a[low] <= pivot)
low++;
if(low < high)
a[high] = a[low];
}
//将pivot放到low和high相遇的地方
a[high] = pivot;
return high;
}
}