Предположим, у меня есть матрица, как показано ниже
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. Поскольку строки и столбцы отсортированы. Тогда есть 2 подматрицы выше 14 (которые включают в себя 5 и 10) и одна ниже 14 (которая включает в себя 16, 17, 18, 21, 22, 23), которые могут содержать или не содержать элементы больше 14. Поэтому, если мы найдем и добавьте число элементов больше 14 из этих 3 матриц, у нас будет количество элементов больше 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-го по величине элемента.
Но я не уверен, какова будет сложность этого метода.