相关介绍:

 给定一个数组,找出该数组中第n大的元素的值。其中,1<=n<=length。例如,给定一个数组A={2,3,6,5,7,9,8,1,4},当n=1时,返回9。解决该问题的算法有三种。依据其时间复杂度的高低,分别对其进行讲解

第一种:时间复杂度为O(NlogN)

 解决该问题,容易想到的一个办法是,先对数组按元素值从大到小的方式进行排序,之后选取出其符合要求的元素并返回其值。由基于比较的排序算法的时间复咋读,其下界为NlogN,为此,解决该问题的时间复杂度为O(NlogN)。

示例代码:

/**
* 思路:排序数组,并寻找一个原本乱序的数组中的第th大的元素
* 平均时间复杂度视具体的排序算法而定,一般是O(nlogn)
* @param th 第th大的元素,即要找的那个元素在数组中的真实的下标的元素索引
* @param low 数组的元素下标,表示需要进行排序的数组元素的开始的下标
* @param high 数组元素的下标,表示需要进行排序的数组元素的结束的下标
* @return 返回第th大的那个数组元素
*/
public int sort(int th)
{
Arrays.sort(array);
return array[th];
}

第二种:平均情况下时间复杂度为O(N)

 该算法运用了快速排序的思想,由于在快速排序的思想中,每次确定一个数组中元素的正确位置,为此,可以通过比较每次查找出来的元素在数组中的正确位置和想要进行查找的位置的元素之间的关系,就可排除掉一部分元素,从而逼近答案。在最优的情况下,每次能够排除掉N/2个数组元素,为此,可以根据主定理法计算得出,其平均情况下,时间复杂度为O(N)

示例代码:

/**
* 思路:使用快速排序的思想,去寻找一个乱序的数组中的第th大的元素
* 由于快速排序的思想中,每次确定一个元素在数组中的正确位置,为此,通过比较每次
* 查找出来的元素在数组中的正确位置和想要进行查找的位置的元素之间的关系,可以排除
* 掉一部分元素,从而逼近答案。在最优的情况下,每次能够排除掉N/2个数组元素
* 平均时间复杂度为O(n)
* @param th 第th大的元素,即要找的那个元素在数组中的真实的下标的元素索引
* @param low 数组的元素下标,表示需要进行排序的数组元素的开始的下标
* @param high 数组元素的下标,表示需要进行排序的数组元素的结束的下标
* @return 返回第th大的那个数组元素
*/
public int QuicklySortedMind(int th,int low,int high)
{
int lows=low;
int highs=high;
//选择该元素为基准元素
int midNumber=array[low];
//以下为快速排序部分,用于将元素midNumber的值插入到数组的正确位置中
while(low<high)
{
while(array[high]>midNumber&&low<high)
high--;
if(low<high)
{
array[low]=array[high];
}
while(array[low]<midNumber&&low<high)
low++;
if(low<high)
{
array[high]=array[low];
}
}
array[low]=midNumber;
//用于递归的寻找第n大的元素
//当找到的那个中间的元素为要找的第th大的元素的时候,将其直接进行返回
//ps:关键点在于,low的值即使是递归查找出来的,其依然为数组的绝对索引值
if(th==low)
return array[low];
//当找到的元素的位置在想要找的元素的位置的左边时,从右边继续寻找
else if(th>low)
return QuicklySortedMind(th,low+1,highs);
//当找到的元素的位置在想要找的元素的位置的右边时,从左边继续寻找
else
return QuicklySortedMind(th,lows,low-1);
}

第三种:时间复杂度为O(N)

 该算法根据以上快速排序的思想进行改进,由于每次选择的元素找到其在数组中的位置时,其不一定能够恰好排除掉N/2个元素,在最坏的情况下,每次只能排除掉一个数组元素。为此,第二种解决办法在最坏的情况下可能达到O(N^2)。而该算法,就是基于解决不能够排除掉N/2个数组元素的情况下提出的。在数组元素中找到一个划分基准,使其按照这个基准所划分出的两个子数组的长度都至少为原数组长度的v倍(0< v< 1),则在最坏情况下,其也能用O(N)的时间完成任务。该算法的基本思路如下:对数组中的元素进行分组(每组元素的个数为5个),之后找到各组中的中位数,之后再找到中位数的中位数,将中位数的中位数那个元素根据快速排序的思想,找到其在数组中的正确的位置,使得其每次都能够排除掉一定数量的数组元素,从而逼近答案。

示例代码如下:

/**
* 采用线性时间选择的算法找出第th大的元素的值
* 线性时间选择算法的思想为:
* 线性时间选择算法是对上面那个基于快速选择排序算法查找第n大的数组元素的改进。具体思想如下:
* 对数组中的元素进行分组(每组元素的个数为5个),之后找到各组中的中位数,之后再找到中位数的中位数
* 将中位数的中位数那个元素根据快速排序的思想,找到其在数组中的正确的位置
* 使得其每次都能够排除掉一定的数组元素,从而逼近答案
* @param th 第th大的元素
* @param low 数组的元素下标,表示需要进行排序的数组元素的开始的下标
* @param high 数组元素的下标,表示需要进行排序的数组元素的结束的下标
* @return 返回第th大的那个数组元素
*
*/
public int LinedSelectFind(int th,int low,int high)
{
//当数组元素个数小于5个的时候,即只有一组元素的时候,将其进行排序,之后直接返回
if(high-low<5)
{
Arrays.sort(array,low,high);
return array[low+th-1];
}
//变量i用于控制数组元素分组的迭代
//每组5个元素,为此,总共的组别数目为(high-low-4)/5组(舍弃掉数组元素不足5个的组别,不考虑元素个数不足5个的分组)
//其等价与i<(high-low)/5
for(int i=0;i<=(high-low-4)/5;i++)
{
//变量s为每组元素的第一个元素的下标(即索引),t为每组元素的最后一个元素的下标(即索引)
int s=low+5*i,t=s+4;
/*for(int j=0;j<3;j++)
{*/
//用于排序s到t之间的数组中的每组元素的前三个数组元素
Arrays.sort(array,s,t-2);
//用于将每组元素中的中位数按照各组之间的区别,
//在原数组(low到high之间的数组)中从头开始排,即原数组(low到high之间的数组)中
// 前(high-low-4)/5个元素为各个分组的中位数
swap(array,low+i,s+2);
/*
}
*/
}
//选择出中位数的中位数,其中在low到low+(high-low-4)/5这个范围内,其中、中位数为第(high-low+6)/10大的元素
int x=LinedSelectFind((high-low+6)/10,low,low+(high-low-4)/5);
//变量i为中位数的中位数那个数在数组中所在的下标位置,变量j为相对low到high这个范围的数组,元素x为其第几大的元素
int i=partition(low,high,x),j=i-low+1;
//进行递归查找,直到数组元素小于5个的时候
//当th在j的左边或者相等的时候,在low到i之间继续寻找第th大的元素
if(th<=j)
return LinedSelectFind(th,low,i);
//当th在j的右边的时候,在i+1到high之间,继续寻找第th-j大的元素
else
return LinedSelectFind(th-j,i+1,high);
}
//将元素x放置到p到r之间的数组元素的正确位置,并返回元素x所在的下标
private int partition(int p,int r,int x)
{
int i=p,j=r+1;
while(true)
{
while(array[++i]<x&&i<r);
while(array[--j]>x);
if(i>=j)
break;
swap(array,i,j);
}
array[p]=array[j];
array[j]=x;
return j;
}
//交换数组array中的下标为index1和index2的两个数组元素
private void swap(int[] array,int index1,int index2)
{
int temp=array[index1];
array[index1]=array[index2];
array[index2]=temp;
}

代码汇总如下:

package other;

import java.util.Arrays;

/**
* 该类用于演示在一个无序的数组中寻找第n大的数的三个算法
* @author 学徒
*
*/ public class FindTHNumber
{
int[] array=null;
public static void main(String[] args)
{
FindTHNumber n=new FindTHNumber();
n.array=new int[]{1,3,2,4,5,6};
int low=0;
int high=n.array.length-1;
int th=6;
System.out.println(n.QuicklySortedMind(th-1, low, high));
System.out.println(n.sort(th-1));
System.out.println(n.LinedSelectFind(th, low, high));
}
/**
* 思路:排序数组,并寻找一个原本乱序的数组中的第th大的元素
* 平均时间复杂度视具体的排序算法而定,一般是O(nlogn)
* @param th 第th大的元素,即要找的那个元素在数组中的真实的下标的元素索引
* @param low 数组的元素下标,表示需要进行排序的数组元素的开始的下标
* @param high 数组元素的下标,表示需要进行排序的数组元素的结束的下标
* @return 返回第th大的那个数组元素
*/
public int sort(int th)
{
Arrays.sort(array);
return array[th];
}
/**
* 思路:使用快速排序的思想,去寻找一个乱序的数组中的第th大的元素
* 由于快速排序的思想中,每次确定一个元素在数组中的正确位置,为此,通过比较每次
* 查找出来的元素在数组中的正确位置和想要进行查找的位置的元素之间的关系,可以排除
* 掉一部分元素,从而逼近答案。在最优的情况下,每次能够排除掉N/2个数组元素
* 平均时间复杂度为O(n)
* @param th 第th大的元素,即要找的那个元素在数组中的真实的下标的元素索引
* @param low 数组的元素下标,表示需要进行排序的数组元素的开始的下标
* @param high 数组元素的下标,表示需要进行排序的数组元素的结束的下标
* @return 返回第th大的那个数组元素
*/
public int QuicklySortedMind(int th,int low,int high)
{
int lows=low;
int highs=high;
//选择该元素为基准元素
int midNumber=array[low];
//以下为快速排序部分,用于将元素midNumber的值插入到数组的正确位置中
while(low<high)
{
while(array[high]>midNumber&&low<high)
high--;
if(low<high)
{
array[low]=array[high];
}
while(array[low]<midNumber&&low<high)
low++;
if(low<high)
{
array[high]=array[low];
}
}
array[low]=midNumber;
//用于递归的寻找第n大的元素
//当找到的那个中间的元素为要找的第th大的元素的时候,将其直接进行返回
//ps:关键点在于,low的值即使是递归查找出来的,其依然为数组的绝对索引值
if(th==low)
return array[low];
//当找到的元素的位置在想要找的元素的位置的左边时,从右边继续寻找
else if(th>low)
return QuicklySortedMind(th,low+1,highs);
//当找到的元素的位置在想要找的元素的位置的右边时,从左边继续寻找
else
return QuicklySortedMind(th,lows,low-1);
}
/**
* 采用线性时间选择的算法找出第th大的元素的值
* 线性时间选择算法的思想为:
* 线性时间选择算法是对上面那个基于快速选择排序算法查找第n大的数组元素的改进。具体思想如下:
* 对数组中的元素进行分组(每组元素的个数为5个),之后找到各组中的中位数,之后再找到中位数的中位数
* 将中位数的中位数那个元素根据快速排序的思想,找到其在数组中的正确的位置
* 使得其每次都能够排除掉一定的数组元素,从而逼近答案
* @param th 第th大的元素
* @param low 数组的元素下标,表示需要进行排序的数组元素的开始的下标
* @param high 数组元素的下标,表示需要进行排序的数组元素的结束的下标
* @return 返回第th大的那个数组元素
*
*/
public int LinedSelectFind(int th,int low,int high)
{
//当数组元素个数小于5个的时候,即只有一组元素的时候,将其进行排序,之后直接返回
if(high-low<5)
{
Arrays.sort(array,low,high);
return array[low+th-1];
}
//变量i用于控制数组元素分组的迭代
//每组5个元素,为此,总共的组别数目为(high-low-4)/5组(舍弃掉数组元素不足5个的组别,不考虑元素个数不足5个的分组)
//其等价与i<(high-low)/5
for(int i=0;i<=(high-low-4)/5;i++)
{
//变量s为每组元素的第一个元素的下标(即索引),t为每组元素的最后一个元素的下标(即索引)
int s=low+5*i,t=s+4;
/*for(int j=0;j<3;j++)
{*/
//用于排序s到t之间的数组中的每组元素的前三个数组元素
Arrays.sort(array,s,t-2);
//用于将每组元素中的中位数按照各组之间的区别,
//在原数组(low到high之间的数组)中从头开始排,即原数组(low到high之间的数组)中
// 前(high-low-4)/5个元素为各个分组的中位数
swap(array,low+i,s+2);
/*
}
*/
}
//选择出中位数的中位数,其中在low到low+(high-low-4)/5这个范围内,其中、中位数为第(high-low+6)/10大的元素
int x=LinedSelectFind((high-low+6)/10,low,low+(high-low-4)/5);
//变量i为中位数的中位数那个数在数组中所在的下标位置,变量j为相对low到high这个范围的数组,元素x为其第几大的元素
int i=partition(low,high,x),j=i-low+1;
//进行递归查找,直到数组元素小于5个的时候
//当th在j的左边或者相等的时候,在low到i之间继续寻找第th大的元素
if(th<=j)
return LinedSelectFind(th,low,i);
//当th在j的右边的时候,在i+1到high之间,继续寻找第th-j大的元素
else
return LinedSelectFind(th-j,i+1,high);
}
//将元素x放置到p到r之间的数组元素的正确位置,并返回元素x所在的下标
private int partition(int p,int r,int x)
{
int i=p,j=r+1;
while(true)
{
while(array[++i]<x&&i<r);
while(array[--j]>x);
if(i>=j)
break;
swap(array,i,j);
}
array[p]=array[j];
array[j]=x;
return j;
}
//交换数组array中的下标为index1和index2的两个数组元素
private void swap(int[] array,int index1,int index2)
{
int temp=array[index1];
array[index1]=array[index2];
array[index2]=temp;
}
}

回到目录|·(工)·)

K:找寻数组中第n大的数组元素的三个算法的更多相关文章

  1. 无序数组中第Kth大的数

    题目:找出无序数组中第Kth大的数,如{63,45,33,21},第2大的数45. 输入: 第一行输入无序数组,第二行输入K值. 该是内推滴滴打车时(2017.8.26)的第二题,也是<剑指of ...

  2. 求一无序数组中第n大的数字 - 快速选择算法

    逛别人博客的时候,偶然看到这一算法题,顺便用C++实现了一下. 最朴素的解法就是先对数组进行排序,返回第n个数即可.. 下面代码中用的是快速选择算法(不晓得这名字对不对) #include <v ...

  3. extract_by_one 根据二维数组中某字段来提取数组信息,查看有无重复信息

    public function tt(){ $param = array( array ( 'hykno' => '2222222-CB', 'tcdk_fid' => '458B6D70 ...

  4. 算法练习之合并两个有序链表, 删除排序数组中的重复项,移除元素,实现strStr(),搜索插入位置,无重复字符的最长子串

    最近在学习java,但是对于数据操作那部分还是不熟悉 因此决定找几个简单的算法写,用php和java分别实现 1.合并两个有序链表 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两 ...

  5. JavaScript数组中出现的次数最多的元素

    var arr = [1,-1,2,4,5,5,6,7,5,8,6]; var maxVal = arr[0]; // 数组中的最大值 var minVal = arr[0]; // 数组中的最小值 ...

  6. 《剑指offer》第五十三题(数组中数值和下标相等的元素)

    // 面试题53(三):数组中数值和下标相等的元素 // 题目:假设一个单调递增的数组里的每个元素都是整数并且是唯一的.请编程实 // 现一个函数找出数组中任意一个数值等于其下标的元素.例如,在数组{ ...

  7. LeetCode 80 Remove Duplicates from Sorted Array II(移除数组中出现两次以上的元素)

    题目链接:https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/#/description 给定一个已经排好序的数组 ...

  8. javascript 数组中出现的次数最多的元素

    javascript 数组中出现的次数最多的元素 var arr = [1,-1,2,4,5,5,6,7,5,8,6]; var maxVal = arr[0]; // 数组中的最大值 var min ...

  9. 剑指offer——58数组中数值和下标相等的元素

    题目三: 数组中数值和下标相等的元素. 假设一个单调递增的数组里的每个元素都是整数并且是唯一的.请编程实现一个函数,找出数组中任意一个数值等于其下标的元素.例如,在数组{-3,-1,1,3,5}中,数 ...

随机推荐

  1. android learning

    https://www.cnblogs.com/kangjianwei101/p/5621238.html https://blog.csdn.net/write6/article/details/7 ...

  2. 总结day3 ---- 进制转换,字符串切片,字符串常用方法.,for 循环,

    前情提要: int 的相关操作 进制转换 bit_lenth() str 的索引,以及常用的相关方法 for 循环 索引 切片 相关方法 一  : int 的相关操作 int 主要用于生活中的计算问题 ...

  3. dbporxy-mysql 协议流转图

    dbproxy 支持 in 查询, 当in 中的字段 属于不同的分表时, QPS约为 5000左右, 如果为 等值查询,  qps的30000左右 主要原因是 对于in操作,会产生多个不同分表的sql ...

  4. 基于聚类的“图像分割”(python)

    基于聚类的“图像分割” 参考网站: https://zhuanlan.zhihu.com/p/27365576 昨天萌新使用的是PIL这个库,今天发现机器学习也可以这样玩. 视频地址Python机器学 ...

  5. LoginForm表单的执行过程

    读取这篇文章,您将了解到 提前熟悉几个基础点 LoginForm表单的执行过程 首先我们看表单模型 声明验证规则 填充模型 触发验证 默认的用户密码加密 用户验证中使用Salt 数据验证 调试Yii ...

  6. media(适配)

    媒体类型    1.all 所有媒体     2.braille 盲文触觉设备     3.embossed 盲文打印机     4.print 手持设备      5.projection 打印预览 ...

  7. 使用EditPlus编辑Linux上的文本文件

    在Linux上我们都使用vim 或者vi命令对文件进行编辑,但是我们习惯的一般都是windows系统, 那么怎么才能像在windows上一样编辑我们Linux上的文件呢?下面我们就来看看如何使用 wi ...

  8. 关于Input输入框蓝色外框的操作

    1.去掉input外框的css: input { outline : none; //去掉外框 //outline:medium; } input:focus { outline : none; } ...

  9. Window10安装Django,并创建第一个Django项目

    1.在cmd中输入pip install Django==1.11.7,安装的版本为:1.11.7. 2.安装完成后输入: >>> import django >>> ...

  10. 11 java 线程池 使用实例

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...