题目描述:给定线性序列集中n个元素和一个整数k,1<=k<=n,要求找出这n个元素中第k小的元素。

题目分析:当k=1时可以看做要找的就是最小的元素;当k=n时,就是要找到最大元素;当k=(n+1)/ 2时,称为找中位数。

思路分析:使用分治算法的思想,随机找一个基准值Key(随机的原因是划分的效率与基准值左右的对称性有关,所以在随机的过程中可以使期望划分是较对

称的)将所有小于Key的值放在集合的左边,大于Key的值放在集合的右边。然后统计在Key左边的元素共有N个。

如果k<=N,则可以判断第k小的元素所处的位置在Key的左边,然后只需要找Key左边的元素第K小的就行了,然后继续随机选择基准值Key,根据基准值划分

集合,一直递归。如果k>N则可以断定此集合中第K小的元素再基准值Key的右边,所以这个时候只需要求Key右边元素的第K-N小的元素即可。经过以上递归

过程最终可以找到该元素。以下附上Java代码做参考使用。

 1 package test;
 2 import java.util.Random;
 3 /**
 4  * 线性时间选择问题:找出一个包含n个元素的集合中的第K小的元素
 5  */
 6 public class LinerSelect {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) {
12         
13         int[] elements={1,5,2,5,8,45,15,26};
14         int b=KthMin(elements, 0, 7, 3);
15         System.out.print("第三小的元素为:"+b);
16     }
17     //寻找第k小的元素
18     public static int KthMin(int[] elements,int start,int end,int k)
19     {
20         if(start==end)return elements[start]; 
21         int i=index(elements, start, end);
22         int j=i-start+1;//统计比随机基准值小的元素个数
23         if(k<=j)
24             return KthMin(elements, start, i, k);
25         else {
26             return KthMin(elements, i+1, end, k-j);
27         }
28     }
29     //随机将集合中的元素分为两部分,中间元素。     
30     private static int index(int[] elements,int start,int end)
31     {
32         Random random=new Random();
33         int i=random.nextInt(end-start+1)+start;
34         swap(elements, i, start);
35         return Partition(elements, start, end);
36     }
37     //以第一个元素为基准值将集合分为左右两部分
38     private static int Partition(int[] a,int start,int end)
39     {
40         //i为从开头比较的元素下标,j为从末尾开始的元素下标
41         int i=start+1,j=end;
42         int key=a[start];
43         while(true)
44         {
45             while(a[i]<key&&i<end)
46             {
47                 i++;
48             }
49             while(a[j]>key)
50             {
51                 j--;
52             }
53             if(i>=j)
54                 break;
55             swap(a, i, j);
56         }
57         a[start]=a[j];
58         a[j]=key;
59         return j;
60         
61     }
62     private static void swap(int[] a,int i,int j)
63     {
64         int temp=a[i];
65         a[i]=a[j];
66         a[j]=temp;
67     }
68 }

运行截图:

 

在将集合基于基准值划分为左右两部分时借鉴了快速排序中的算法思想,在代码中使用方法Partition(int[],int,int)实现。

在此算法中,最坏情况下,算法需要O(n^2)的计算时间。比如当需要寻找最小元素时,但是随机划分一直是从最大的元素开始的。可以将此情况做特殊处理,尽管是不必要的

但是总体来说此算法性能还是不错的。

posted on 2018-03-11 21:43 小菜心儿 阅读() 评论() 编辑 收藏
版权声明:本文为xiaocaihua原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiaocaihua/p/8546299.html