我有一个二维数组。行和列已排序。如何从二维数组中找到第k个最大元素?
答案 0 :(得分:6)
如果您有n * n
矩阵,则可以平均时间O(n * log(n) * log(n))
执行此操作。
您要做的是将矩阵分解为一系列已排序的数组,然后立即对所有这些数组进行二进制搜索。例如,假设n = 4
并从(0,0)
索引到(3,3)
。我们可以将其分解为从列向上升到对角线的数组,然后向右转以完成行。这将为我们提供以下一组排序数组:
(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (3,3)
(1,0), (1,1), (1,2), (2,2), (3,2)
(2,0), (2,1), (3,1)
(3,0)
这为我们提供了n
排序列表。
所以我们需要弄清楚k
'元素在一组排序数组中的位置。
我们将通过二进制搜索来实现它的值。
首先取最长数组的中点,即示例中的元素(0,3)
。然后对于每个其他数组计算出多少大于,小于或等于该值。 (你可以通过二分搜索找到它。)这让我们弄清楚k
'元素在哪一边划分。如果它与我们刚刚选择的元素匹配,我们就有了答案。否则,我们可以平均丢弃每个数组的一半(有时会丢弃整个数组)并重复。
在平均O(log(n))
次操作后,每次操作费用O(n log(n))
,我们都会得到答案,从而导致我的估算值。
答案 1 :(得分:3)
在最小尺寸上进行n路合并。当你完成第k项后,你就完成了。
测试显示这在k lg d中运行,其中d = min(rows,cols)。
答案 2 :(得分:2)
假设我有如下所示的矩阵
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
当我在考虑这个问题的解决方案时,我看到第一个最大的元素总是在(4,4)。第二大元素将是(3,4)或(4,3),它不能在(4,4)中。所以我在考虑是否可以根据矩阵大小和k来找到第k个最大元素的可能位置。
假设第k个最大元素的可能位置集合= f(大小(矩阵),k)。
但是在下面发布的答案中,我找不到一个简单的函数f(),它可以生成可能的位置。
而不是检查所有位置的元素,我只能检查可能位置的元素。
为了找到大于元素的数字,我们可以使用以下方式。
如果我想找到有多少元素大于14.无论如何,14(15)和14(19,24)右边的元素和它们之间的所有元素(20,25)都是大于14,因为行和列已排序。然后有14个以上的子矩阵(包括5和10)和一个低于14的子矩阵(包括16,17,18,21,22,23),它们可能包含也可能不包含大于14的元素。所以如果我们找到并且从这3个矩阵中添加大于14的元素个数,我们将得到大于14的元素。
对于每个可能的位置,我们可以在矩阵中找到没有更大的元素。如果有k-1个较大的元素,则当前位置的元素是第k个最大元素。
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NewTest
{
private static int matrixSize = 25;
private static Map < Integer, List < Point > > largestEltVsPossiblePositions = new HashMap < Integer, List < Point >>();
static
{
// In the initialize method, I am populating the map
// "largestEltVsPossiblePositions" with kth largest element and its
// possible positions. That is 1st largest element will always be in
// (24,24) and 2nd largest element will be (23,24) and (24,23). Like
// that I am populating the possible locations for all the nth largest
// elements. This map we need to initialize only once.
initialize();
}
private static void initialize()
{
for ( int i = 1; i <= matrixSize * matrixSize; i++ )
{
//Getting the possible locations for each number and putting in the map.
List < Point > possiblePositions = getPossiblePositions( matrixSize, i );
largestEltVsPossiblePositions.put( i, possiblePositions );
}
}
/**
* @param args
*/
public static void main( String [] args )
{
// int matrixSize = 5;
// for ( int i = 1; i <= matrixSize * matrixSize; i++ )
// {
// List < Point > possiblePositions = getPossiblePositions( matrixSize, i );
// System.out.println( i + " --- " + possiblePositions.size() + " - " + possiblePositions );
// }
//creating a test array.
int [][] matrix = createTestArray();
long currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 7 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 27 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 34 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 624 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 2 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 4 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
currentTimeMillis = System.currentTimeMillis();
findKthLargestElement( matrix, 310 );
System.out.println( "Total time : " + ( System.currentTimeMillis() -
currentTimeMillis ) );
}
private static int [][] createTestArray()
{
int [][] matrix = new int [matrixSize] [matrixSize];
int count = 1;
for ( int i = 0; i < matrixSize; i++ )
{
for ( int j = 0; j < matrixSize; j++ )
{
matrix[j][i] = count;
count++ ;
}
}
return matrix;
}
private static void findKthLargestElement( int [][] matrix, int k )
{
//Get all the possible positions of this kth largest element.
List < Point > possiblePoints = largestEltVsPossiblePositions.get( k );
//I am sorting the points in descending order of the values in them.
Collections.sort( possiblePoints, new PointComparator( matrix ) );
for ( Point point : possiblePoints )
{
//For a point, If there are exactly k-1, larger elements in the matrix, then it is the kth largest element.
if ( ( k - 1 ) == getNoofLargerElementsThanKFromMatrix( matrix, point ) )
{
System.out.println( "Largest " + k + "th element in the matrix is : " + matrix[point.x][point.y]
+ " in the co-ordinates : " + point );
break;
}
}
}
/*
* This method will find the elements larger than the element at the specified point from the matrix.
*/
private static int getNoofLargerElementsThanKFromMatrix( int [][] matrix, Point point )
{
int sum = 0;
// Suppose the point is (x,y). Then all the elements (x+1,y),
// (x+2,y).... (maxRows,y), (x,y+1), (x,y+2), ... (x,maxCols) and all
// the numbers between them(x+1,y+1), (x+2,y+1)... (maxRows,maxCols)
// will be surely greater than the element at the point (x,y.). We are counting those element.
sum = ( matrixSize - point.x ) * ( matrixSize - point.y ) - 1;
if ( point.x > 0 )
{
// In the above case, we were sure that all the elements in that range are greater than element at the point.
// There is a region in the matrix where there might be elements larger than the element at the point.
// If the point is (x,y), then the elements from (0,y+1) to
// (x-1,maxCols), In this region there might be some elements which
// are larger than the element we need to count those.
sum = sum + getNumbersGreaterThanKFromUpperMatrix( matrix, point );
}
if ( point.x < matrix.length - 1 )
{
// It is same as the above case, There is another region in the
// matrix where there might be elements larger than the element at the point.
// If the point is (x,y), then the elements from (x+1,0) to
// (maxRows,y-1), In this region there might be some elements which
// are larger than the element we need to count those.
sum = sum + getNumbersGreaterThanKFromLowerMatrix( matrix, point );
}
//Once we get all the elements larger than k, we can return it.
return sum;
}
private static int getNumbersGreaterThanKFromUpperMatrix( int [][] matrix, Point point )
{
int startY = point.y;
if ( point.y + 1 != matrix[0].length )
{
startY = point.y + 1;
}
Point matrixStart = new Point( 0, startY );
int startX = point.x;
if ( point.x != 0 )
{
startX = point.x - 1;
}
Point matrixEnd = new Point( startX, matrix[0].length - 1 );
return getLargerElementsFromTheMatrix( matrix, matrixStart, matrixEnd, matrix[point.x][point.y] );
}
private static int getNumbersGreaterThanKFromLowerMatrix( int [][] matrix, Point point )
{
int startX = point.x;
if ( point.x + 1 != matrix.length )
{
startX = point.x + 1;
}
Point matrixStart = new Point( startX, 0 );
int startY = point.y;
if ( point.y != 0 )
{
startY = point.y - 1;
}
Point matrixEnd = new Point( matrix.length - 1, startY );
return getLargerElementsFromTheMatrix( matrix, matrixStart, matrixEnd, matrix[point.x][point.y] );
}
private static int getLargerElementsFromTheMatrix( int [][] matrix, Point matrixStart, Point matrixEnd, int elt )
{
//If it is a single cell matrix, just check that element in the matrix is larger than the kth element we are checking.
if ( matrixStart.equals( matrixEnd ) )
{
if ( elt <= matrix[matrixStart.x][matrixStart.y] )
{
return 1;
}
else
{
return 0;
}
}
if ( elt <= matrix[matrixStart.x][matrixStart.y] )
{
return ( matrixEnd.x - matrixStart.x + 1 ) * ( matrixEnd.y - matrixStart.y + 1 );
}
else
{
//Do it recursively to get all the elements larger than elt from the matrix from the startPoint to endPoint.
int matrixStartX = matrixStart.x;
if ( matrixStart.x + 1 <= matrixEnd.x )
{
matrixStartX = matrixStart.x + 1;
}
int matrixStartY = matrixStart.y;
if ( matrixStart.y + 1 <= matrixEnd.y )
{
matrixStartY = matrixStart.y + 1;
}
Point newMatrixStart = new Point( matrixStartX, matrixStartY );
int s1 = getLargerElementsFromTheMatrix( matrix, newMatrixStart, matrixEnd, elt );
int s2 = getLargerElementsFromTheMatrix( matrix, new Point( matrixStartX, matrixStart.y ), new Point(
matrixEnd.x, matrixStart.y ), elt );
int s3 = getLargerElementsFromTheMatrix( matrix, new Point( matrixStart.x, matrixStartY ), new Point(
matrixStart.x, matrixEnd.y ), elt );
return s1 + s2 + s3;
}
}
//For getting the possible positions of kth largest element.
private static List < Point > getPossiblePositions( int matrixSize, int k )
{
List < Point > points = new ArrayList < Point >();
k-- ;
for ( int i = 0; i < matrixSize; i++ )
{
for ( int j = 0; j < matrixSize; j++ )
{
int minNoGreaterThanIJ = ( matrixSize - i ) * ( matrixSize - j ) - 1;
int maxNoGreaterThanIJ = matrixSize * matrixSize - ( ( i + 1 ) * ( j + 1 ) );
if ( minNoGreaterThanIJ <= k && maxNoGreaterThanIJ >= k )
points.add( new Point( i, j ) );
}
}
return points;
}
}
class Point
{
final int x;
final int y;
Point( int x, int y )
{
this.x = x;
this.y = y;
}
@Override
public String toString()
{
return "(" + x + "," + y + ")";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals( Object obj )
{
if ( this == obj )
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
Point other = ( Point ) obj;
if ( x != other.x )
return false;
if ( y != other.y )
return false;
return true;
}
}
class PointComparator implements Comparator < Point >
{
private final int [][] matrix;
public PointComparator( int [][] matrix )
{
this.matrix = matrix;
}
@Override
public int compare( Point o1, Point o2 )
{
if ( matrix[o1.x][o1.y] == matrix[o2.x][o2.y] )
{
return -1;
}
else if ( matrix[o1.x][o1.y] < matrix[o2.x][o2.y] )
{
return 1;
}
else
{
return 1;
}
}
}
初始化完成一次初始化。初始化完成后,将计算并缓存可能的位置。此信息可用于查找第k个最大元素。
但我不确定这种方法的复杂程度。
答案 3 :(得分:1)
这个怎么样?
假设
保持大小为k的最大堆。
Push A[0][0] in the heap.
for i = 1 to k
curr_element = pop max element from heap
Push the right and bottom neighbor of the popped element from the matrix
(if they exist and have not been pushed earlier)
return curr_element
时间复杂度=循环运行k次(O(k))* 1次迭代运行O(3 * log(k))次= O(k * log(k))
答案 4 :(得分:1)
实际上有一个 O(n)分而治之算法可以解决排序矩阵中的选择问题(即找到排序矩阵中的第k个最小元素)。
Selection In X+Y and Matrices with Sorted Rows and Columns的作者最初提出了这样一种算法,但它的工作方式并不直观。可以在Selection in a sorted matrix中找到更简单的算法,如下所示。
定义:假设排序的nxm矩阵M,n <= m且没有重复,我们可以定义子矩阵N,使得N由所有奇数列和最后一列组成。 M.矩阵M中元素e的等级定义为rank(M,e) = |{M(i,j) | M(i,j) < e}|
。
主要定理:该算法依赖于以下事实:如果M是有序矩阵,2*rank(N,e) - 2n <= rank(M,e) <= 2*rank(N,e)
。
证明:以f(i) = min j s.t. M(i,j) >= e
为例,我们可以说明
rank(M,e) = sum i=1 to n of f(i)
rank(N,e) = sum i=1 to n of ceil(f(i)/2) <= rank(M,e)/2 + n
=> 2*rank(N,e) - 2n <= rank(M,e)
rank(N,e) > sum i=1 to n of f(i)/2
=> rank(M,e) <= 2*rank(N,e)
征服:换句话说,如果我们要在M中找到一个排名为k的元素,我们只需要研究由元素a和b限定的M的子矩阵P这样rank(N,a) = floor(k/2)
和rank(N,b) = ceil(k/2) + n
。这个子矩阵中有多少个元素?由于先前的不等式和假设没有重复,所以最多为O(n)。因此我们只需选择P中的k - rank(N,a)
元素,这可以通过将P重新排列为O(m)中的排序数组,然后运行线性时间算法(如quickselect)来查找实际元件。 rank(M,a)
可以在O(m)中计算,从矩阵中的最小元素开始并迭代列,直到找到大于a的元素,然后转到下一行并转到上一列,直到我们发现第一个元素大于a等。征服部分因此在O(m)中运行。
分割:唯一要做的就是找到rank(N,a) = k/2
和rank(N,b) = k/2 + n
的a和b。这显然可以在N上递归地完成(其大小相对于M除以2)。
运行时分析:总而言之,我们有一个O(m)征服算法。将f(n,m)
作为n×m矩阵的算法的复杂度,其中n <= m(如果不是矩阵可以在概念上旋转),我们可以建立递归关系f(m) = c*m + f(m/2)
。根据主定理,从f(1) = 1
开始,我们找到f(n,m) = O(m)
。因此整个算法的运行时间为O(m),即O(n)在方形矩阵的情况下(这也是n(k),因为我们可以将搜索限制在包含第一个的kxk矩阵中) k列和行)。
对于具有重复的矩阵的一般情况,可以标记矩阵&#39;具有行号和列号的元素。
答案 5 :(得分:-1)
让我们假设一个r行和c列的数组。 索引从1开始。
更新:抱歉,我忘记提及首先必须转换k以使以下公式起作用:
k = n - (k-1)。其中n是元素的总数,即r * c。
你可以得到k个最大元素的行索引:ceil(k / r)
您可以获取k个最大元素的列索引:k%c(%是Mod运算符)
更新:如果k%c = 0,请将结果设置为c。
运行时间为O(1)。
如果对于尺寸为r = 4且c = 4
的数组,k = 14k = 16 - (14 - 1)
k = 3
ARR [ceil(3/4),3%c]将返回第k个最大元素。
答案 6 :(得分:-1)
方法:
- 复制每个节点并将其插入原始节点的右侧。
- 重复随机指针。
- 左指针重复。
- 将两棵树分开。
醇>
有关图形说明,请参阅http://mytechspaze.com/index.php/2016/09/15/clone-binary-tree/