Katakanlah Anda memiliki struktur daftar tertaut di Jawa. Ini terdiri dari Nodes:
class Node {
Node next;
// some user data
}
dan setiap Node menunjuk ke node berikutnya, kecuali untuk Node terakhir, yang memiliki null untuk selanjutnya. Katakanlah ada kemungkinan bahwa daftar tersebut dapat berisi loop - yaitu Node akhir, daripada memiliki nol, memiliki referensi ke salah satu node dalam daftar yang datang sebelumnya.
Apa cara penulisan terbaik
boolean hasLoop(Node first)
yang akan kembali true
jika Node yang diberikan adalah yang pertama dari daftar dengan loop, dan false
sebaliknya? Bagaimana Anda bisa menulis sehingga dibutuhkan ruang yang konstan dan waktu yang masuk akal?
Berikut ini gambaran tampilan daftar dengan lingkaran:
java
algorithm
data-structures
linked-list
jjujuma
sumber
sumber
finite amount of space and a reasonable amount of time?
:)Jawaban:
Anda dapat menggunakan algoritme pencarian siklus Floyd , yang juga dikenal sebagai algoritma kura-kura dan kelinci .
Idenya adalah memiliki dua referensi ke daftar dan memindahkannya dengan kecepatan yang berbeda . Pindahkan satu maju dengan
1
simpul dan lainnya dengan2
simpul.next
) akan menjadinull
.Fungsi Java mengimplementasikan algoritma:
sumber
fast.next
sebelum meneleponnext
lagi:if(fast.next!=null)fast=fast.next.next;
Inilah penyempurnaan dari solusi Cepat / Lambat, yang dengan benar menangani daftar panjang ganjil dan meningkatkan kejelasan.
sumber
slow == fast.next
kemudianslow
akan samafast
pada iterasi berikutnya; hanya menghemat satu iterasi paling banyak dengan mengorbankan tes tambahan untuk setiap iterasi.slow
tidak dapat menjadi nol sebelumnyafast
karena mengikuti jalur referensi yang sama (kecuali jika Anda memiliki modifikasi daftar bersamaan dalam hal ini semua taruhan dimatikan).Lebih baik daripada algoritma Floyd
Richard Brent menggambarkan algoritma pendeteksi siklus alternatif , yang mirip seperti kelinci dan kura-kura [Floyd's cycle] kecuali bahwa, simpul lambat di sini tidak bergerak, tetapi kemudian "diteleportasikan" ke posisi simpul cepat di fix interval.
Deskripsi tersedia di sini: http://www.siafoo.net/algorithm/11 Brent mengklaim bahwa algoritmanya 24 hingga 36% lebih cepat daripada algoritme siklus Floyd. O (n) kompleksitas waktu, O (1) kompleksitas ruang.
sumber
slow.next != null
? Sejauh yang saya bisa lihatslow
selalu di belakang atau sama denganfast
.Solusi alternatif untuk Turtle and Rabbit, tidak begitu baik, karena saya sementara mengubah daftar:
Idenya adalah untuk berjalan daftar, dan membalikkannya saat Anda pergi. Kemudian, ketika Anda pertama kali mencapai node yang telah dikunjungi, pointer berikutnya akan menunjuk "mundur", menyebabkan iterasi untuk melanjutkan ke arah
first
lagi, di mana ia berakhir.Kode uji:
sumber
Kura-kura dan kelinci
Lihatlah algoritma Pollard rho . Ini bukan masalah yang sama, tetapi mungkin Anda akan memahami logika darinya, dan menerapkannya untuk daftar tertaut.
(Jika Anda malas, Anda bisa memeriksa deteksi siklus - lihat bagian tentang kura-kura dan kelinci.)
Ini hanya membutuhkan waktu linier, dan 2 petunjuk tambahan.
Di Jawa:
(Sebagian besar solusinya tidak memeriksa keduanya
next
dan apakah adanext.next
null. Juga, karena kura-kura selalu ada di belakang, Anda tidak perlu memeriksanya untuk null - kelinci sudah melakukannya.)sumber
Pengguna unicornaddict memiliki algoritme yang bagus di atas, tetapi sayangnya itu berisi bug untuk daftar aneh yang panjangnya> = 3. Masalahnya adalah bahwa
fast
bisa "macet" tepat sebelum akhir daftar,slow
menangkapnya, dan sebuah loop terdeteksi (salah).Berikut algoritma yang diperbaiki.
sumber
Dalam konteks ini, ada banyak bahan teks di mana-mana. Saya hanya ingin memposting representasi diagram yang benar-benar membantu saya untuk memahami konsep tersebut.
Ketika cepat dan lambat bertemu pada titik p,
Jarak yang ditempuh dengan cepat = a + b + c + b = a + 2b + c
Jarak yang ditempuh lambat = a + b
Karena puasa 2 kali lebih cepat daripada lambat. Jadi a + 2b + c = 2 (a + b) , maka kita mendapatkan a = c .
Jadi ketika pointer lambat lainnya berjalan lagi dari kepala ke q , pada saat yang sama, pointer cepat akan berjalan dari p ke q , sehingga mereka bertemu di titik q bersama.
sumber
a
lebih besar dari panjang loop maka fast akan membuat multiple loop dan formuladistance (fast) = a + b + b + c
akan berubah untuka + (b+c) * k + b
memperkenalkan parameter tambahank
yang menghitung jumlah lopps yang dibuat oleh fast.Algoritma
Kompleksitas
sumber
n
, diperbaikiequals
danhashCode
. Bukan hal yang sama. Dan itu referensinull
pada elemen terakhir. Dan pertanyaannya tidak mengatakan apa-apa tentang menyimpan node dalam fileLinkedList
.Berikut ini mungkin bukan metode terbaik - itu adalah O (n ^ 2). Namun, itu harus berfungsi untuk menyelesaikan pekerjaan (pada akhirnya).
sumber
Maafkan saya ketidaktahuan saya (saya masih cukup baru untuk Java dan pemrograman), tetapi mengapa tidak bekerja di atas
Saya kira ini tidak menyelesaikan masalah ruang konstan ... tapi setidaknya sampai di sana dalam waktu yang masuk akal, benar? Ini hanya akan mengambil ruang dari daftar tertaut ditambah ruang dari himpunan dengan n elemen (di mana n adalah jumlah elemen dalam daftar tertaut, atau jumlah elemen hingga mencapai satu lingkaran). Dan untuk waktu, analisis kasus terburuk, saya pikir, akan menyarankan O (nlog (n)). Pencarian SortedSet untuk berisi () adalah log (n) (periksa javadoc, tapi saya cukup yakin struktur mendasar TreeSet adalah TreeMap, yang pada gilirannya adalah pohon merah-hitam), dan dalam kasus terburuk (tidak ada loop, atau loop di akhir), itu harus dilakukan n look-up.
sumber
Jika kita diizinkan untuk menanamkan kelas
Node
, saya akan memecahkan masalah karena saya sudah menerapkannya di bawah ini.hasLoop()
berjalan dalam waktu O (n), dan hanya mengambil ruang daricounter
. Apakah ini tampak seperti solusi yang tepat? Atau adakah cara untuk melakukannya tanpa menanamkanNode
? (Jelas, dalam implementasi nyata akan ada lebih banyak metode, sepertiRemoveNode(Node n)
, dll.)sumber
Anda bahkan dapat melakukannya dalam waktu O (1) yang konstan (walaupun itu tidak akan sangat cepat atau efisien): Ada jumlah node yang terbatas yang dapat disimpan oleh memori komputer Anda, kata N records. Jika Anda melintasi lebih dari N catatan, maka Anda memiliki lingkaran.
sumber
sumber
Gunakan fungsi di atas untuk mendeteksi loop dalam linkedlist di java.
sumber
Mendeteksi loop dalam daftar tertaut dapat dilakukan dengan salah satu cara paling sederhana, yang menghasilkan kompleksitas O (N) menggunakan hashmap atau O (NlogN) menggunakan pendekatan berbasis sort.
Saat Anda menelusuri daftar mulai dari kepala, buat daftar alamat yang diurutkan. Ketika Anda memasukkan alamat baru, periksa apakah alamat tersebut sudah ada di daftar yang disortir, yang membutuhkan kompleksitas O (logN).
sumber
Saya tidak dapat melihat cara apa pun untuk melakukan ini dengan jumlah waktu atau ruang yang tetap, keduanya akan bertambah dengan ukuran daftar.
Saya akan menggunakan IdentityHashMap (mengingat bahwa belum ada IdentityHashSet) dan menyimpan setiap Node ke dalam peta. Sebelum sebuah node disimpan, Anda akan memanggil containKey di atasnya. Jika Node sudah ada Anda memiliki siklus.
ItentityHashMap menggunakan == bukan .equals sehingga Anda memeriksa di mana objek berada di memori daripada jika memiliki konten yang sama.
sumber
Saya mungkin sangat terlambat dan baru untuk menangani utas ini. Tetapi tetap saja..
Mengapa alamat simpul dan simpul "selanjutnya" tidak dapat disimpan dalam sebuah tabel
Jika kita bisa melakukan tabulasi dengan cara ini
Karenanya ada siklus yang terbentuk.
sumber
Ini kode runnable saya.
Apa yang saya lakukan adalah menghormati daftar yang ditautkan dengan menggunakan tiga node sementara (kompleksitas ruang
O(1)
) yang melacak tautan.Fakta menarik tentang melakukannya adalah untuk membantu mendeteksi siklus dalam daftar yang ditautkan karena ketika Anda maju, Anda tidak berharap untuk kembali ke titik awal (root node) dan salah satu node sementara harus pergi ke nol kecuali Anda memiliki siklus yang berarti menunjuk ke simpul root.
Kompleksitas waktu dari algoritma ini adalah
O(n)
dan kompleksitas ruang adalahO(1)
.Berikut adalah simpul kelas untuk daftar yang ditautkan:
Berikut adalah kode utama dengan kasus uji sederhana dari tiga node yang menunjuk terakhir ke node kedua:
Berikut ini adalah contoh kasus sederhana dari tiga node yang menunjuk ke node kedua:
sumber
Kode ini dioptimalkan dan akan menghasilkan hasil lebih cepat daripada dengan yang dipilih sebagai jawaban terbaik. Kode ini menyelamatkan dari proses yang sangat lama dalam mengejar pointer node maju dan mundur yang akan terjadi dalam kasus berikut jika kita mengikuti 'terbaik jawab metode. Lihatlah langkah-langkah berikut ini dan Anda akan menyadari apa yang saya coba katakan. Kemudian lihat masalahnya melalui metode yang diberikan di bawah ini dan ukur no. langkah-langkah yang diambil untuk menemukan jawabannya.
1-> 2-> 9-> 3 ^ -------- ^
Ini kodenya:
sumber
boolean hasLoop(Node first)
yang akan mengembalikan true jika Node yang diberikan adalah yang pertama dari daftar dengan loop, dan salah jika tidak?Ini solusi saya di java
sumber
Anda dapat menggunakan algoritma kura-kura Floyd seperti yang disarankan dalam jawaban di atas juga.
Algoritma ini dapat memeriksa apakah daftar yang ditautkan sendiri memiliki siklus tertutup. Ini dapat dicapai dengan mengulang daftar dengan dua petunjuk yang akan bergerak dalam kecepatan yang berbeda. Dengan cara ini, jika ada siklus kedua pointer akan bertemu di beberapa titik di masa depan.
Silakan periksa posting blog pada struktur data daftar tertaut, di mana saya juga memasukkan potongan kode dengan implementasi algoritma yang disebutkan di atas dalam bahasa java.
Salam,
Andreas (@xnorcode)
sumber
Inilah solusi untuk mendeteksi siklus.
sumber
// fungsi loop daftar temukan tautan
sumber
Pendekatan ini memiliki overhead ruang, tetapi implementasi yang lebih sederhana:
Loop dapat diidentifikasi dengan menyimpan node di Peta. Dan sebelum menempatkan node; periksa apakah node sudah ada. Jika node sudah ada di peta maka itu berarti Linked List memiliki loop.
sumber
sumber