相关介绍:

 给定一个数组,找出该数组中第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. Metasploit域渗透测试全程实录(终结篇)

    本文作者:i春秋签约作家——shuteer 前言 内网渗透测试资料基本上都是很多大牛的文章告诉我们思路如何,但是对于我等小菜一直是云里雾里.于是使用什么样的工具才内网才能畅通无阻,成了大家一直以来的渴 ...

  2. 南昌网络赛 Distance on the tree 主席树+树剖 (给一颗树,m次查询ui->vi这条链中边权小于等于ki的边数。)

    https://nanti.jisuanke.com/t/38229 题目: 给一颗树,m次查询ui->vi这条链中边权小于等于ki的边数. #include <bits/stdc++.h ...

  3. mysql数据库使用脚本实现分库备份过程

    一条命令解决分库分表备份: [root@db01 data]# mysql -uroot -p123456 -e "show databases;"|egrep -v " ...

  4. xilinx DMA IP核(二) —— 文档阅读

    本笔记不记录DMA的Scatter/Gather特性.DMA上有三种总线:AXI4-LIte(对寄存器进行配置),AXI4-Memory Map(用于与内存交互)和AXI4 Stream(用于与外设交 ...

  5. 认识CSS中字体图标

    前端之HTML,CSS(十一) 字体图标 使用文字做出小图标的效果并超越了小图标应用精灵图,使得图标变得灵活,减少了请求次数,优化了界面的性能.字体图标本身为矢量图. 字体图标的使用过程 1.UI设计 ...

  6. CentOS 7 主机名bogon解决办法

    转https://blog.csdn.net/qq_24221531/article/details/80334942 一.修改linux主机的配置文件/etc/hostname 和 /etc/hos ...

  7. net与树莓派的情缘-安装Redis(四)

    在Linux下安装Redis非常简单,具体步骤如下(官网有说明): .下载源码,解压缩后编译源码. $ wget http://download.redis.io/releases/redis-3.0 ...

  8. Spring整合Hibernate_数据源Datasource_dbcp连接池

    1,  Spring指定 datasource DataSource接口,在javax.sql包,里边有一个getConnection()方法.提供了标准化的取得连接的方式.只要实现了这个接口.Sun ...

  9. Capture Conversion解读

    Let G name a generic type declaration with n type parameters A1,...,An with corresponding bounds U1, ...

  10. Bootboxjs快速制作Bootstrap的弹出框效果

    Bootboxjs是一个简单的js库,简单快捷帮你制作一个Bootstrap的弹出框效果. 一.简介 bootbox.js是一个小的JavaScript库,它帮助您在使用bootstrap框架的时候快 ...