Algorithm 求未排序数组的中值
为了找到未排序数组的中位数,我们可以在O(nlogn)时间内为n个元素创建一个最小堆,然后我们可以逐个提取n/2个元素以获得中位数。但这种方法需要O(nlogn)时间Algorithm 求未排序数组的中值,algorithm,heap,median,Algorithm,Heap,Median,为了找到未排序数组的中位数,我们可以在O(nlogn)时间内为n个元素创建一个最小堆,然后我们可以逐个提取n/2个元素以获得中位数。但这种方法需要O(nlogn)时间 我们能在O(n)时间内用某种方法做同样的事情吗?如果可以,请告诉或建议一些方法。您可以使用该算法在线性时间内查找未排序数组的中值。在O(n)中有效,这也用于快速排序的分区步骤。可以在O(n)中使用快速选择算法,请参考k阶统计量(随机算法) 快速选择算法可以在线性(O(n))运行时找到数组的第k个最小元素。下面是python的一个实
我们能在O(n)时间内用某种方法做同样的事情吗?如果可以,请告诉或建议一些方法。您可以使用该算法在线性时间内查找未排序数组的中值。在O(n)中有效,这也用于快速排序的分区步骤。可以在O(n)中使用快速选择算法,请参考k阶统计量(随机算法) 快速选择算法可以在线性(
O(n)
)运行时找到数组的第k个最小元素。下面是python的一个实现:
随机导入
def分区(L,v):
较小的=[]
更大=[]
对于L中的val:
如果valv:biger+=[val]
返回值(较小[v],较大)
def top_k(左、右):
v=L[random.randrange(len(L))]
(左、中、右)=分区(L、v)
#为了清楚起见,在下面使用中间部分(代替[v])
如果len(左)==k:返回左
如果len(左)+1==k:返回左+中
如果len(左)>k:返回top_k(左,k)
返回左+中+上(右,k-镜头(左)-镜头(中))
def中值(L):
n=len(L)
l=顶部(l,n/2+1)
返回最大值(l)
正如维基百科所说,中位数理论上为o(N),但实际上并未使用,因为寻找“好”支点的开销使其速度太慢。以下是用于在数组中查找第k个元素的Quickselect算法的Java源代码:
/**
* Returns position of k'th largest element of sub-list.
*
* @param list list to search, whose sub-list may be shuffled before
* returning
* @param lo first element of sub-list in list
* @param hi just after last element of sub-list in list
* @param k
* @return position of k'th largest element of (possibly shuffled) sub-list.
*/
static int select(double[] list, int lo, int hi, int k) {
int n = hi - lo;
if (n < 2)
return lo;
double pivot = list[lo + (k * 7919) % n]; // Pick a random pivot
// Triage list to [<pivot][=pivot][>pivot]
int nLess = 0, nSame = 0, nMore = 0;
int lo3 = lo;
int hi3 = hi;
while (lo3 < hi3) {
double e = list[lo3];
int cmp = compare(e, pivot);
if (cmp < 0) {
nLess++;
lo3++;
} else if (cmp > 0) {
swap(list, lo3, --hi3);
if (nSame > 0)
swap(list, hi3, hi3 + nSame);
nMore++;
} else {
nSame++;
swap(list, lo3, --hi3);
}
}
assert (nSame > 0);
assert (nLess + nSame + nMore == n);
assert (list[lo + nLess] == pivot);
assert (list[hi - nMore - 1] == pivot);
if (k >= n - nMore)
return select(list, hi - nMore, hi, k - nLess - nSame);
else if (k < nLess)
return select(list, lo, lo + nLess, k);
return lo + k;
}
/**
*返回子列表第k个最大元素的位置。
*
*@param list要搜索的列表,其子列表可能会在
*返回
*@param lo列表中子列表的第一个元素
*@param hi位于列表中子列表的最后一个元素之后
*@param k
*@返回子列表中第k个最大元素的位置(可能被洗牌)。
*/
静态整数选择(双[]列表,整数低,整数高,整数k){
int n=高-低;
if(n<2)
返回lo;
双支点=列表[lo+(k*7919)%n];//选择一个随机支点
//分类列表到[透视]
int nLess=0,nSame=0,nMore=0;
int-lo3=lo;
int-hi3=hi;
而(lo30){
交换(列表,lo3,--hi3);
如果(nSame>0)
交换(列表、hi3、hi3+nSame);
nMore++;
}否则{
nSame++;
交换(列表,lo3,--hi3);
}
}
断言(nSame>0);
断言(nLess+nSame+nMore==n);
断言(列表[lo+nLess]==pivot);
断言(列表[hi-nMore-1]==pivot);
如果(k>=n-nMore)
返回select(list,hi-nMore,hi,k-nLess-nSame);
else if(k
我没有包括compare和swap方法的源代码,因此很容易将代码更改为使用Object[],而不是double[]
实际上,您可以预期上述代码为o(N)。我已经对@dasblinkenlight答案进行了投票,因为中值算法实际上在o(N)时间内解决了这个问题。我只想补充一点,这个问题也可以通过使用堆在O(n)时间内解决。通过使用自底向上的方法,可以在O(n)时间内完成堆的构建。请参阅下面的文章以获得详细的解释 假设数组有N个元素,则必须构建两个堆:一个包含前N/2个元素的MaxHeap(如果N为奇数,则为(N/2)+1)和一个包含其余元素的MinHeap。如果N是奇数,则中值是MaxHeap(O(1)的最大元素,通过获取最大值)。如果N是偶数,那么中间值是(MaxHeap.max()+MinHeap.min())/2这也需要O(1)。因此,整个操作的实际成本是堆构建操作,即O(n)
顺便说一句,当您事先不知道数组元素的数目时(例如,如果您必须为一个整数流解决相同的问题),这种MaxHeap/MinHeap算法也可以工作。您可以在下面的文章中看到有关如何解决此问题的更多详细信息。答案是“不,在线性时间内无法找到任意未排序数据集的中值”。一般来说(据我所知),最好的方法是中位数(以获得一个良好的开端),然后是Quickselect。Ref:[问题是:在未排序的数组中查找第k个最大的元素 将数组分成n/5组,每组由5个元素组成 现在a1,a2,a3…a(n/5)代表每组的中位数 x=元素a1、a2、…a(n/5)的中值 现在,如果kn/2,那么我们可以移除中位数小于x的组中最小的、第二小的和第三小的元素。我们现在可以用7n/10元素再次调用函数,并找到第(k-3n/10)个最大值 时间复杂性分析: T(n)在大小为n的数组中查找第k个最大值的时间复杂度 T(n)=T(n/5)+T(7n/10)+O(n) 如果你解这个,你会发现T(n)实际上是O(n)
n/5+7n/10=9n/10
Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.
代码:
请记住,如果需要O(nlogn),那么您最好对数组进行排序并将索引除以2。构建堆需要O(n)时间而不是O(nlogn)@JerryGoyal,如果您同时拥有所有元素,那么构建堆需要O(n)。但是如果您拥有元素流,则需要O(nlogn).这就像一次推一个元素
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
merged_array = sorted(nums1 + nums2)
if len(merged_array) % 2 == 0:
index = int(len(merged_array)/2)
output = (merged_array[index - 1] + merged_array[index])/2
else:
index = int(len(merged_array)/2)
output = merged_array[index]
return output