Search in Rotated Sorted Array
Birbirinden farklı tam sayılardan oluşan bir liste artan sırada sıralandı ve ardından döndürüldü: muhtemelen sıfır olmak üzere belirli sayıda eleman baştan alınıp aynı sırayla sona taşındı. Örneğin, [2, 5, 8, 11, 15, 19, 23] 4 konum döndürüldüğünde [15, 19, 23, 2, 5, 8, 11] olur. Döndürülmüş nums listesini ve bir target tam sayısını alıyorsun. target değerinin nums içindeki indeksini 0'dan başlayarak sayıp döndür; listede yoksa -1 döndür. Bunu O(log n) zamanda yap.
Fonksiyon
- numsinteger-array
- döndürülmüş sıralı farklı tam sayı listesi
- targetinteger
- aranacak değer
- Döndürürinteger
- nums içinde target'ın indeksi veya yoksa -1
Kısıtlar
1 ≤ nums.length ≤ 5000-104 ≤ nums[i], target ≤ 104- <|?
nums,0 ≤ k < nums.lengthkoşulunu sağlayan birkkadar döndürülmüş artan bir listedir;k = 0listeyi döndürülmemiş hâlde bırakır.
Örnekler
- Girdi
- nums = [15, 19, 23, 2, 5, 8, 11]target = 5
- Çıktı
- 4
- Açıklama
- İlk orta eleman, indeks 3, 2 değerini tutar; bu nedenle sağ yarı
[2, 5, 8, 11]sıralıdır ve 5 değerini tutar. Sonraki orta eleman, indeks 5, 8 değerini tutar; sıralı sol bölüm[5, 8]5 değerini tutar ve bu da indeks 4'e götürür.
- Girdi
- nums = [40, 50, 60, 70, 10, 20, 30]target = 65
- Çıktı
- -1
- Açıklama
- 65, 60 ile 70 arasına ait olur ve onu hiçbir öğe içermez. İlk orta değer olan 70, 65'i sıralanmış sol kısım
[40, 50, 60, 70]içine yerleştirir ve aralık boşalana kadar orada daralır.
- Girdi
- nums = [8, 13, 21, 1, 3, 5]target = 13
- Çıktı
- 1
- Açıklama
- İlk orta eleman, 2. indeks, 21 değerini tutar. Sol kısım
[8, 13, 21]sıralıdır ve 13, 8 ile 21 arasındadır; bu nedenle sağ kısım elenir. Arama daha sonra 13'ü 1. indekste bulur.
Ek soru
Eğer nums yinelenen değerler içerebiliyorsa, hiçbir algoritma O(log n) garantisi veremez. Tek bir 0'ı gizleyen, döndürülmüş bir 1'ler listesiyle bunu kanıtlayabilir misin?
İpuçları
Tek tek açın. Her biri biraz daha fazlasını gösterir.
Ortadaki herhangi bir indeksi seç ve onun iki yanındaki iki yarıya bak. Döndürme, değerlerin en büyükten en küçüğe doğru düştüğü bir yer oluşturdu. Bu düşüş her iki yarıda da bulunabilir mi?
En az bir yarı her zaman sıralıdır ve
nums[lo]ilenums[mid]değerlerini karşılaştırmak hangisinin sıralı olduğunu gösterir. Sıralı bir yarıda,targetdeğerinin ilk ve son değer arasında olup olmadığını tek adımda kontrol edebilirsin.targetdeğerini hâlâ içerebilecek bölümün sınırları olaraklovehideğerlerini koru. Her adımda, sıralı yarının değer aralığıtargetdeğerini içeriyorsa o yarıyı koru; aksi hâlde diğer yarıyı koru.targetdeğerini bulduğunda veya aralık boşaldığında dur.
Çözüm
Döndürülmüş sıralı bir liste, iki sıralı parçadan oluşur: [15, 19, 23] ve ardından [2, 5, 8, 11]. target değerini ortadaki değerle karşılaştırmak, artık hangi tarafta bulunduğunu söylemez. Ancak listeyi nereden bölersen böl, en az bir yarısı sıralıdır ve tek bir karşılaştırma, target değerinin o yarıda olup olamayacağını söyler.
Her öğeyi tara
Sezgi
Her dizini kontrol et ve değeri target değerine eşit olan ilk dizini döndür; döngüden sonra -1 döndür. Her öğeyi okuduğu için her liste için doğrudur. Sıralanmış iki parçayı göz ardı eder: 5000 öğe en fazla 5000 karşılaştırmaya mal olurken ikili arama 13 karşılaştırma gerektirir.
Algoritma
- 0'dan
n-1'e kadar heriindeksi içinnums[i]iletarget'ı karşılaştır. - Eşitlerse
ideğerini döndür. - Döngüden sonra
-1değerini döndür.
def search(nums, target):
for i, value in enumerate(nums):
if value == target:
return i
return -1Dönme noktasını bul, ardından ikili arama yap
Sezgi
İkinci parça en küçük değerden başlar; bu değerin indeksine k diyelim. Hem nums[k..n-1] hem de nums[0..k-1] sıralıdır ve ilk parçadaki her değer, ikinci parçadaki her değerden büyüktür. k değerini bulduktan sonra target değerini nums[k] ve nums[n-1] ile karşılaştırarak hangi parçanın kullanılacağını seçebilir, ardından standart ikili aramayı uygulayabilirsin.
k değerini bulmak için ortadaki değeri nums[hi] ile karşılaştır. nums[mid] > nums[hi] ise değerler mid konumundan sonra bir yerde düşer; dolayısıyla minimum sağındadır. Aksi takdirde nums[mid..hi] aralığı düşüş olmadan yükselir; dolayısıyla minimum mid konumunda ya da ondan öncedir. İlk örnekte orta değerler 2, 19 ve 23'tür; bu da k = 3 sonucunu verir ve 5, nums[3] = 2 ile nums[6] = 11 arasındadır. İki ikili arama yaklaşık 2 log2 n adım sürer.
Algoritma
lo = 0vehi = n-1olarak ayarla.lo < hiolduğu sürecemiddeğerini hesapla;nums[mid] > nums[hi]iselo = mid + 1olarak ayarla, aksi hâldehi = midolarak ayarla.- Son indeks
k, en küçük değeri tutar. nums[k] ≤ target ≤ nums[n-1]isekilen-1arasındaki indekslerde ikili arama yap; aksi hâlde 0 ilek-1arasında ara.- Bulunan indeksi döndür veya aralık boşalırsa
-1döndür.
def search(nums, target):
n = len(nums)
# 1. Find k, the index of the smallest value, where the second run starts.
lo, hi = 0, n - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] > nums[hi]:
lo = mid + 1 # the drop is right of mid
else:
hi = mid # mid is in the low run: the minimum is at mid or left of it
k = lo
# 2. nums[k..n-1] and nums[0..k-1] are sorted: search the one whose range holds target.
if nums[k] <= target <= nums[n - 1]:
lo, hi = k, n - 1
else:
lo, hi = 0, k - 1
while lo <= hi:
mid = (lo + hi) // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1Sıralanmış yarıda bir ikili arama
Sezgi
İkili arama ilkesini koruyun: target listede bulunuyorsa, indeksi lo ile hi arasındadır. Değerler tüm listede yalnızca bir kez düşer; bu nedenle mid etrafındaki iki yarıdan en fazla biri düşüşü içerir, diğeri ise sıralıdır. nums[lo] ≤ nums[mid] ise sol yarı sıralıdır; değilse sağ yarı sıralıdır.
Sıralı yarı için, uçlarındaki iki değer üzerinde yapılan bir aralık testi target değerinin bu aralıkta olup olamayacağını gösterir; örneğin sol yarıda nums[lo] ≤ target < nums[mid]. Olabiliyorsa orada arama yapın; olamıyorsa target yalnızca diğer yarıda olabilir. Her adımda aralık yarıya iner: 5000 öğe için en fazla 13 adım gerekir.
Algoritma
lo = 0vehi = n-1olarak ayarla.lo ≤ hiolduğu sürecemiddeğerini hesapla venums[mid],targetdeğerine eşitsemiddeğerini döndür.nums[lo] ≤ nums[mid]ise sol yarı sıralıdır:nums[lo] ≤ target < nums[mid]olduğundahi = mid - 1, aksi hâldelo = mid + 1olarak ayarla.- Aksi hâlde sağ yarı sıralıdır:
nums[mid] < target ≤ nums[hi]olduğundalo = mid + 1, aksi hâldehi = mid - 1olarak ayarla. - Döngü sona erdiğinde
-1değerini döndür.
def search(nums, target):
lo, hi = 0, len(nums) - 1 # target, if present, sits in nums[lo..hi]
while lo <= hi:
mid = (lo + hi) // 2
if nums[mid] == target:
return mid
if nums[lo] <= nums[mid]:
# nums[lo..mid] is sorted: target is in it only if it fits its range
if nums[lo] <= target < nums[mid]:
hi = mid - 1
else:
lo = mid + 1
else:
# nums[mid..hi] is sorted
if nums[mid] < target <= nums[hi]:
lo = mid + 1
else:
hi = mid - 1
return -1
Tuzaklar ve uç durumlar
nums[lo] < nums[mid]yerine≤yazmak. İki öğe kaldığındamid,lo'ya eşittir ve katı koşul[9, 4]dizisini sıralı bir sağ yarı olarak kabul eder:target = 4,-1döndürür.- Önce
targetilenums[mid]'i karşılaştırmak.target = 19olan ilk örnekte ortadaki 2 daha küçüktür; bu nedenle arama sağa ilerler ve 1. indeksi kaçırır. - Sıralı yarının yalnızca bir ucunu sınamak.
[40, 50, 60, 70, 80, 10, 20]içindetarget = 80olduğunda, yalnızcatarget ≥ nums[lo]koşulu aramayı[40, 50, 60, 70]aralığına yönlendirir. - Rust'ta, döndürülmemiş liste olan
k = 0durumunda,usizetüründekik - 1taşmaya yol açar. Yarı açık aralıklar kullanın. - Lua ve R'de 1 tabanlı konumu döndürmek. 1 çıkarın.
Sıkça sorulan sorular3
Döndürülmüş sıralı bir dizide arama yapmanın zaman karmaşıklığı nedir?
O(log n) zaman ve O(1) ek alan. Her adım aralığın yarısını korur, bu nedenle 5000 öğe en fazla 13 adım gerektirir. Önce döndürme noktasını bulmak da O(log n) zaman alır ve yaklaşık iki katı kadar adım gerektirir.
Algoritma, dizi yinelenen öğeler içerdiğinde çalışır mı?
Yazıldığı şekliyle olmaz. nums[lo], nums[mid] ve nums[hi] eşit olduğunda, iki yarımdan hiçbirinin sıralı olduğu kanıtlanamaz. Bu durumda yaygın çözüm lo değerini bir artırır; bu, yanıtın doğruluğunu korur ancak en kötü durum karmaşıklığını O(n) yapar.
Dönme noktasını önce mi bulmalısın, yoksa tek geçişte mi aramalısın?
İkisi de O(log n) süresinde çalışır. Önce minimumu bulmak, problemi iki basit ikili aramaya böler. Tek geçişli arama işi daha az adımla tek bir döngüde yapar ve çoğu görüşmecinin beklediği sürüm budur.
Benzer problemler
Aynı fikirleri kullanan problemler. İki üçünü çözmek bir kalıbı kalıcı hale getirir.
def search(nums, target):
# Kodu buraya yazınTest #1
Test #2
Test #3
Argümanlar
nums = [15, 19, 23, 2, 5, 8, 11] target = 5
Beklenen Çıktı
4