Teknik Java Two Pointer

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan membincangkan pendekatan dua penunjuk untuk menyelesaikan masalah yang melibatkan tatasusunan dan senarai. Teknik ini adalah kaedah yang mudah dan efisien untuk meningkatkan prestasi algoritma kami.

2. Penerangan Teknik

Dalam banyak masalah yang melibatkan tatasusunan atau senarai, kita harus menganalisis setiap elemen larik berbanding elemennya yang lain.

Untuk menyelesaikan masalah seperti ini, kita biasanya bermula dari indeks pertama dan gelung melalui array satu atau beberapa kali bergantung pada pelaksanaan kita. Kadang-kadang, kita juga harus membuat susunan sementara bergantung pada keperluan masalah kita.

Pendekatan di atas mungkin memberi kita hasil yang betul, tetapi kemungkinan tidak akan memberi kita penyelesaian paling efisien pada masa dan masa.

Akibatnya, selalunya bagus untuk mempertimbangkan apakah masalah kita dapat diselesaikan dengan cekap dengan menggunakan pendekatan dua titik .

Dalam pendekatan dua penunjuk, penunjuk merujuk kepada indeks array. Dengan menggunakan penunjuk, kita dapat memproses dua elemen per gelung, bukan hanya satu.

Corak umum dalam pendekatan dua penunjuk melibatkan:

  • Dua titik masing-masing bermula dari awal dan akhir sehingga mereka berdua bertemu
  • Satu penunjuk bergerak pada kadar yang perlahan sementara penunjuk yang lain bergerak pada kadar yang lebih cepat

Kedua-dua corak di atas dapat membantu kita untuk mengurangkan kerumitan masa dan ruang masalah kita kerana kita mendapat hasil yang diharapkan dalam lelaran yang lebih sedikit dan tanpa menggunakan terlalu banyak ruang tambahan.

Sekarang, mari kita lihat beberapa contoh yang akan membantu kita memahami teknik ini sedikit lebih baik.

3. Jumlah Terdapat dalam Array

Masalah: Memandangkan susunan bilangan bulat yang disusun, kita perlu melihat apakah ada dua nombor di dalamnya sehingga jumlahnya sama dengan nilai tertentu.

Sebagai contoh, jika susunan input kami adalah [1, 1, 2, 3, 4, 6, 8, 9] dan nilai sasaran adalah 11 , maka kaedah kami harus kembali benar . Walau bagaimanapun, jika nilai sasaran adalah 20 , ia harus kembali palsu .

Mari kita lihat penyelesaian yang naif:

public boolean twoSumSlow(int[] input, int targetValue) { for (int i = 0; i < input.length; i++) { for (int j = 1; j < input.length; j++) { if (input[i] + input[j] == targetValue) { return true; } } } return false; }

Dalam penyelesaian di atas, kami menggunakan array input dua kali untuk mendapatkan semua kemungkinan kombinasi. Kami memeriksa jumlah gabungan dengan nilai sasaran dan kembali benar jika sesuai. Kerumitan masa penyelesaian ini adalah O (n ^ 2) .

Sekarang mari kita lihat bagaimana kita dapat menerapkan teknik dua penunjuk di sini:

public boolean twoSum(int[] input, int targetValue) { int pointerOne = 0; int pointerTwo = input.length - 1; while (pointerOne < pointerTwo) { int sum = input[pointerOne] + input[pointerTwo]; if (sum == targetValue) { return true; } else if (sum < targetValue) { pointerOne++; } else { pointerTwo--; } } return false; }

Oleh kerana susunan sudah disusun, kita boleh menggunakan dua petunjuk. Satu penunjuk bermula dari awal tatasusunan, dan penunjuk yang lain bermula dari akhir tatasusunan, dan kemudian kita menambahkan nilai pada penunjuk ini. Sekiranya jumlah nilai kurang dari nilai sasaran, kita menambah penunjuk kiri, dan jika jumlahnya lebih tinggi daripada nilai sasaran, kita akan menurunkan penunjuk kanan.

Kami terus menggerakkan penunjuk ini sehingga kami mendapat jumlah yang sepadan dengan nilai sasaran atau kami telah mencapai tengah larik, dan tidak ada kombinasi yang dijumpai. Kerumitan masa penyelesaian ini adalah O (n) dan kerumitan ruang adalah O (1) , peningkatan yang ketara berbanding pelaksanaan pertama kami.

4. Putar Array k Langkah

Masalah: Diberi array, putar larik ke kanan dengan langkah k , di mana k bukan negatif. Sebagai contoh, jika susunan input kami adalah [1, 2, 3, 4, 5, 6, 7] dan k adalah 4 , maka outputnya mestilah [4, 5, 6, 7, 1, 2, 3] .

Kita dapat menyelesaikannya dengan mempunyai dua gelung sekali lagi yang akan menjadikan kerumitan waktu O (n ^ 2) atau dengan menggunakan susunan sementara tambahan, tetapi itu akan menjadikan kerumitan ruang O (n) .

Mari selesaikan ini dengan menggunakan teknik dua penunjuk:

public void rotate(int[] input, int step) { step %= input.length; reverse(input, 0, input.length - 1); reverse(input, 0, step - 1); reverse(input, step, input.length - 1); } private void reverse(int[] input, int start, int end) { while (start < end) { int temp = input[start]; input[start] = input[end]; input[end] = temp; start++; end--; } }

Dalam kaedah di atas, kami membalikkan bahagian array input di tempat, berkali-kali, untuk mendapatkan hasil yang diperlukan. Untuk membalikkan bahagian, kami menggunakan pendekatan dua penunjuk di mana pertukaran elemen dilakukan di kedua-dua hujung bahagian array.

Khususnya, kami membalikkan semua elemen array terlebih dahulu. Kemudian, kita membalikkan elemen k pertama diikuti dengan membalikkan unsur-unsur selebihnya. Kerumitan masa penyelesaian ini adalah O (n) dan kerumitan ruang adalah O (1) .

5. Elemen Tengah dalam LinkedList

Masalah: Diberi LinkedList tunggal , cari elemen tengahnya. Contohnya, jika input LinkedList kami adalah 1-> 2-> 3-> 4-> 5, maka outputnya mestilah 3 .

Kami juga boleh menggunakan teknik dua penunjuk dalam struktur data lain yang serupa dengan tatasusunan seperti LinkedList :

public  T findMiddle(MyNode head) { MyNode slowPointer = head; MyNode fastPointer = head; while (fastPointer.next != null && fastPointer.next.next != null) { fastPointer = fastPointer.next.next; slowPointer = slowPointer.next; } return slowPointer.data; }

Dalam pendekatan ini, kami melintasi senarai terpaut menggunakan dua petunjuk. Satu penunjuk ditingkatkan oleh satu sementara yang lain meningkat oleh dua. Apabila penunjuk pantas sampai ke penghujung, penunjuk perlahan akan berada di tengah-tengah senarai yang dipautkan. Kerumitan masa penyelesaian ini adalah O (n) , dan kerumitan ruang adalah O (1) .

6. Kesimpulannya

Dalam artikel ini, kami membincangkan bagaimana kami dapat menerapkan teknik dua penunjuk dengan melihat beberapa contoh dan melihat bagaimana ia meningkatkan kecekapan algoritma kami.

Kod dalam artikel ini terdapat di Github.