算法-可重复的无序数组的第j个最大值

问题

求无序元素可重复的数组的第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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值