Алгоритмы выбора на отсортированной матрице
Это вопрос интервью с Google:
Учитывая матрицу N * N.
Все строки отсортированы, и все столбцы отсортированы.
Найти Kth Наибольший элемент матрицы.
делает это в n ^ 2 просто, и мы можем сортировать его с помощью кучи или сортировки слияния (n lg n), а затем получить его, но есть ли лучший подход, лучше, чем (n lg n)?
пример массива::
1 5 7 12
3 6 8 14
4 9 10 15
11 17 19 20
1 < 5 < 12 < 3 < 4 < 4 аналогично другим строкам и столбцам. теперь скажем, что нам нужно найти 10-й наименьший элемент, здесь 11. Надеюсь, это добавит некоторые детали к вопросу...
Ответы
Ответ 1
Да, есть алгоритм O (K) из-за Фредериксона и Джонсона.
Грег Н. Фредериксон и Дональд Б. Джонсон. Обобщенный выбор и ранжирование: отсортированные матрицы. SIAM J. Comput. 13, с. 14-30. http://epubs.siam.org/sicomp/resource/1/smjcat/v13/i1/p14_s1?isAuthorized=no
Ответ 2
С матрицей, приведенной в примере:
Если вы хотите найти 7-й элемент, вы знаете, что 7-й элемент находится в элементах M [4] [1..4], M [1..4] [4]. Вы получаете уже отсортированные массивы, 12,14,15,20 и 11,17,19, которые можно объединить. Затем вы применяете двоичный поиск, который является O (log N).
Обобщите: для k-го самого большого элемента в этой матрице вам нужно выбрать правильный уровень: [2N-1] + [2 (N-1) -1] +... >= k, поэтому алгоритм выберите правильный слой для поиска - Sum [2 (Ni) -1] >= k, для я = 0, N-1, где я - номер слоя. После того, как вы найдете i, номер слоя, у вас будет 2 (N-i) -1 элемента в этом массиве, которые необходимо объединить, а затем выполнить поиск. Сложность поиска этого уровня - O (log [2 (N-i) -1] = O (log (N-i))...
Арифметическая прогрессия приводит к
0 >= я ^ 2-2 * N * + К
i1,2 = N + -sqrt (N ^ 2-k), где k - элемент, который мы ищем...
Ответ 3
Поскольку все уже отсортировано, вы можете просто выполнить диагональный поиск. (Хотя, честно говоря, я не знаю, что это означает, что "все строки отсортированы и все столбцы отсортированы". Если это правда буквально, то просто перейдите к k-му элементу в диагональном перечислении матрицы.)
Ответ 4
вращать матрицу по часовой стрелке на 45 градусов. Вы получите набор данных в форме алмаза. Высота будет 2N-1, количество элементов в каждой строке сверху будет выглядеть следующим образом: 1,2,3,4,5,4,3,2,1 для N = 5
Вы узнаете, что каждое число в строке всегда больше любого числа выше.
для k-й строки (считая от 1), у вас будет k элементов для k < N и 2N-k при k >= N
k принадлежит {1..2N-1}
Вычисляя накопительное число элементов из строк 1 в k-1 и от 1 до k, вы найдете строку, в которой находится ваша цель (сумма (от 1 до k-1)
Теперь, когда вы обнаружили ряд элементов с наихудшим итогом N всего. Вы можете отсортировать их, а затем найти правильный. это taks O (N ln N)
так как N = sqrt (n), общая стоимость этого алгоритма равна O (sqrt (n) ln (sqrt (n)))
Ответ 5
На основе N вы можете найти диагональ, где находится элемент. Например, в матрице
1 5 7 12
3 6 8 14
4 9 10 15
11 17 19 20
Вы можете вывести диагональ, определив общее количество элементов в предыдущих диагоналях,
/diagonal#/elements/# of elements/cumulative # of elements/
/d1/ 1 / 1 / 1 /
/d2/ 3 5 / 2 / 1+2 = 3 /
/d3/ 4 6 7 / 3 / 1+2+3 = 6 /
/d4/ 11 9 8 12 / 4 / 1+2+3+4 = 10 /
/d5/ 17 10 14 / 3 /
/d6/ 19 15 / 2 /
/d7/ 20 / 1 /
Причина, по которой нам нужно найти диагональ, состоит в том, что вышеприведенные диагонали всегда будут содержать элементы, меньшие, чем любой из существующих диагональных элементов, а внизу диагоналей всегда будут элементы, большие, чем любой из существующих диагональных элементов.
Итак, вы можете быть уверены, что диагональ d4
имеет необходимый элемент (поскольку он содержит 7-е место по величине до 10-го по величине). Поскольку до предыдущей диагонали было 6 элементов, вам просто нужно найти 4-й по величине элемент в диагонали d4
.
Ответ 6
Сначала вы начинаете поиск дыхания, начиная с (0,0). (0,0) s 2 ребенка (0,1) и (1,0) добавляются в список потенциальных кандидатов для 2-го элемента. Петля, выбирающая наименьший элемент в списке потенциальных кандидатов, будет следующим элементом, добавьте его детей в список потенциальных кандидатов. Остановитесь, когда найдете k-ый элемент.
Сделайте потенциальными кандидатами список минутной кучи. Куча никогда не будет больше, чем n + m.
Также вы можете сделать обратное от последнего элемента (n, m), если k больше n * m/2.
Худший случай: это будет n * m/2 lg (n + m) вместо n * m lg (n * m) сортировки.
Ответ 7
Вы можете найти наименьший элемент k th во времени O (n log n), если вы заметили, что:
- Генерация случайного числа, которое находится между Array [i] [j] и Array [k] [l], так что Array [i] [j] Массив [k] [l] принимает O (n) время (ожидается) и
Используя [1] в качестве подпрограммы, вы можете использовать процедуру, похожую на RANDOMIZED-SELECT, чтобы генерировать наименьшее число k th во всем массиве.
Ответ 8
Мой код ниже - это алгоритм O (k). Он не работает на определенном краевом случае (вероятно, по одному в каждом направлении: x и y). Я перечислил кромку, чтобы кто-то мог ее исправить. Я не собираюсь это исправлять, потому что это время для меня.
Резюме алгоритма: вам нужно только отслеживать два кандидата #, которые могут быть самыми маленькими, один при продолжении в направлении x и один, продолжая в направлении y. Подумайте об этом, и это может иметь смысл для вас.
enum Direction {
X,
Y
};
struct Index
{
Index(int unsigned x, int unsigned y)
: x(x),
y(y)
{}
void operator = (Index const & rhs)
{
x = rhs.x;
y = rhs.y;
}
int unsigned x;
int unsigned y;
};
int unsigned solve(int unsigned i_k, int unsigned ** i_data, int unsigned i_n)
{
if (1 == i_k) {
return i_data[0][0];
}
Direction dir = X;
Index smaller(0,0);
Index larger(0,0);
if (i_data[1][0] < i_data[0][1]) {
dir = X;
smaller = Index(1,0);
larger = Index(0,1); }
else {
dir = Y;
smaller = Index(0,1);
larger = Index(1,0);
}
for (int unsigned i = 0; i < (i_k - 2); ++i) {
int unsigned const x = smaller.x;
int unsigned const y = smaller.y;
if (X == dir) {
if ((x + 1) == i_n) {
// End of row
smaller = larger;
larger.x += 1;
dir = Y; }
else if (i_data[x + 1][y] < i_data[larger.x][larger.y]) {
smaller.x += 1; }
else {
smaller = larger;
larger = Index(x + 1, y);
dir = Y;
} }
else {
if ((y + 1) == i_n) {
// End of col
smaller = larger;
larger.y += 1;
dir = X; }
else if (i_data[x][y + 1] < i_data[larger.x][larger.y]) {
smaller.y += 1; }
else {
smaller = larger;
larger = Index(x, y + 1);
dir = X;
}
}
}
return i_data[smaller.x][smaller.y];
}
не работает в следующем краевом случае (где мы попадаем в конец строки). Я буду спать, не стесняйтесь исправить этот случай:
size = 4;
data = createMatrix(size);
data[0][0] = 1; data[1][0] = 6; data[2][0] = 10; data[3][0] = 11;
data[0][1] = 3; data[1][1] = 7; data[2][1] = 12; data[3][1] = 14;
data[0][2] = 4; data[1][2] = 8; data[2][2] = 13; data[3][2] = 15;
data[0][3] = 5; data[1][3] = 9; data[2][3] = 19; data[3][3] = 20;
answer = solve(14, data, size);
assertAnswer(answer, 15, ++testNum);
deleteMatrix(data, size);
Ответ 9
Ниже приведено мое решение на С++, основанное на минимальной куче. Когда ячейка в матрице находится сверху верхней части кучи, число справа и/или нижняя сторона будет вставлена в кучу.
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct Entry {
int value;
int x;
int y;
bool operator < (const Entry& other) {
return this->value > other.value;
}
};
bool getKthNumber(int* matrix, int row, int col, int k, int* result){
if(matrix == NULL || row <= 0 || col <= 0 || result == NULL)
return false;
if(k <= 0 || k > row * col)
return false;
vector<Entry> minHeap;
Entry first = {matrix[0], 0, 0};
minHeap.push_back(first);
make_heap(minHeap.begin(), minHeap.end());
for(int i = 0; i < k; ++i){
first = minHeap[0];
int x = first.x;
int y = first.y;
if(first.y == 0 && first.x < row - 1){
Entry next = {matrix[(x + 1) * col], x + 1, y};
minHeap.push_back(next);
push_heap(minHeap.begin(), minHeap.end());
}
if(first.y < col - 1){
Entry next = {matrix[x * col + y + 1], x, y + 1};
minHeap.push_back(next);
push_heap(minHeap.begin(), minHeap.end());
}
pop_heap(minHeap.begin(), minHeap.end());
minHeap.pop_back();
}
*result = first.value;
return true;
}