Java Két mutató technika

1. Áttekintés

Ebben az oktatóanyagban a tömbökkel és listákkal kapcsolatos problémák megoldásának kétpontos megközelítését tárgyaljuk. Ez a technika egyszerű és hatékony módszer algoritmusunk teljesítményének javítására.

2. A technika leírása

Számos tömböt vagy listát érintő problémában elemeznünk kell a tömb minden egyes elemét a többi eleméhez képest.

Az ilyen problémák megoldásához általában az első indexből indulunk ki, és a megvalósítás függvényében egy vagy több alkalommal végigvezetünk a tömbön. Néha ideiglenes tömböt is létre kell hoznunk a problémánk követelményeitől függően.

A fenti megközelítés megadhatja számunkra a helyes eredményt, de valószínűleg nem a leginkább hely- és időhatékony megoldást nyújtja.

Ennek eredményeként gyakran jó megfontolni, hogy problémánk hatékonyan megoldható-e a kétmutatós megközelítés.

A kétmutatós megközelítésben a mutatók egy tömb indexeire utalnak. Mutatók használatával ciklusonként két elemet dolgozhatunk fel, egy helyett.

A kétmutatós megközelítés közös mintái:

  • Két mutató az elejétől a végéig, mindkettő találkozásáig
  • Az egyik mutató lassú, míg a másik mutató gyorsabban halad

Mindkét fenti minta segíthet a idő és tér összetettsége problémáinkból, mivel kevesebb ismétléssel és túl sok további hely használata nélkül érjük el a várt eredményt.

Most nézzünk meg néhány példát, amelyek segítenek megérteni ezt a technikát egy kicsit jobban.

3. Az összeg egy tömbben létezik

Probléma: Az egész számok rendezett tömbje alapján meg kell látnunk, van-e benne két szám úgy, hogy összegük megegyezzen egy adott értékkel.

Például, ha a bemeneti tömbünk [1, 1, 2, 3, 4, 6, 8, 9] és a célérték az 11, akkor a módszerünknek vissza kell térnie igaz. Ha azonban a célérték az 20, vissza kell térnie hamis.

Lássuk először egy naiv megoldást:

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; }

A fenti megoldásban kétszer hurkoltuk a bemeneti tömböt, hogy minden lehetséges kombinációt megkapjunk. Ellenőriztük a kombináció összegét a célértékkel és visszatértünk igaz ha egyezik. A megoldás időbeli összetettsége O (n ^ 2) .

Most nézzük meg, hogyan alkalmazhatjuk itt a kétpontos technikát:

nyilvános logikai kettőSum (int [] input, int targetValue) {int pointerOne = 0; int pointerTwo = bemenet.hossz - 1; while (pointerOne <pointerTwo) {int összeg = bemenet [pointerOne] + bemenet [pointerTwo]; if (összeg == célérték) {return true; } else if (összeg <célérték) {pointerOne ++; } else {pointerTwo--; }} return false; }

Mivel a tömb már rendezve van, két mutatót használhatunk. Az egyik mutató a tömb elejéről indul, a másik mutató a tömb végéről indul, majd ezekhez a mutatókhoz adjuk hozzá az értékeket. Ha az értékek összege kisebb, mint a célérték, akkor növeljük a bal mutatót, és ha az összeg magasabb, mint a célérték, akkor a jobb mutatót csökkentjük.

Addig mozgatjuk ezeket a mutatókat, amíg meg nem kapjuk a célértéknek megfelelő összeget, vagy el nem érjük a tömb közepét, és nem találtunk kombinációt. A megoldás időbeli összetettsége Tovább) az űr komplexitása pedig az O (1), jelentős előrelépés az első megvalósításhoz képest.

4. Forgassa el a tömböt k Lépések

Probléma: Adott tömb, forgassa jobbra a tömböt k lépések, hol k nem negatív. Például, ha a bemeneti tömbünk [1, 2, 3, 4, 5, 6, 7] és k van 4, akkor a kimenet legyen [4, 5, 6, 7, 1, 2, 3].

Megoldhatjuk ezt úgy, hogy ismét két hurok van, ami megnehezíti az időt O (n ^ 2) vagy egy extra, ideiglenes tömb használatával, de ez megnehezíti a helyet Tovább).

Megoldjuk ezt inkább a két mutató technikával:

public void rotate (int [] input, int step) {lépés% = bemenet.hossz; hátramenet (bemenet, 0, bemenet.hossz - 1); hátramenet (bemenet, 0, 1. lépés); hátramenet (bemenet, lépés, bemenet.hossz - 1); } private void reverse (int [] input, int start, int end) {while (start <end) {int temp = input [start]; input [kezdet] = input [vég]; bemenet [vég] = hőmérséklet; start ++; vége--; }}

A fenti módszerekben a bemeneti tömb szakaszait többször megfordítjuk a helyén, többször is, hogy megkapjuk a kívánt eredményt. A szakaszok megfordításához a kétpontos megközelítést alkalmaztuk, ahol az elemek cseréjét a tömb szakasz mindkét végén végeztük.

Pontosabban először a tömb összes elemét megfordítjuk. Ezután megfordítjuk az elsőt k elemek, majd a többi elem megfordítása. A megoldás időbeli összetettsége Tovább) és a tér bonyolultsága az O (1).

5. Középső elem a LinkedList

Probléma: Egyenként adva LinkedList, keresse meg középső elemét. Például, ha a bemenetünk LinkedList van 1->2->3->4->5, akkor a kimenet legyen 3.

A kétpontos technikát más adatstruktúrákban is alkalmazhatjuk, hasonlóan az a tömbökhöz LinkedList:

public T findMiddle (MyNode fej) {MyNode slowPointer = fej; MyNode fastPointer = fej; míg (fastPointer.next! = null && fastPointer.next.next! = null) {fastPointer = fastPointer.next.next; slowPointer = slowPointer.next; } return slowPointer.data; }

Ebben a megközelítésben két mutató segítségével haladunk át a linkelt listán. Az egyik mutatót egy, a másikat kettővel növeljük. Amikor a gyors mutató a végére ér, a lassú mutató a csatolt lista közepén lesz. A megoldás időbeli összetettsége Tovább) , és az űr bonyolultsága az O (1).

6. Következtetés

Ebben a cikkben megvitattuk, hogyan alkalmazhatnánk a kétmutatós technikát néhány példával, és megvizsgáltuk, hogyan javítja az algoritmusunk hatékonyságát.

A cikk kódja a Github oldalon érhető el.


$config[zx-auto] not found$config[zx-overlay] not found