Palindrome Linked List
Aynı uzunluktaki iki dizide saklanan tek yönlü bağlı bir liste veriliyor. i düğümü values[i] değerini tutar ve next[i] düğümüne bağlanır; -1 listenin sonunu belirtir ve baş düğüm 0'dır. Düğümler liste sırasına göre saklanmadığından bağlantıları takip edin.
Değerler baştan sona ve sondan başa okunduğunda aynıysa true, aksi takdirde false döndürün.
Fonksiyon
- nextinteger-array
- her düğümün bağlantı verdiği düğümün indeksi veya son düğüm için -1
- valuesinteger-array
- her düğümün tuttuğu değer
- Döndürürboolean
- liste her iki yönde de aynı okunuyorsa true
Kısıtlar
1 ≤ L ≤ 5000; buradaL,nextvevaluesuzunluğudur.-100 ≤ values[i] ≤ 100- Her
next[i],-1ya da0ileL-1arasında bir düğüm indeksidir. -
0düğümünden başlayarak liste her düğümü tam olarak bir kez ziyaret eder ve ardından-1'e ulaşır. Döngü yoktur.
Örnekler
- Girdi
- next = [4, 3, -1, 2, 1]values = [3, 4, 3, 1, 1]
- Çıktı
- true
- Açıklama
- Bağlantılar
0, 4, 1, 3, 2düğümlerini ziyaret eder; bu nedenle liste3, 1, 4, 1, 3şeklindedir. İki 3 eşleşir, iki 1 eşleşir ve ortadaki4için kontrol edilecek bir eş yoktur.
- Girdi
- next = [2, -1, 3, 1]values = [4, 4, 9, 9]
- Çıktı
- true
- Açıklama
- Liste,
0, 2, 3, 1düğümlerini ziyaret eder ve her iki yönde de aynı olan4, 9, 9, 4değerlerini okur.
- Girdi
- next = [2, -1, 1]values = [1, 2, 1]
- Çıktı
- false
- Açıklama
valuesdizisi[1, 2, 1], ancak liste0, 2, 1düğümlerini ziyaret eder ve1, 1, 2değerlerini okur. Tersten bakıldığında bu2, 1, 1olur, dolayısıyla cevapfalse.
Ek soru
Bunu O(L) zamanda ve O(1) ek bellekle yapabilir misin?
İpuçları
Tek tek açın. Her biri biraz daha fazlasını gösterir.
Palindrom kontrolü, ilk değeri son değerle, ikinci değeri sondan ikinci değerle ve bu şekilde devam ederek karşılaştırır. Tek yönlü bağlı bir listede yalnızca ileriye doğru ilerleyebilirsiniz. Listenin sonuna erişilebilir olmasını ne sağlar?
Değerleri liste sırasıyla bir diziye kopyalamak işe yarar, ancak
O(L)bellek gerektirir. Kopyalama olmadan ikinci yarıda geriye doğru ilerlemek gerekir. Bağlantılarını değiştirmek bunu mümkün kılabilir.İki işaretçiyle ortayı bulun:
fast,slowbir bağlantı ilerlediğinde iki bağlantı ilerler.fastdaha fazla ilerleyemediğindeslowilk yarının sonuna gelir.slow'dan sonraki bağlantıları tersine çevirin, ardından baştan ve eski son düğümden aynı anda ilerleyip karşılaştırın.
Çözüm
Palindrom kontrolü, her değeri diğer uçtaki karşılığıyla eşleştirir. Tek yönlü bağlı liste yalnızca ileri doğru ilerler; bu yüzden zor olan, arka yarıyı tersten okumaktır. Bir dizi kopyası bunu O(L) bellekle yapar; burada L, düğüm sayısıdır. İkinci yarının bağlantılarını tersine çevirmek ise bunu O(1) ile yapar.
Değerleri bir diziye kopyala
Sezgi
Buradaki bir işaretçi düğüm indeksidir ve ilerlemek için node = next[node] kullanılır. Listeyi bir kez dolaşıp her değeri, iki uçtan da okuyabileceğin bir diziye kopyala. İlk ve son değerleri karşılaştır, her iki indeksi de ortaya doğru ilerlet ve herhangi bir uyuşmazlıkta false döndür. Uzunluk tek olduğunda ortadaki değerin eşi kendisidir.
Her değer okunmak zorunda olduğundan, O(L) süre mümkün olan en iyi süredir. Bunun bedeli L ek tamsayıdır.
Algoritma
0düğümünden-1düğümüne kadar ilerleyin ve hervalues[node]değerini bir diziye ekleyin.i = 0olarak ayarlayın vejdeğerini son indeks olarak belirleyin.i < jiken, iki değer farklıysafalsedöndürün; aksi takdirdeideğerini artırın vejdeğerini azaltın.truedöndürün.
def isPalindrome(next, values):
# Write the values out in list order.
seq = []
node = 0
while node != -1:
seq.append(values[node])
node = next[node]
# Compare from both ends toward the middle.
i = 0
j = len(seq) - 1
while i < j:
if seq[i] != seq[j]:
return False
i += 1
j -= 1
return Trueİkinci yarıyı yerinde tersine çevir
Sezgi
Arka yarıyı kopyalamak yerine bağlantılarını tersine çevirin: eski son düğümden başlayarak ilerlemek sizi ortaya götürür. Ortayı bulmak için fast, slow her bir bağlantı ilerlediğinde iki bağlantı ilerlesin; bunu fast'in önünde iki bağlantı olduğu sürece yapın. slow, ilk yarının son düğümünde, L tek olduğunda ise orta düğümde durur. İlk örnekte slow 0 → 4 → 1 ilerlerken fast 0 → 1 → 2 ilerler; böylece slow 4'te durur.
slow'dan sonraki düğümleri tersine çevirin, ardından baş düğümden başlayan left ile tersine çevrilmiş yarıdan başlayan right'ı birlikte ilerletin: önce 3 ile 3, sonra 1 ile 1. Tersine çevrilmiş yarı hiçbir zaman ilk yarıdan uzun olmaz; bu nedenle right sona erdiğinde durmak, tam olarak önemli olan çiftleri karşılaştırır. Her aşama en fazla L düğüm boyunca ilerler ve birkaç tam sayı saklar. R'de next_'i değiştirmek vektörü bir kez kopyalar; bu nedenle R, O(L) bellek kullanır.
Algoritma
slow = fast = 0olarak ayarla.next[fast]venext[next[fast]]değerlerinin ikisi de-1olmadığı süreceslow'u bir bağlantı,fast'i iki bağlantı ilerlet.next[slow]konumunda başlayan listeyi tersine çevir: her düğüm için bağlantısını kaydet, onuprev'e yönlendir, ardındanprev'i ve düğümü ilerlet.left = 0ve ters çevrilmiş yarının başı olanright = prevolarak ayarla.right != -1olduğu sürece, bir uyuşmazlık varsafalsedöndür; aksi takdirde her iki işaretçiyi de bir bağlantı ilerlet.truedöndür.
def isPalindrome(next, values):
# Find the middle: fast moves two links for every one link slow moves.
slow = 0
fast = 0
while next[fast] != -1 and next[next[fast]] != -1:
slow = next[slow]
fast = next[next[fast]]
# slow is the last node of the first half (the middle node when the length is odd).
# Reverse the links of the second half.
prev = -1
node = next[slow]
while node != -1:
after = next[node]
next[node] = prev
prev = node
node = after
# prev is the old last node, now the head of the reversed half.
left = 0
right = prev
while right != -1:
if values[left] != values[right]:
return False
left = next[left]
right = next[right]
return True
Tuzaklar ve uç durumlar
- Liste yerine dizileri kontrol etmek. Üçüncü örnekte
values,[1, 2, 1]değerindedir, ancak liste1, 1, 2şeklindedir. - Yalnızca
next[fast] != -1koşulunu test etmek.fastdaha sonra-1değerine atlayıpnext[-1]değerini okuyabilir; bu, çoğu dilde aralık dışıdır ve Python ile Ruby'de sessizce son öğeyi verir. rightyerineleftbitene kadar karşılaştırmak. Orta düğüm hâlâ eski ikinci yarıya bağlıdır, bu yüzden baştan yapılan yürüyüş daha uzundur.- Tersine çevirme sırasında kaydetmeden önce
next[node]değerinin üzerine yazmak; bu, listenin geri kalanını kaybettirir. - Dizilerin 1'den başladığı Lua ve R'de, 0 tabanlı bir düğüm kimliği için
next[node]değerini okumak.next[node + 1]değerini oku.
Sıkça sorulan sorular3
Palindrom bağlı liste kontrolünün zaman karmaşıklığı nedir?
L düğümden oluşan bir liste için her yaklaşımın zaman karmaşıklığı O(L) olur, çünkü her değerin karşılaştırılması gerekir. Değerleri bir diziye veya yığına kopyalamak O(L) ek bellek kullanır; ikinci yarıyı yerinde tersine çevirmek ise O(1) kullanır.
Hızlı ve yavaş işaretçiler bağlı bir listenin ortasını nasıl bulur?
İkisi de baştan başlar ve hızlı işaretçi, yavaş işaretçinin her adımına karşılık iki adım atar. Hızlı işaretçi sona ulaştığında listenin tamamını, yavaş işaretçi ise yarısını katetmiş olur.
Listenin yarısını tersine çevirmek girdiyi bozar mı?
Evet, ikinci yarının bağlantılarını değiştirir ve çoğu dilde bu dizi çağırana aittir. Listeye hâlâ ihtiyaç varsa, erken dönmeden karşılaştırmayı tamamlayın, ardından o yarıyı tekrar ters çevirin. Orta düğüm hâlâ o yarının ilk düğümüne bağlıdır; böylece liste, O(1) ek bellekle yeniden bütün hâle gelir.
Benzer problemler
Aynı fikirleri kullanan problemler. İki üçünü çözmek bir kalıbı kalıcı hale getirir.
def isPalindrome(next, values):
# Kodu buraya yazınTest #1
Test #2
Test #3
Argümanlar
next = [4, 3, -1, 2, 1] values = [3, 4, 3, 1, 1]
Beklenen Çıktı
true