Cari Elemen Tengah Senarai Terpaut di Java

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan menerangkan bagaimana mencari elemen tengah dari senarai terpaut di Java.

Kami akan memperkenalkan masalah utama di bahagian seterusnya, dan kami akan menunjukkan pendekatan yang berbeza untuk menyelesaikannya.

2. Menjaga Ukuran

Masalah ini dapat diselesaikan dengan mudah hanya dengan mengawasi ukuran ketika kita menambahkan elemen baru ke dalam senarai . Sekiranya kita mengetahui ukurannya, kita juga tahu di mana elemen tengahnya, jadi penyelesaiannya adalah remeh.

Mari lihat contoh menggunakan pelaksanaan Java dari LinkedList :

public static Optional findMiddleElementLinkedList( LinkedList linkedList) { if (linkedList == null || linkedList.isEmpty()) { return Optional.empty(); } return Optional.of(linkedList.get( (linkedList.size() - 1) / 2)); }

Sekiranya kita memeriksa kod dalaman kelas LinkedList , kita dapat melihat bahawa dalam contoh ini kita hanya melintasi senarai sehingga kita mencapai elemen tengah:

Node node(int index) { if (index > 1)) { Node x = first; for (int i = 0; i < index; i++) { x = x.next; } return x; } else { Node x = last; for (int i = size - 1; i > index; i--) { x = x.prev; } return x; } }

3. Mencari Tengah Tanpa Mengetahui Ukuran

Sangat biasa kita menghadapi masalah di mana kita hanya mempunyai node utama senarai terpaut, dan kita perlu mencari elemen tengah. Dalam kes ini, kami tidak mengetahui ukuran senarai, yang menjadikan masalah ini lebih sukar untuk diselesaikan.

Kami akan menunjukkan di bahagian seterusnya beberapa pendekatan untuk menyelesaikan masalah ini, tetapi pertama, kita perlu membuat kelas untuk mewakili simpul senarai.

Mari buat kelas Node , yang menyimpan nilai String :

public static class Node { private Node next; private String data; // constructors/getters/setters public boolean hasNext() { return next != null; } public void setNext(Node next) { this.next = next; } public String toString() { return this.data; } }

Selain itu, kami akan menggunakan kaedah pembantu ini dalam kes ujian kami untuk membuat senarai yang dipautkan dengan hanya menggunakan nod kami:

private static Node createNodesList(int n) { Node head = new Node("1"); Node current = head; for (int i = 2; i <= n; i++) { Node newNode = new Node(String.valueOf(i)); current.setNext(newNode); current = newNode; } return head; }

3.1. Mencari Saiz Pertama

Pendekatan paling mudah untuk mengatasi masalah ini adalah mencari ukuran senarai terlebih dahulu, dan setelah itu ikuti pendekatan yang sama seperti yang kita gunakan sebelumnya - untuk melakukan lelang hingga elemen tengah.

Mari lihat penyelesaian ini dalam tindakan:

public static Optional findMiddleElementFromHead(Node head) { if (head == null) { return Optional.empty(); } // calculate the size of the list Node current = head; int size = 1; while (current.hasNext()) { current = current.next(); size++; } // iterate till the middle element current = head; for (int i = 0; i < (size - 1) / 2; i++) { current = current.next(); } return Optional.of(current.data()); }

Seperti yang kita lihat, kod ini berulang melalui senarai dua kali. Oleh itu, penyelesaian ini mempunyai prestasi yang buruk dan tidak digalakkan .

3.2. Mencari Elemen Tengah dalam Satu Pas Secara Berterusan

Kami sekarang akan memperbaiki penyelesaian sebelumnya dengan mencari elemen tengah dengan hanya satu lelaran di atas senarai.

Untuk melakukannya secara berulang, kita memerlukan dua petunjuk untuk meneruskan senarai pada masa yang sama. Satu penunjuk akan memajukan 2 node dalam setiap lelaran, dan satu penunjuk akan memajukan satu node setiap lelaran .

Apabila penunjuk yang lebih cepat sampai di hujung senarai, penunjuk yang lebih perlahan akan berada di tengah:

public static Optional findMiddleElementFromHead1PassIteratively(Node head) { if (head == null) { return Optional.empty(); } Node slowPointer = head; Node fastPointer = head; while (fastPointer.hasNext() && fastPointer.next().hasNext()) { fastPointer = fastPointer.next().next(); slowPointer = slowPointer.next(); } return Optional.ofNullable(slowPointer.data()); }

Kita boleh menguji penyelesaian ini dengan ujian unit sederhana menggunakan senarai dengan bilangan unsur ganjil dan genap:

@Test public void whenFindingMiddleFromHead1PassIteratively_thenMiddleFound() { assertEquals("3", MiddleElementLookup .findMiddleElementFromHead1PassIteratively( createNodesList(5)).get()); assertEquals("2", MiddleElementLookup .findMiddleElementFromHead1PassIteratively( reateNodesList(4)).get()); }

3.3. Mencari Elemen Tengah dalam Satu Pas Secara Berurutan

Kaedah lain untuk menyelesaikan masalah ini dalam satu hantaran adalah dengan menggunakan rekursi. Kita dapat melakukan pengulangan hingga akhir senarai untuk mengetahui ukurannya dan, dalam panggilan balik, kita hanya menghitung hingga separuh ukuran.

Untuk melakukan ini di Java, kita akan membuat kelas tambahan untuk menyimpan rujukan ukuran senarai dan elemen tengah semasa pelaksanaan semua panggilan berulang:

private static class MiddleAuxRecursion { Node middle; int length = 0; }

Sekarang, mari kita laksanakan kaedah rekursif:

private static void findMiddleRecursively( Node node, MiddleAuxRecursion middleAux) { if (node == null) { // reached the end middleAux.length = middleAux.length / 2; return; } middleAux.length++; findMiddleRecursively(node.next(), middleAux); if (middleAux.length == 0) { // found the middle middleAux.middle = node; } middleAux.length--; }

Dan akhirnya, mari buat kaedah yang memanggil kaedah rekursif:

public static Optional findMiddleElementFromHead1PassRecursively(Node head) { if (head == null) { return Optional.empty(); } MiddleAuxRecursion middleAux = new MiddleAuxRecursion(); findMiddleRecursively(head, middleAux); return Optional.of(middleAux.middle.data()); }

Sekali lagi, kita dapat mengujinya dengan cara yang sama seperti sebelumnya:

@Test public void whenFindingMiddleFromHead1PassRecursively_thenMiddleFound() { assertEquals("3", MiddleElementLookup .findMiddleElementFromHead1PassRecursively( createNodesList(5)).get()); assertEquals("2", MiddleElementLookup .findMiddleElementFromHead1PassRecursively( createNodesList(4)).get()); }

4. Kesimpulan

Dalam artikel ini, kami telah memperkenalkan masalah mencari elemen tengah dari senarai terpaut di Java, dan kami telah menunjukkan cara yang berbeza untuk menyelesaikannya.

Kami bermula dari pendekatan termudah di mana kami terus mengawasi ukurannya, dan setelah itu, kami meneruskan penyelesaian untuk mencari elemen tengah dari simpul utama senarai.

Seperti biasa, kod sumber penuh contoh terdapat di GitHub.