Inilah yang saya dapatkan, yang tidak memerlukan sedikit tanda tambahan:
for i := 0 to n - 1
while A[A[i]] != A[i]
swap(A[i], A[A[i]])
end while
end for
for i := 0 to n - 1
if A[i] != i then
print A[i]
end if
end for
Perulangan pertama mengijinkan array sehingga jika elemen x
hadir setidaknya satu kali, maka salah satu entri tersebut akan berada pada posisi A[x]
.
Perhatikan bahwa itu mungkin tidak terlihat O (n) pada blush pertama, tetapi ini - meskipun memiliki loop bersarang, itu masih berjalan O(N)
tepat waktu. Swap hanya terjadi jika ada i
sehingga A[i] != i
, dan masing-masing Swap set setidaknya satu elemen sehingga A[i] == i
, di mana itu tidak benar sebelumnya. Ini berarti bahwa jumlah total swap (dan dengan demikian jumlah total eksekusi dari while
badan perulangan) paling banyak N-1
.
Loop kedua mencetak nilai x
yang A[x]
tidak sama x
- karena loop pertama menjamin bahwa jika x
ada setidaknya satu kali dalam array, salah satu instance akan berada di A[x]
, ini berarti mencetak nilai-nilai x
yang tidak ada di larik.
(Tautan Ideone sehingga Anda dapat bermain dengannya)
a[a[i]]
, dan batasan ruang O (1) mengisyaratkanswap()
operasi menjadi kuncinya.5
tidak dalam kisaran0..N-1
(N
dalam hal ini sedang5
).print
pernyataan untukprint i
mengubahnya menjadi solusi untuk stackoverflow.com/questions/5249985/… dan (dengan asumsi "tas" adalah larik yang dapat dimodifikasi) Qk dari stackoverflow.com/questions/3492302/… .Jawaban brilian caf mencetak setiap angka yang muncul k kali dalam larik k-1 kali. Itu perilaku yang berguna, tetapi pertanyaannya bisa dibilang memanggil setiap duplikat untuk dicetak sekali saja, dan dia menyinggung kemungkinan melakukan ini tanpa meniup batas ruang waktu / konstan linier. Ini dapat dilakukan dengan mengganti loop keduanya dengan pseudocode berikut:
Ini mengeksploitasi properti yang setelah loop pertama dijalankan, jika ada nilai yang
m
muncul lebih dari satu kali, maka salah satu kemunculan tersebut dijamin berada pada posisi yang benar, yaituA[m]
. Jika kita berhati-hati kita dapat menggunakan lokasi "rumah" itu untuk menyimpan informasi tentang apakah ada duplikat yang telah dicetak atau belum.Dalam versi caf, saat kita menelusuri array,
A[i] != i
tersirat bahwa ituA[i]
adalah duplikat. Dalam versi saya, saya mengandalkan invarian yang sedikit berbeda: yangA[i] != i && A[A[i]] == A[i]
menyiratkan bahwa ituA[i]
adalah duplikat yang belum pernah kita lihat sebelumnya . (Jika Anda membuang bagian "yang belum pernah kami lihat sebelumnya", sisanya dapat dilihat tersirat oleh kebenaran dari ketidak-beraturan kafe, dan jaminan bahwa semua duplikat memiliki beberapa salinan di lokasi rumah.) Properti ini berlaku di permulaan (setelah loop pertama kafe selesai) dan saya tunjukkan di bawah bahwa itu dipertahankan setelah setiap langkah.Saat kita menelusuri larik, keberhasilan pada
A[i] != i
bagian pengujian menyiratkan bahwaA[i]
bisa jadi duplikat yang belum pernah terlihat sebelumnya. Jika kita belum pernah melihatnya sebelumnya, maka kita berharapA[i]
lokasi rumah mengarah ke dirinya sendiri - itulah yang diuji pada paruh keduaif
kondisi tersebut. Jika demikian, kami mencetaknya dan mengubah lokasi rumah agar mengarah kembali ke duplikat yang pertama ditemukan ini, membuat "siklus" 2 langkah.Untuk melihat bahwa operasi ini tidak mengubah invarian kami, anggaplah
m = A[i]
untuk posisi tertentui
memuaskanA[i] != i && A[A[i]] == A[i]
. Jelas bahwa perubahan yang kita buat (A[A[i]] = i
) akan berfungsi untuk mencegah kejadian non-rumah lainnyam
menjadi keluaran sebagai duplikat dengan menyebabkan paruh kedua dariif
kondisi mereka gagal, tetapi apakah itu akan berfungsi ketikai
tiba di lokasi rumahm
,? Ya itu akan, karena sekarang, meskipun pada yang baru inii
kami menemukan bahwa paruh pertama dariif
kondisi,A[i] != i
benar, paruh ke-2 menguji apakah lokasi yang ditunjuknya adalah lokasi rumah dan ternyata bukan. Dalam situasi ini kami tidak lagi mengetahui apakah nilai duplikatm
atauA[m]
merupakan nilai duplikat, tetapi kami tahu bahwa bagaimanapun juga,sudah dilaporkan , karena 2 siklus ini dijamin tidak akan muncul di hasil loop pertama kafe. (Perhatikan bahwa jikam != A[m]
maka tepat satu darim
danA[m]
terjadi lebih dari sekali, dan yang lainnya tidak terjadi sama sekali.)sumber
Ini pseudocode-nya
Kode contoh di C ++
sumber
-
dengan~
untuk masalah nol.O(n)
ruang tersembunyi -n
bit tanda. Jika array didefinisikan sedemikian rupa sehingga setiap elemen hanya dapat menampung nilai antara0
dann-1
, maka itu jelas tidak berfungsi.Untuk N yang relatif kecil kita dapat menggunakan operasi div / mod
Bukan C / C ++ tapi bagaimanapun juga
http://ideone.com/GRZPI
sumber
Tidak terlalu cantik tapi setidaknya mudah untuk melihat properti O (N) dan O (1). Pada dasarnya kami memindai larik dan, untuk setiap nomor kami melihat apakah posisinya telah ditandai sudah-terlihat-sekali (N) atau sudah-terlihat-beberapa kali (N + 1). Jika itu ditandai sudah-terlihat-sekali, kami mencetaknya dan menandainya sudah-terlihat-beberapa kali. Jika tidak ditandai, kami menandainya sudah-terlihat-sekali dan kami memindahkan nilai asli dari indeks terkait ke posisi saat ini (penandaan adalah operasi yang merusak).
atau, lebih baik lagi (lebih cepat, meskipun ada putaran ganda):
sumber
if (value > i) a[i--] = a[value];
berhasil: jikavalue <= i
kita telah memproses nilai dia[value]
dan dapat menimpanya dengan aman. Juga saya tidak akan mengatakan sifat O (N) jelas! Mengeja: Putaran utama berjalanN
berkali-kali, ditambah berapa kalia[i--] = a[value];
garis berjalan. Baris itu hanya dapat berjalan jikaa[value] < N
, dan setiap kali dijalankan, segera setelah itu nilai array yang belumN
disetel keN
, sehingga dapat berjalan palingN
banyak, dengan total paling banyak2N
iterasi perulangan.Salah satu solusi di C adalah:
Ini adalah kompleksitas ruang O (n) waktu dan O (1).
sumber
Mari kita asumsikan bahwa kita menyajikan larik ini sebagai struktur data graf searah - setiap bilangan adalah simpul dan indeksnya dalam larik menunjuk ke simpul lain yang membentuk tepi graf.
Untuk lebih mudahnya kami memiliki indeks 0 hingga n-1 dan rentang angka dari 0..n-1. misalnya
0 (3) -> 3 (3) adalah sebuah siklus.
Jawaban: Lintasi saja larik dengan mengandalkan indeks. jika a [x] = a [y] maka itu adalah siklus dan duplikat. Lompat ke indeks berikutnya dan lanjutkan lagi dan seterusnya hingga akhir dari sebuah array. Kompleksitas: O (n) waktu dan O (1) ruang.
sumber
Kode python kecil untuk mendemonstrasikan metode caf di atas:
sumber
i
nilai - perhatikanwhile
dalam jawaban saya.Algoritma dapat dilihat pada fungsi C berikut. Mengambil larik asli, meskipun tidak diperlukan, dapat dilakukan dengan mengambil setiap entri modulo n .
Ideone Link untuk pengujian.
sumber
sumber
Saya telah membuat satu aplikasi contoh taman bermain dengan cepat untuk menemukan duplikat dalam 0 (n) kompleksitas waktu dan ruang ekstra yang konstan. Silakan periksa url Finding Duplicates
Solusi IMP Di atas bekerja ketika sebuah array berisi elemen dari 0 hingga n-1, dengan salah satu dari angka-angka ini muncul beberapa kali.
sumber
sumber
Jika array tidak terlalu besar, solusi ini lebih sederhana, Ini membuat array lain dengan ukuran yang sama untuk ticking.
1 Buat bitmap / larik dengan ukuran yang sama dengan larik masukan Anda
2 pindai larik masukan Anda dan tingkatkan jumlahnya dalam larik di atas
3 Sekarang pindai array check_list dan cetak duplikatnya sekali atau sebanyak yang telah diduplikasi
Tentu saja dibutuhkan dua kali ruang yang dikonsumsi oleh solusi yang diberikan di atas, tetapi efisiensi waktu adalah O (2n) yang pada dasarnya O (n).
sumber
O(1)
ruang.