Struktur Data Trie di Jawa

1. Gambaran keseluruhan

Struktur data merupakan aset penting dalam pengaturcaraan komputer, dan mengetahui kapan dan mengapa menggunakannya sangat penting.

Artikel ini adalah pengenalan ringkas untuk trie (diucapkan "cuba") struktur data, pelaksanaannya dan analisis kerumitan.

2. Trie

Trie adalah struktur data diskrit yang tidak begitu terkenal atau disebut secara meluas dalam kursus algoritma biasa, tetapi tetap penting.

Trie (juga dikenali sebagai pokok digital) dan kadang-kadang bahkan pohon radix atau pohon awalan (kerana mereka boleh dicari oleh awalan), adalah struktur pokok yang disusun, yang memanfaatkan kunci yang disimpannya - biasanya rentetan.

Kedudukan simpul di pohon menentukan kunci yang berkaitan dengan simpul tersebut, yang membuat percubaan berbeza berbanding dengan pokok carian binari, di mana nod menyimpan kunci yang hanya sesuai dengan simpul tersebut.

Semua keturunan nod mempunyai awalan umum String yang berkaitan dengan nod itu, sedangkan akar dikaitkan dengan String kosong .

Di sini kita mempunyai pratonton TrieNode yang akan kita gunakan dalam pelaksanaan Trie:

public class TrieNode { private HashMap children; private String content; private boolean isWord; // ... }

Mungkin ada kes apabila trie adalah pokok carian binari, tetapi secara umum, ini berbeza. Kedua-dua pokok carian dan percubaan binari adalah pokok, tetapi setiap simpul dalam pokok carian binari selalu mempunyai dua anak, sedangkan simpul percubaan, sebaliknya, boleh mempunyai lebih banyak.

Dalam trie, setiap nod (kecuali node root) menyimpan satu watak atau digit. Dengan melintasi trie ke bawah dari simpul akar ke simpul n tertentu , awalan watak atau digit yang biasa dapat dibentuk yang dibagi oleh cabang-cabang trie yang lain juga.

Dengan melintasi trie dari simpul daun ke simpul akar, String atau urutan digit dapat dibentuk.

Berikut adalah kelas Trie , yang mewakili pelaksanaan struktur data trie:

public class Trie { private TrieNode root; //... }

3. Operasi Biasa

Sekarang, mari kita lihat bagaimana melaksanakan operasi asas.

3.1. Memasukkan Elemen

Operasi pertama yang akan kami jelaskan adalah penyisipan nod baru.

Sebelum kita memulakan pelaksanaannya, penting untuk memahami algoritma:

  1. Tetapkan nod semasa sebagai nod akar
  2. Tetapkan huruf semasa sebagai huruf pertama perkataan
  3. Sekiranya node semasa sudah ada rujukan yang ada pada huruf saat ini (melalui salah satu elemen dalam bidang "anak-anak"), kemudian tetapkan simpul semasa ke simpul yang dirujuk. Jika tidak, buat simpul baru, tetapkan huruf sama dengan huruf semasa, dan juga mulakan simpul semasa ke simpul baru ini
  4. Ulangi langkah 3 sehingga kunci dilintasi

Kerumitan operasi ini adalah O (n) , di mana n mewakili ukuran kunci.

Berikut adalah pelaksanaan algoritma ini:

public void insert(String word) { TrieNode current = root; for (char l: word.toCharArray()) { current = current.getChildren().computeIfAbsent(l, c -> new TrieNode()); } current.setEndOfWord(true); }

Sekarang mari kita lihat bagaimana kita boleh menggunakan kaedah ini untuk memasukkan elemen baru dalam trie:

private Trie createExampleTrie() { Trie trie = new Trie(); trie.insert("Programming"); trie.insert("is"); trie.insert("a"); trie.insert("way"); trie.insert("of"); trie.insert("life"); return trie; }

Kita boleh menguji bahawa trie telah diisi dengan nod baru dari ujian berikut:

@Test public void givenATrie_WhenAddingElements_ThenTrieNotEmpty() { Trie trie = createTrie(); assertFalse(trie.isEmpty()); }

3.2. Mencari Elemen

Mari sekarang tambahkan kaedah untuk memeriksa sama ada elemen tertentu sudah ada dalam trie:

  1. Dapatkan anak-anak dari akar umbi
  2. Ulangi setiap watak Rentetan
  3. Periksa sama ada watak itu sudah menjadi sebahagian daripada sub-trie. Sekiranya tidak ada di mana-mana sahaja, hentikan carian dan kembali palsu
  4. Ulangi langkah kedua dan ketiga sehingga tidak ada watak yang tersisa dalam Rentetan. Sekiranya akhir rentetan tercapai, kembali benar

Kerumitan algoritma ini adalah O (n) , di mana n mewakili panjang kunci.

Pelaksanaan Java dapat dilihat seperti:

public boolean find(String word) { TrieNode current = root; for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); TrieNode node = current.getChildren().get(ch); if (node == null) { return false; } current = node; } return current.isEndOfWord(); }

Dan dalam tindakan:

@Test public void givenATrie_WhenAddingElements_ThenTrieContainsThoseElements() { Trie trie = createExampleTrie(); assertFalse(trie.containsNode("3")); assertFalse(trie.containsNode("vida")); assertTrue(trie.containsNode("life")); }

3.3. Memadamkan Elemen

Selain memasukkan dan mencari elemen, jelas bahawa kita juga perlu dapat menghapus elemen.

Untuk proses penghapusan, kita perlu mengikuti langkah-langkahnya:

  1. Periksa sama ada elemen ini sudah menjadi sebahagian daripada trie
  2. Sekiranya unsur itu dijumpai, maka keluarkan dari trie

Kerumitan algoritma ini adalah O (n) , di mana n mewakili panjang kunci.

Mari lihat pelaksanaannya dengan cepat:

public void delete(String word) { delete(root, word, 0); } private boolean delete(TrieNode current, String word, int index) { if (index == word.length()) { if (!current.isEndOfWord()) { return false; } current.setEndOfWord(false); return current.getChildren().isEmpty(); } char ch = word.charAt(index); TrieNode node = current.getChildren().get(ch); if (node == null) { return false; } boolean shouldDeleteCurrentNode = delete(node, word, index + 1) && !node.isEndOfWord(); if (shouldDeleteCurrentNode) { current.getChildren().remove(ch); return current.getChildren().isEmpty(); } return false; }

Dan dalam tindakan:

@Test void whenDeletingElements_ThenTreeDoesNotContainThoseElements() { Trie trie = createTrie(); assertTrue(trie.containsNode("Programming")); trie.delete("Programming"); assertFalse(trie.containsNode("Programming")); }

4. Kesimpulan

Dalam artikel ini, kami telah melihat pengenalan ringkas mengenai struktur data trie dan operasi yang paling biasa serta pelaksanaannya.

Kod sumber lengkap untuk contoh yang ditunjukkan dalam artikel ini boleh didapati di GitHub.