K:找寻数组中第n大的数组元素的三个算法
相关介绍:
给定一个数组,找出该数组中第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大的数组元素的三个算法的更多相关文章
- 无序数组中第Kth大的数
题目:找出无序数组中第Kth大的数,如{63,45,33,21},第2大的数45. 输入: 第一行输入无序数组,第二行输入K值. 该是内推滴滴打车时(2017.8.26)的第二题,也是<剑指of ...
- 求一无序数组中第n大的数字 - 快速选择算法
逛别人博客的时候,偶然看到这一算法题,顺便用C++实现了一下. 最朴素的解法就是先对数组进行排序,返回第n个数即可.. 下面代码中用的是快速选择算法(不晓得这名字对不对) #include <v ...
- extract_by_one 根据二维数组中某字段来提取数组信息,查看有无重复信息
public function tt(){ $param = array( array ( 'hykno' => '2222222-CB', 'tcdk_fid' => '458B6D70 ...
- 算法练习之合并两个有序链表, 删除排序数组中的重复项,移除元素,实现strStr(),搜索插入位置,无重复字符的最长子串
最近在学习java,但是对于数据操作那部分还是不熟悉 因此决定找几个简单的算法写,用php和java分别实现 1.合并两个有序链表 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两 ...
- JavaScript数组中出现的次数最多的元素
var arr = [1,-1,2,4,5,5,6,7,5,8,6]; var maxVal = arr[0]; // 数组中的最大值 var minVal = arr[0]; // 数组中的最小值 ...
- 《剑指offer》第五十三题(数组中数值和下标相等的元素)
// 面试题53(三):数组中数值和下标相等的元素 // 题目:假设一个单调递增的数组里的每个元素都是整数并且是唯一的.请编程实 // 现一个函数找出数组中任意一个数值等于其下标的元素.例如,在数组{ ...
- LeetCode 80 Remove Duplicates from Sorted Array II(移除数组中出现两次以上的元素)
题目链接:https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/#/description 给定一个已经排好序的数组 ...
- javascript 数组中出现的次数最多的元素
javascript 数组中出现的次数最多的元素 var arr = [1,-1,2,4,5,5,6,7,5,8,6]; var maxVal = arr[0]; // 数组中的最大值 var min ...
- 剑指offer——58数组中数值和下标相等的元素
题目三: 数组中数值和下标相等的元素. 假设一个单调递增的数组里的每个元素都是整数并且是唯一的.请编程实现一个函数,找出数组中任意一个数值等于其下标的元素.例如,在数组{-3,-1,1,3,5}中,数 ...
随机推荐
- BruteXSS(汉化版)
BruteXSS是一个非常强大和快速的跨站点脚本暴力注入.它用于暴力注入一个参数.该BruteXSS从指定的词库加载多种有效载荷进行注入并且使用指定的载荷和扫描检查这些参数很容易受到XSS漏洞.得益于 ...
- 护网杯圆满结束,还不满足?不如来看看大佬的WP扩展思路~
护网杯预选赛 WP转载自:https://qingchenldl.github.io/2018/10/13/%E6%8A%A4%E7%BD%91%E6%9D%AFWP-BitPwn/#more WEB ...
- 编程开发之--java多线程学习总结(6)
5.测试 package com.lfy.ThreadsSynchronize; public class Test { public static void main(String[] args) ...
- 洛谷 P3757 [CQOI2017]老C的键盘
题面 luogu 题解 其实就是一颗二叉树 我们假设左儿子小于根,右儿子大于根 考虑树形\(dp\) \(f[u][i]\)表示以\(u\)为根的子树,\(u\)为第\(i\)小 那么考虑子树合并 其 ...
- 题解 p2420 让我们异或吧
传送门 #include<iostream> #include<cstdio> #include<cstring> using namespace std; ;in ...
- 【App性能分析】:tracelog分析法
tracelog可以记录每个OpenGL函数调用的消耗时间,所以很多时候用来作performance分析.目前只支持安卓4.1以上的版本设备 1,目前Android Device Monitor最新的 ...
- dubbo集群容错之LoadBalance
原文地址:Dubbo 源码分析 - 集群容错之 LoadBalance dubbo 提供了4种负载均衡实现,分别是基于权重随机算法的 RandomLoadBalance.基于最少活跃调用数算法的 Le ...
- Service层异常处理
1.在service方法里面如果对异常进行了捕获的话,该事务是不会进行回滚的 * 默认spring事务只在发生未被捕获的 runtime excetpion()时才回滚. * * spring aop ...
- Mac下使用Wine安装文件内容搜索工具Search and Replace
下载: (链接: https://pan.baidu.com/s/1mij7WX6 密码: xsu8) 安装: 1.安装Wine 参考:http://www.cnblogs.com/EasonJim/ ...
- 基于MVC4+EF5.0+Ajax+Json+CSS3的简单注册页面(get&post)
使用mvc4可以很快速的创建页面,但封装的过多,难免会有些性能上的问题.所以基于此,通过使用简单的手写html,加ajax,json来创建一个注册页面,会比较干净,简洁. 本项目的环境是MVC4+EF ...