Saya baru-baru ini diberi pertanyaan wawancara ini dan saya ingin tahu apa solusi yang baik untuk itu.
Katakanlah saya diberi array 2d di mana semua angka dalam array berada dalam urutan meningkat dari kiri ke kanan dan dari atas ke bawah.
Apa cara terbaik untuk mencari dan menentukan apakah nomor target ada dalam larik?
Sekarang, kecenderungan pertama saya adalah menggunakan pencarian biner karena data saya diurutkan. Saya dapat menentukan apakah suatu angka berada dalam satu baris dalam waktu O (log N). Namun, 2 arah itulah yang membuat saya menjauh.
Solusi lain yang saya pikir mungkin berhasil adalah memulai di suatu tempat di tengah. Jika nilai tengahnya kurang dari target saya, maka saya bisa yakin itu berada di bagian persegi kiri matriks dari tengah. Saya kemudian bergerak secara diagonal dan memeriksa lagi, mengurangi ukuran persegi yang berpotensi menjadi sasaran target sampai saya mengasah nomor target.
Apakah ada yang punya ide bagus untuk memecahkan masalah ini?
Contoh larik:
Diurutkan dari kiri ke kanan, dari atas ke bawah.
1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11
[[1 1][1 1]]
?Jawaban:
Inilah pendekatan sederhana:
Untuk sebuah
NxM
array, ini berjalan diO(N+M)
. Saya pikir akan sulit untuk berbuat lebih baik. :)Edit: Banyak diskusi bagus. Saya berbicara tentang kasus umum di atas; jelas, jika
N
atauM
kecil, Anda dapat menggunakan pendekatan pencarian biner untuk melakukan ini pada sesuatu yang mendekati waktu logaritmik.Berikut beberapa detailnya, bagi yang penasaran:
Sejarah
Algoritma sederhana ini disebut Pencarian Saddleback . Sudah ada untuk sementara waktu, dan saat itu optimal
N == M
. Beberapa referensi:Namun, kapan
N < M
, intuisi menyarankan bahwa pencarian biner seharusnya dapat bekerja lebih baik daripadaO(N+M)
: Misalnya, ketikaN == 1
, pencarian biner murni akan berjalan dalam waktu logaritmik daripada waktu linier.Terikat kasus terburuk
Richard Bird meneliti intuisi ini bahwa pencarian biner dapat meningkatkan algoritma Saddleback dalam makalah tahun 2006:
Menggunakan teknik percakapan yang agak tidak biasa, Bird menunjukkan kepada kita bahwa untuk
N <= M
, masalah ini memiliki batas bawahΩ(N * log(M/N))
. Batasan ini masuk akal, karena memberi kita kinerja linier kapanN == M
dan kinerja logaritmik kapanN == 1
.Algoritma untuk array persegi panjang
Salah satu pendekatan yang menggunakan pencarian biner baris demi baris terlihat seperti ini:
N < M
. MisalkanN
adalah baris danM
kolom.value
. Jika kita menemukannya, kita sudah selesai.s
dang
, di manas < value < g
.s
kurang darivalue
, jadi kita bisa menghilangkannya.g
lebih besar darivalue
, jadi kita bisa menghilangkannya.Dalam hal kompleksitas kasus terburuk, algoritme ini
log(M)
berfungsi untuk menghilangkan setengah dari solusi yang mungkin, dan kemudian secara rekursif memanggil dirinya dua kali pada dua masalah yang lebih kecil. Kita memang harus mengulang versi yang lebih kecil darilog(M)
pekerjaan itu untuk setiap baris, tetapi jika jumlah barisnya kecil dibandingkan dengan jumlah kolom, maka kemampuan untuk menghilangkan semua kolom tersebut dalam waktu logaritmik mulai menjadi bermanfaat .Ini memberi algoritme kompleksitas
T(N,M) = log(M) + 2 * T(M/2, N/2)
, yang ditunjukkan oleh BirdO(N * log(M/N))
.Pendekatan lain yang diposting oleh Craig Gidney menjelaskan algoritme yang mirip dengan pendekatan di atas: ini memeriksa baris pada satu waktu menggunakan ukuran langkah
M/N
. Analisisnya menunjukkan bahwa ini juga menghasilkanO(N * log(M/N))
kinerja.Perbandingan Kinerja
Analisis Big-O semuanya baik dan bagus, tetapi seberapa baik pendekatan ini bekerja dalam praktik? Bagan di bawah ini memeriksa empat algoritme untuk larik yang semakin "persegi":
(Algoritme "naif" hanya menelusuri setiap elemen larik. Algoritme "rekursif" dijelaskan di atas. Algoritme "hibrid" adalah implementasi algoritme Gidney . Untuk setiap ukuran larik, kinerja diukur dengan mengatur waktu setiap algoritme pada set tetap dari 1.000.000 array yang dihasilkan secara acak.)
Beberapa poin penting:
Ringkasan
Penggunaan pencarian biner yang cerdas dapat memberikan
O(N * log(M/N)
kinerja untuk array persegi panjang dan persegi. TheO(N + M)
"Saddleback" algoritma jauh lebih sederhana, tetapi menderita dari penurunan kinerja sebagai array menjadi semakin persegi panjang.sumber
M==N
kita menginginkanO(N)
kompleksitas, bukanO(N*log(N/N))
karena yang terakhir adalah nol. Batas tajam "terpadu" yang benar adalahO(N*(log(M/N)+1))
kapanN<=M
.Masalah ini membutuhkan
Θ(b lg(t))
waktu, dimanab = min(w,h)
dant=b/max(w,h)
. Saya membahas solusinya di posting blog ini .Batas bawah
Musuh dapat memaksa algoritme untuk membuat
Ω(b lg(t))
kueri, dengan membatasi dirinya pada diagonal utama:Legenda: sel putih adalah item yang lebih kecil, sel abu-abu adalah item yang lebih besar, sel kuning adalah item yang lebih kecil atau sama, dan sel oranye adalah item yang lebih besar atau sama. Musuh memaksa solusi menjadi sel kuning atau oranye mana pun yang kueri algoritmanya terakhir.
Perhatikan bahwa ada
b
daftar ukuran yang diurutkan secara independent
, yang memerlukanΩ(b lg(t))
kueri untuk dihilangkan sepenuhnya.Algoritma
w >= h
)t
di kiri pojok kanan atas area yang validt
sel yang tersisa di baris dengan pencarian biner. Jika item yang cocok ditemukan saat melakukan ini, kembali dengan posisinya.t
kolom pendek.Menemukan sebuah item:
Menentukan item tidak ada:
Legenda: sel putih adalah item yang lebih kecil, sel abu-abu adalah item yang lebih besar, dan sel hijau adalah item yang sama.
Analisis
Ada
b*t
kolom pendek untuk dihilangkan. Adab
baris panjang yang harus dihilangkan. Menghilangkan pertengkaran yang panjang membutuhkanO(lg(t))
waktu. Menghilangkant
kolom pendek membutuhkanO(1)
waktu.Dalam kasus terburuk kita harus menghilangkan setiap kolom dan setiap baris, memakan waktu
O(lg(t)*b + b*t*1/t) = O(b lg(t))
.Perhatikan bahwa saya mengasumsikan
lg
klem ke hasil di atas 1 (yaitulg(x) = log_2(max(2,x))
). Itu sebabnya ketikaw=h
, artinyat=1
, kita mendapatkan batas yang diharapkanO(b lg(1)) = O(b) = O(w+h)
.Kode
public static Tuple<int, int> TryFindItemInSortedMatrix<T>(this IReadOnlyList<IReadOnlyList<T>> grid, T item, IComparer<T> comparer = null) { if (grid == null) throw new ArgumentNullException("grid"); comparer = comparer ?? Comparer<T>.Default; // check size var width = grid.Count; if (width == 0) return null; var height = grid[0].Count; if (height < width) { var result = grid.LazyTranspose().TryFindItemInSortedMatrix(item, comparer); if (result == null) return null; return Tuple.Create(result.Item2, result.Item1); } // search var minCol = 0; var maxRow = height - 1; var t = height / width; while (minCol < width && maxRow >= 0) { // query the item in the minimum column, t above the maximum row var luckyRow = Math.Max(maxRow - t, 0); var cmpItemVsLucky = comparer.Compare(item, grid[minCol][luckyRow]); if (cmpItemVsLucky == 0) return Tuple.Create(minCol, luckyRow); // did we eliminate t rows from the bottom? if (cmpItemVsLucky < 0) { maxRow = luckyRow - 1; continue; } // we eliminated most of the current minimum column // spend lg(t) time eliminating rest of column var minRowInCol = luckyRow + 1; var maxRowInCol = maxRow; while (minRowInCol <= maxRowInCol) { var mid = minRowInCol + (maxRowInCol - minRowInCol + 1) / 2; var cmpItemVsMid = comparer.Compare(item, grid[minCol][mid]); if (cmpItemVsMid == 0) return Tuple.Create(minCol, mid); if (cmpItemVsMid > 0) { minRowInCol = mid + 1; } else { maxRowInCol = mid - 1; maxRow = mid - 1; } } minCol += 1; } return null; }
sumber
O(b*(lg(t)+1))
penggantiO(b*lg(t))
. Tulisan yang bagus, khususnya. untuk menarik perhatian pada "teknik musuh" dalam menunjukkan ikatan "kasus terburuk".Saya akan menggunakan strategi bagi-dan-taklukkan untuk masalah ini, mirip dengan yang Anda sarankan, tetapi detailnya sedikit berbeda.
Ini akan menjadi pencarian rekursif pada subrange matriks.
Di setiap langkah, pilih elemen di tengah rentang. Jika nilai yang ditemukan adalah apa yang Anda cari, maka Anda sudah selesai.
Sebaliknya, jika nilai yang ditemukan lebih kecil dari nilai yang Anda cari, maka Anda tahu bahwa nilai tersebut tidak berada di kuadran atas dan di kiri posisi Anda saat ini. Jadi secara rekursif mencari dua subrange: semuanya (eksklusif) di bawah posisi saat ini, dan semua (eksklusif) di sebelah kanan yang berada di atau di atas posisi saat ini.
Jika tidak, (nilai yang ditemukan lebih besar dari nilai yang Anda cari) Anda tahu bahwa nilai tersebut tidak berada di kuadran di bawah dan di sebelah kanan posisi Anda saat ini. Jadi secara rekursif mencari dua subrange: semuanya (eksklusif) di kiri posisi saat ini, dan semua (eksklusif) di atas posisi saat ini yang ada di kolom saat ini atau kolom di sebelah kanan.
Dan ba-da-bing, Anda menemukannya.
Perhatikan bahwa setiap panggilan rekursif hanya menangani subrentang saat ini saja, bukan (misalnya) SEMUA baris di atas posisi saat ini. Hanya yang ada di subrentang saat ini.
Berikut beberapa pseudocode untuk Anda:
sumber
Dua jawaban utama yang diberikan sejauh ini tampaknya adalah
O(log N)
"metode ZigZag" danO(N+M)
metode Pencarian Biner. Saya pikir saya akan melakukan beberapa pengujian yang membandingkan dua metode dengan beberapa pengaturan yang berbeda. Berikut detailnya:Array adalah N x N persegi di setiap pengujian, dengan N bervariasi dari 125 hingga 8000 (tumpukan JVM terbesar saya dapat menangani). Untuk setiap ukuran array, saya memilih tempat acak dalam array untuk meletakkan satu
2
. Saya kemudian meletakkan di3
mana saja mungkin (ke kanan dan di bawah 2) dan kemudian mengisi sisa array dengan1
. Beberapa pemberi komentar sebelumnya tampaknya berpikir jenis pengaturan ini akan menghasilkan run time kasus terburuk untuk kedua algoritme. Untuk setiap ukuran array, saya memilih 100 lokasi acak yang berbeda untuk 2 (target pencarian) dan menjalankan pengujian. Saya mencatat waktu pengoperasian rata-rata dan waktu pengoperasian kasus terburuk untuk setiap algoritme. Karena itu terjadi terlalu cepat untuk mendapatkan pembacaan ms yang baik di Java, dan karena saya tidak mempercayai nanoTime () Java, saya mengulangi setiap pengujian 1000 kali hanya untuk menambahkan faktor bias yang seragam setiap saat. Berikut hasilnya:ZigZag mengalahkan biner di setiap pengujian untuk waktu rata-rata dan kasus terburuk, namun, semuanya berada dalam urutan besarnya satu sama lain lebih atau kurang.
Ini kode Java:
public class SearchSortedArray2D { static boolean findZigZag(int[][] a, int t) { int i = 0; int j = a.length - 1; while (i <= a.length - 1 && j >= 0) { if (a[i][j] == t) return true; else if (a[i][j] < t) i++; else j--; } return false; } static boolean findBinarySearch(int[][] a, int t) { return findBinarySearch(a, t, 0, 0, a.length - 1, a.length - 1); } static boolean findBinarySearch(int[][] a, int t, int r1, int c1, int r2, int c2) { if (r1 > r2 || c1 > c2) return false; if (r1 == r2 && c1 == c2 && a[r1][c1] != t) return false; if (a[r1][c1] > t) return false; if (a[r2][c2] < t) return false; int rm = (r1 + r2) / 2; int cm = (c1 + c2) / 2; if (a[rm][cm] == t) return true; else if (a[rm][cm] > t) { boolean b1 = findBinarySearch(a, t, r1, c1, r2, cm - 1); boolean b2 = findBinarySearch(a, t, r1, cm, rm - 1, c2); return (b1 || b2); } else { boolean b1 = findBinarySearch(a, t, r1, cm + 1, rm, c2); boolean b2 = findBinarySearch(a, t, rm + 1, c1, r2, c2); return (b1 || b2); } } static void randomizeArray(int[][] a, int N) { int ri = (int) (Math.random() * N); int rj = (int) (Math.random() * N); a[ri][rj] = 2; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (i == ri && j == rj) continue; else if (i > ri || j > rj) a[i][j] = 3; else a[i][j] = 1; } } } public static void main(String[] args) { int N = 8000; int[][] a = new int[N][N]; int randoms = 100; int repeats = 1000; long start, end, duration; long zigMin = Integer.MAX_VALUE, zigMax = Integer.MIN_VALUE; long binMin = Integer.MAX_VALUE, binMax = Integer.MIN_VALUE; long zigSum = 0, zigAvg; long binSum = 0, binAvg; for (int k = 0; k < randoms; k++) { randomizeArray(a, N); start = System.currentTimeMillis(); for (int i = 0; i < repeats; i++) findZigZag(a, 2); end = System.currentTimeMillis(); duration = end - start; zigSum += duration; zigMin = Math.min(zigMin, duration); zigMax = Math.max(zigMax, duration); start = System.currentTimeMillis(); for (int i = 0; i < repeats; i++) findBinarySearch(a, 2); end = System.currentTimeMillis(); duration = end - start; binSum += duration; binMin = Math.min(binMin, duration); binMax = Math.max(binMax, duration); } zigAvg = zigSum / randoms; binAvg = binSum / randoms; System.out.println(findZigZag(a, 2) ? "Found via zigzag method. " : "ERROR. "); //System.out.println("min search time: " + zigMin + "ms"); System.out.println("max search time: " + zigMax + "ms"); System.out.println("avg search time: " + zigAvg + "ms"); System.out.println(); System.out.println(findBinarySearch(a, 2) ? "Found via binary search method. " : "ERROR. "); //System.out.println("min search time: " + binMin + "ms"); System.out.println("max search time: " + binMax + "ms"); System.out.println("avg search time: " + binAvg + "ms"); } }
sumber
Ini adalah bukti singkat batas bawah masalah.
Anda tidak dapat melakukannya dengan lebih baik daripada waktu linier (dalam hal dimensi array, bukan jumlah elemen). Dalam larik di bawah ini, setiap elemen yang ditandai sebagai
*
dapat berupa 5 atau 6 (terlepas dari yang lain). Jadi, jika nilai target Anda adalah 6 (atau 5), algoritme perlu memeriksa semuanya.Tentu saja ini meluas ke array yang lebih besar juga. Artinya jawaban ini sudah optimal.
Pembaruan: Seperti yang ditunjukkan oleh Jeffrey L Whitledge, ini hanya optimal sebagai batas bawah asimtotik pada waktu berjalan vs ukuran data masukan (diperlakukan sebagai variabel tunggal). Waktu berjalan yang diperlakukan sebagai fungsi dua variabel pada kedua dimensi array dapat ditingkatkan.
sumber
Saya pikir Inilah jawabannya dan berfungsi untuk semua jenis matriks yang diurutkan
bool findNum(int arr[][ARR_MAX],int xmin, int xmax, int ymin,int ymax,int key) { if (xmin > xmax || ymin > ymax || xmax < xmin || ymax < ymin) return false; if ((xmin == xmax) && (ymin == ymax) && (arr[xmin][ymin] != key)) return false; if (arr[xmin][ymin] > key || arr[xmax][ymax] < key) return false; if (arr[xmin][ymin] == key || arr[xmax][ymax] == key) return true; int xnew = (xmin + xmax)/2; int ynew = (ymin + ymax)/2; if (arr[xnew][ynew] == key) return true; if (arr[xnew][ynew] < key) { if (findNum(arr,xnew+1,xmax,ymin,ymax,key)) return true; return (findNum(arr,xmin,xmax,ynew+1,ymax,key)); } else { if (findNum(arr,xmin,xnew-1,ymin,ymax,key)) return true; return (findNum(arr,xmin,xmax,ymin,ynew-1,key)); } }
sumber
Pertanyaan menarik. Pertimbangkan ide ini - buat satu batas di mana semua angkanya lebih besar dari target Anda dan yang lain di mana semua angkanya kurang dari target Anda. Jika ada yang tertinggal di antara keduanya, itu target Anda.
Jika saya mencari 3 dalam contoh Anda, saya membaca di baris pertama sampai saya mencapai 4, lalu mencari angka terdekat terkecil (termasuk diagonal) lebih besar dari 3:
1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11
Sekarang saya melakukan hal yang sama untuk angka-angka yang kurang dari 3:
1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11
Sekarang saya bertanya, apakah ada yang di dalam dua batasan itu? Jika ya, pasti 3. Jika tidak, maka tidak ada 3. Urutkan tidak langsung karena saya tidak benar-benar menemukan nomornya, saya hanya menyimpulkan bahwa itu pasti ada. Ini memiliki bonus tambahan untuk menghitung SEMUA 3.
Saya mencoba ini pada beberapa contoh dan tampaknya berfungsi dengan baik.
sumber
Pencarian biner melalui diagonal array adalah pilihan terbaik. Kita dapat mengetahui apakah elemen tersebut kurang dari atau sama dengan elemen di diagonal.
sumber
A. Lakukan pencarian biner pada baris di mana nomor target mungkin berada.
B.Buatlah menjadi grafik: Carilah angkanya dengan selalu mengambil simpul tetangga terkecil yang belum dikunjungi dan mundur ketika nomor yang terlalu besar ditemukan
sumber
Pencarian biner akan menjadi pendekatan terbaik, imo. Mulai dari 1/2 x, 1/2 y akan dipotong menjadi dua. IE persegi 5x5 akan menjadi seperti x == 2 / y == 3. Saya membulatkan satu nilai ke bawah dan satu nilai ke atas ke zona yang lebih baik ke arah nilai yang ditargetkan.
Untuk kejelasan, iterasi berikutnya akan memberi Anda sesuatu seperti x == 1 / y == 2 ATAU x == 3 / y == 5
sumber
Untuk memulainya, mari kita asumsikan kita menggunakan persegi.
1. Mencari kotak
Saya akan menggunakan pencarian biner di diagonal. Tujuannya adalah menemukan angka yang lebih kecil yang tidak lebih rendah dari angka target.
Katakanlah saya mencari
4
misalnya, maka saya akan menemukan5
di(2,2)
.Kemudian, saya yakin bahwa jika
4
ada di tabel, itu ada di posisi baik(x,2)
atau(2,x)
denganx
masuk[0,2]
. Nah, itu hanya 2 pencarian biner.Kompleksitasnya tidak menakutkan:
O(log(N))
(3 pencarian biner pada rentang panjangN
)2. Mencari pendekatan yang naif dan persegi panjang
Tentu saja, ini menjadi sedikit lebih rumit ketika
N
danM
berbeda (dengan persegi panjang), pertimbangkan kasus yang merosot ini:Dan katakanlah saya mencari
9
... Pendekatan diagonal masih bagus, tetapi definisi perubahan diagonal. Di sini diagonal saya[1, (5 or 6), 17]
. Katakanlah saya mengambil[1,5,17]
, maka saya tahu bahwa jika9
ada di tabel itu ada di sub bagian:Ini memberi kita 2 persegi panjang:
Jadi kita bisa mengulang! mungkin dimulai dengan yang memiliki lebih sedikit elemen (meskipun dalam hal ini membunuh kita).
Saya harus menunjukkan bahwa jika salah satu dimensi lebih kecil dari
3
, kita tidak dapat menerapkan metode diagonal dan harus menggunakan pencarian biner. Ini artinya:10 11 12 13 14 15 16
, tidak ditemukan5 6 7 8
, tidak ditemukan6 7 8 9
, tidak ditemukanIni rumit karena untuk mendapatkan kinerja yang baik Anda mungkin ingin membedakan antara beberapa kasing, tergantung pada bentuk umum ....
3. Pencarian persegi panjang, pendekatan brutal
Akan jauh lebih mudah jika kita berurusan dengan persegi ... jadi mari kita persegi.
Kami sekarang memiliki persegi.
Tentu saja, kami mungkin TIDAK akan benar-benar membuat baris tersebut, kami dapat dengan mudah meniru mereka.
sehingga berperilaku seperti persegi tanpa menempati lebih banyak memori (dengan mengorbankan kecepatan, mungkin, tergantung pada cache ... oh well: p)
sumber
EDIT:
Saya salah paham dengan pertanyaan itu. Seperti yang ditunjukkan oleh komentar, ini hanya berfungsi dalam kasus yang lebih terbatas.
Dalam bahasa seperti C yang menyimpan data dalam urutan baris-mayor, perlakukan saja sebagai larik 1D berukuran n * m dan gunakan penelusuran biner.
sumber
Saya memiliki Solusi Divide & Conquer rekursif. Ide Dasar untuk satu langkah adalah: Kita tahu bahwa Kiri-Atas (LU) terkecil dan kanan-bawah (RB) adalah no terbesar, jadi No (N) yang diberikan harus: N> = LU dan N <= RB
IF N == LU dan N == RB :::: Element Found dan Abort mengembalikan posisi / Index Jika N> = LU dan N <= RB = FALSE, No tidak ada dan batalkan. Jika N> = LU dan N <= RB = TRUE, Bagilah larik 2D dalam 4 bagian yang sama dari larik 2D masing-masing secara logis .. Dan kemudian terapkan langkah algo yang sama ke keempat sub-larik.
Algo saya Benar Saya telah menerapkan di PC teman saya. Kompleksitas: masing-masing 4 perbandingan dapat digunakan untuk menyimpulkan tidak total elemen menjadi seperempat pada kasus terburuknya .. Jadi kompleksitas saya menjadi 1 + 4 x lg (n) + 4 Tapi benar-benar berharap ini akan bekerja pada O (n)
Saya pikir ada yang salah di suatu tempat dalam penghitungan Kompleksitas saya, harap perbaiki jika demikian ..
sumber
Solusi optimal adalah mulai dari sudut kiri atas, yang memiliki nilai minimal. Pindah secara diagonal ke bawah ke kanan sampai Anda mencapai elemen yang nilainya> = nilai elemen yang diberikan. Jika nilai elemen sama dengan elemen yang diberikan, kembalikan ditemukan sebagai benar.
Jika tidak, dari sini kita dapat melanjutkan dengan dua cara.
Strategi 1:
Strategi 2: Misalkan i menunjukkan indeks baris dan j menunjukkan indeks kolom dari elemen diagonal tempat kita berhenti. (Di sini, kami memiliki i = j, BTW). Misalkan k = 1.
1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11
sumber
public boolean searchSortedMatrix(int arr[][] , int key , int minX , int maxX , int minY , int maxY){ // base case for recursion if(minX > maxX || minY > maxY) return false ; // early fails // array not properly intialized if(arr==null || arr.length==0) return false ; // arr[0][0]> key return false if(arr[minX][minY]>key) return false ; // arr[maxX][maxY]<key return false if(arr[maxX][maxY]<key) return false ; //int temp1 = minX ; //int temp2 = minY ; int midX = (minX+maxX)/2 ; //if(temp1==midX){midX+=1 ;} int midY = (minY+maxY)/2 ; //if(temp2==midY){midY+=1 ;} // arr[midX][midY] = key ? then value found if(arr[midX][midY] == key) return true ; // alas ! i have to keep looking // arr[midX][midY] < key ? search right quad and bottom matrix ; if(arr[midX][midY] < key){ if( searchSortedMatrix(arr ,key , minX,maxX , midY+1 , maxY)) return true ; // search bottom half of matrix if( searchSortedMatrix(arr ,key , midX+1,maxX , minY , maxY)) return true ; } // arr[midX][midY] > key ? search left quad matrix ; else { return(searchSortedMatrix(arr , key , minX,midX-1,minY,midY-1)); } return false ; }
sumber
Saya sarankan, simpan semua karakter dalam file
2D list
. kemudian temukan indeks elemen yang diperlukan jika ada dalam daftar.Jika tidak ada, cetak pesan yang sesuai, jika tidak, cetak baris dan kolom sebagai:
row = (index/total_columns)
dancolumn = (index%total_columns -1)
Ini hanya akan menghasilkan waktu pencarian biner dalam daftar.
Harap sarankan koreksi apa pun. :)
sumber
Jika solusi O (M log (N)) baik-baik saja untuk array MxN -
Bekerja demo C ++.
Tolong beri tahu saya jika ini tidak akan berhasil atau jika ada bug.
sumber
Saya telah menanyakan pertanyaan ini dalam wawancara selama lebih dari satu dekade dan saya pikir hanya ada satu orang yang dapat menghasilkan algoritma yang optimal.
Solusi saya selalu:
Pencarian biner di tengah diagonal, yang merupakan diagonal mengalir ke bawah dan ke kanan, berisi item di
(rows.count/2, columns.count/2)
.Jika nomor target ditemukan, kembalikan true.
Jika tidak, dua angka (
u
danv
) akan ditemukan sehinggau
lebih kecil dari target,v
lebih besar dari target, danv
satu kanan dan satu turun dariu
.Mencari sub-matriks secara berulang di kanan
u
dan atas dan sub-matriksv
di bawahu
dan kiriv
.Saya percaya ini adalah peningkatan yang ketat atas algoritma yang diberikan oleh Nate di sini , karena mencari diagonal sering kali memungkinkan pengurangan lebih dari setengah ruang pencarian (jika matriksnya mendekati persegi), sedangkan mencari baris atau kolom selalu menghasilkan eliminasi dari tepat setengah.
Berikut kode di Swift (mungkin tidak terlalu Swifty):
import Cocoa class Solution { func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { if (matrix.isEmpty || matrix[0].isEmpty) { return false } return _searchMatrix(matrix, 0..<matrix.count, 0..<matrix[0].count, target) } func _searchMatrix(_ matrix: [[Int]], _ rows: Range<Int>, _ columns: Range<Int>, _ target: Int) -> Bool { if (rows.count == 0 || columns.count == 0) { return false } if (rows.count == 1) { return _binarySearch(matrix, rows.lowerBound, columns, target, true) } if (columns.count == 1) { return _binarySearch(matrix, columns.lowerBound, rows, target, false) } var lowerInflection = (-1, -1) var upperInflection = (Int.max, Int.max) var currentRows = rows var currentColumns = columns while (currentRows.count > 0 && currentColumns.count > 0 && upperInflection.0 > lowerInflection.0+1) { let rowMidpoint = (currentRows.upperBound + currentRows.lowerBound) / 2 let columnMidpoint = (currentColumns.upperBound + currentColumns.lowerBound) / 2 let value = matrix[rowMidpoint][columnMidpoint] if (value == target) { return true } if (value > target) { upperInflection = (rowMidpoint, columnMidpoint) currentRows = currentRows.lowerBound..<rowMidpoint currentColumns = currentColumns.lowerBound..<columnMidpoint } else { lowerInflection = (rowMidpoint, columnMidpoint) currentRows = rowMidpoint+1..<currentRows.upperBound currentColumns = columnMidpoint+1..<currentColumns.upperBound } } if (lowerInflection.0 == -1) { lowerInflection = (upperInflection.0-1, upperInflection.1-1) } else if (upperInflection.0 == Int.max) { upperInflection = (lowerInflection.0+1, lowerInflection.1+1) } return _searchMatrix(matrix, rows.lowerBound..<lowerInflection.0+1, upperInflection.1..<columns.upperBound, target) || _searchMatrix(matrix, upperInflection.0..<rows.upperBound, columns.lowerBound..<lowerInflection.1+1, target) } func _binarySearch(_ matrix: [[Int]], _ rowOrColumn: Int, _ range: Range<Int>, _ target: Int, _ searchRow : Bool) -> Bool { if (range.isEmpty) { return false } let midpoint = (range.upperBound + range.lowerBound) / 2 let value = (searchRow ? matrix[rowOrColumn][midpoint] : matrix[midpoint][rowOrColumn]) if (value == target) { return true } if (value > target) { return _binarySearch(matrix, rowOrColumn, range.lowerBound..<midpoint, target, searchRow) } else { return _binarySearch(matrix, rowOrColumn, midpoint+1..<range.upperBound, target, searchRow) } } }
sumber
Diberikan matriks persegi sebagai berikut:
Kita tahu bahwa a <c, d <f, i <k. Yang tidak kami ketahui adalah apakah d <c atau d> c, dll. Kami memiliki jaminan hanya dalam 1-dimensi.
Melihat elemen akhir (c, f, k), kita dapat melakukan semacam filter: apakah N <c? pencarian (): berikutnya (). Jadi, kami memiliki n iterasi di atas baris, dengan setiap baris mengambil O (log (n)) untuk pencarian biner atau O (1) jika disaring.
Izinkan saya memberikan CONTOH dimana N = j,
Coba lagi dengan N = q,
Mungkin ada solusi yang lebih baik di luar sana tetapi ini mudah dijelaskan .. :)
sumber
Karena ini adalah pertanyaan wawancara, ini tampaknya mengarah pada diskusi tentang pemrograman Paralel dan algoritma pengurangan peta .
Lihat http://code.google.com/intl/de/edu/parallel/mapreduce-tutorial.html
sumber