问题
求无序元素可重复的数组的第j个最大值。如果不存在,返回最大值,假定数组不为空。
[2 2 3 1] 第2个最大值为2
[4 6 6 4] 第3个最大值不存在,返回第一个最大值6
实现思路
NthMax对象内部封装了一个0~j-1个最大值的数组_maxVals,当一个target元素进来后,判断_maxVlas的元素个数少于j时,找到一个合适位置插入;当元素个数等于j时,target元素插入位置假如为k,则依次向后移动这k-1个元素,丢弃掉_maxVals中的第一个元素,然后再在位置k-1处插入target。遍历完成后,若_maxVals个数等于j,则0~j依次为无序数组的前j个最大值,若小于j,则表示未找到j个最大值,返回最大值即可。
代码实现
下面是代码,只用数组实现,不借助其他数据结构,封装了一个对象NthMax,它提供的API有3个,如下所示,
//构造函数:确立求第几个最大值
//遍历原数组的元素时,插入到NthMax实例中
public void Insert(int target);
//报告第n个最大值
public int Nth{get;}
下面介绍了如何使用NthMax对象中的API。已知一个int数组,先构造一个myNth实例,确定求数组的第5个最大值。遍历原数组,并依次放到myNth中,遍历完成后,获取到第5个最大的元素。
int[] a = new int[] { 1,2,6,7,8,9,0 };
NthMax myNth = new NthMax(5);
foreach (var item in a)
{
myNth.Insert(item);
}
int thirdMax = myNth.Nth;
API的实现代码:
public class NthMax
{
//构造函数
public NthMax(int nth)
{
_nth = nth;
}
public int Nth
{
get
{
int dis = _maxVals.Length-_nth;
return _maxVals[dis];
}
}
public void Insert(int target)
{
if (_maxVals == null)
{
_maxVals = new int[1];
_maxVals[0] = target;
return;
}
if (_maxVals.Length < _nth)
{
int insertPos = searchInsertPos(target);
insertAt(insertPos, target);
}
else if (_maxVals.Length == _nth)
{
int insertPos = searchInsertPos(target);
if (insertPos > 0)
{
moveBackward(insertPos - 1);
_maxVals[insertPos - 1] = target; //在空出来的位置处插入目标元素
}
}
}
private int[] _maxVals;
private int _nth;
// 如果target不在_maxVals中,则找到它的插入位置;如果在直接返回-1
private int searchInsertPos(int target)
{
int lo = 0;
int hi = _maxVals.Length;
while (lo < hi)
{
int mi = (lo + hi) >> 1;
if (target < _maxVals[mi]) //目标值小于中间位置的数
hi = mi;
else //不小于中间位置的值
lo = lo + 1;
}
return lo;
}
//在有序数组index处插入目标元素target
private void insertAt(int index, int target)
{
int[] tmp = new int[_maxVals.Length + 1];
for (int i = 0; i < index; i++)
{
tmp[i] = _maxVals[i];
}
tmp[index] = target;
for (int i = index; i < _maxVals.Length; i++)
{
tmp[i + 1] = _maxVals[i];
}
_maxVals = tmp;
}
// 从beginIndex开始覆盖性后移1步
private void moveBackward(int beginIndex)
{
if (beginIndex < 1)
return;
for (int i = 1; i <= beginIndex; i++)
{
_maxVals[i - 1] = _maxVals[i];
}
}
}
算法分析
如果确立的第j个最大值,j取值较小,则算法的时间复杂度O(n),在实际应用中,j也是取值较小,即求解较大的最大值,这样才有实际意义。
相关应用
LeetCode
414 : Third Maximum Number
215. Kth Largest Element in an Array