Two pointers method
This commit is contained in:
parent
1d56f95355
commit
7f443cd64e
243
luku08.tex
243
luku08.tex
|
@ -1,41 +1,42 @@
|
||||||
\chapter{Amortized analysis}
|
\chapter{Amortized analysis}
|
||||||
|
|
||||||
\index{tasoitettu analyysi@tasoitettu analyysi}
|
\index{amortized analysis}
|
||||||
|
|
||||||
Monen algoritmin aikavaativuuden pystyy laskemaan
|
Often the time complexity of an algorithm
|
||||||
suoraan katsomalla algoritmin rakennetta:
|
is easy to analyze by looking at the structure
|
||||||
mitä silmukoita algoritmissa on ja miten monta
|
of the algorithm:
|
||||||
kertaa niitä suoritetaan.
|
what loops there are and how many times
|
||||||
Joskus kuitenkaan näin suoraviivainen analyysi ei
|
they are performed.
|
||||||
riitä antamaan todellista kuvaa algoritmin tehokkuudesta.
|
However, sometimes a straightforward analysis
|
||||||
|
doesn't give a true picture of the efficiency of the algorithm.
|
||||||
|
|
||||||
\key{Tasoitettu analyysi} soveltuu sellaisten
|
\key{Amortized analysis} can be used for analyzing
|
||||||
algoritmien analyysiin, joiden osana on jokin operaatio,
|
an algorithm that contains an operation whose
|
||||||
jonka ajankäyttö vaihtelee.
|
time complexity varies.
|
||||||
Ideana on tarkastella yksittäisen operaation
|
The idea is to consider all such operations during the
|
||||||
sijasta kaikkia operaatioita algoritmin
|
execution of the algorithm instead of a single operation,
|
||||||
aikana ja laskea niiden ajankäytölle yhteinen raja.
|
and estimate the total time complexity of the operations.
|
||||||
|
|
||||||
\section{Kahden osoittimen tekniikka}
|
\section{Two pointers method}
|
||||||
|
|
||||||
\index{kahden osoittimen tekniikka}
|
\index{two pointers method}
|
||||||
|
|
||||||
\key{Kahden osoittimen tekniikka} on taulukon käsittelyssä
|
In the \key{two pointers method},
|
||||||
käytettävä menetelmä, jossa taulukkoa käydään läpi
|
two pointers iterate through the elements in an array.
|
||||||
kahden osoittimen avulla.
|
Both pointers can move during the algorithm,
|
||||||
Molemmat osoittimet liikkuvat algoritmin aikana,
|
but the restriction is that each pointer can move
|
||||||
mutta rajoituksena on, että ne voivat liikkua vain
|
to only one direction.
|
||||||
yhteen suuntaan, mikä takaa, että algoritmi toimii tehokkaasti.
|
This ensures that the algorithm works efficiently.
|
||||||
|
|
||||||
Tutustumme seuraavaksi kahden osoittimen tekniikkaan
|
We will next discuss two problems that can be solved
|
||||||
kahden esimerkkitehtävän kautta.
|
using the two pointers method.
|
||||||
|
|
||||||
\subsubsection{Alitaulukon summa}
|
\subsubsection{Subarray sum}
|
||||||
|
|
||||||
Annettuna on taulukko, jossa on $n$ positiivista kokonaislukua.
|
Given an array that contains $n$ positive integers,
|
||||||
Tehtävänä on selvittää, onko taulukossa alitaulukkoa,
|
our task is to find out if there is a subarray
|
||||||
jossa lukujen summa on $x$.
|
where the sum of the elements is $x$.
|
||||||
Esimerkiksi taulukossa
|
For example, the array
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -60,7 +61,7 @@ Esimerkiksi taulukossa
|
||||||
\node at (7.5,1.4) {$8$};
|
\node at (7.5,1.4) {$8$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
on alitaulukko, jossa lukujen summa on 8:
|
contains a subarray with sum 8:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\fill[color=lightgray] (2,0) rectangle (5,1);
|
\fill[color=lightgray] (2,0) rectangle (5,1);
|
||||||
|
@ -87,17 +88,18 @@ on alitaulukko, jossa lukujen summa on 8:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Osoittautuu, että tämän tehtävän voi ratkaista
|
It turns out that the problem can be solved in
|
||||||
ajassa $O(n)$ kahden osoittimen tekniikalla.
|
$O(n)$ time using the two pointers method.
|
||||||
Ideana on käydä taulukkoa läpi kahden osoittimen
|
The idea is to iterate through the array
|
||||||
avulla, jotka rajaavat välin taulukosta.
|
using two pointers that define a range in the array.
|
||||||
Joka vuorolla vasen osoitin liikkuu
|
On each turn, the left pointer moves one step
|
||||||
yhden askeleen eteenpäin ja oikea osoitin
|
forward, and the right pointer moves forward
|
||||||
liikkuu niin kauan eteenpäin kuin summa on enintään $x$.
|
as long as the sum is at most $x$.
|
||||||
Jos välin summaksi tulee tarkalleen $x$, ratkaisu on löytynyt.
|
If the sum of the range becomes exactly $x$,
|
||||||
|
we have found a solution.
|
||||||
|
|
||||||
Tarkastellaan esimerkkinä algoritmin toimintaa
|
As an example, we consider the following array
|
||||||
seuraavassa taulukossa, kun tavoitteena on muodostaa summa $x=8$:
|
with target sum $x=8$:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -123,10 +125,10 @@ seuraavassa taulukossa, kun tavoitteena on muodostaa summa $x=8$:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Aluksi osoittimet rajaavat taulukosta välin,
|
First, the pointers define a range with sum $1+3+2=6$.
|
||||||
jonka summa on $1+3+2=6$.
|
The range can't be larger
|
||||||
Väli ei voi olla tätä suurempi,
|
because the next number 5 would make the sum
|
||||||
koska seuraava luku 5 veisi summan yli $x$:n.
|
larger than $x$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -157,9 +159,9 @@ koska seuraava luku 5 veisi summan yli $x$:n.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Seuraavaksi vasen osoitin siirtyy askeleen eteenpäin.
|
After this, the left pointer moves one step forward.
|
||||||
Oikea osoitin säilyy paikallaan, koska muuten
|
The right pointer doesn't move because otherwise
|
||||||
summa kasvaisi liian suureksi.
|
the sum would become too large.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -190,10 +192,11 @@ summa kasvaisi liian suureksi.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Vasen osoitin siirtyy taas askeleen eteenpäin
|
Again, the left pointer moves one step forward,
|
||||||
ja tällä kertaa oikea osoitin siirtyy kolme askelta
|
and this time the right pointer moves three
|
||||||
eteenpäin. Muodostuu summa $2+5+1=8$ eli taulukosta
|
steps forward.
|
||||||
on löytynyt väli, jonka lukujen summa on $x$.
|
The sum is $2+5+1=8$, so we have found a subarray
|
||||||
|
where the sum of the elements is $x$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -224,67 +227,42 @@ on löytynyt väli, jonka lukujen summa on $x$.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
% Algoritmin toteutus näyttää seuraavalta:
|
The time complexity of the algorithm depends on
|
||||||
%
|
the number of steps the right pointer moves.
|
||||||
% \begin{lstlisting}
|
There is no upper bound how many steps the
|
||||||
% int s = 0, b = 0;
|
pointer can move on a single turn.
|
||||||
% for (int a = 1; a <= n; a++) {
|
However, the pointer moves \emph{a total of}
|
||||||
% while (b<n && s+t[b+1] <= x) {
|
$O(n)$ steps during the algorithm
|
||||||
% b++;
|
because it only moves forward.
|
||||||
% s += t[b];
|
|
||||||
% }
|
|
||||||
% if (s == x) {
|
|
||||||
% // ratkaisu löytyi
|
|
||||||
% }
|
|
||||||
% s -= t[a];
|
|
||||||
% }
|
|
||||||
% \end{lstlisting}
|
|
||||||
%
|
|
||||||
% Muuttujat $a$ ja $b$ sisältävät vasemman ja oikean
|
|
||||||
% osoittimen kohdan.
|
|
||||||
% Muuttuja $s$ taas laskee lukujen summan välillä.
|
|
||||||
% Joka askeleella $a$ liikkuu askeleen eteenpäin
|
|
||||||
% ja $b$ liikkuu niin kauan kuin summa on enintään $x$.
|
|
||||||
|
|
||||||
Algoritmin aikavaativuus riippuu siitä,
|
Since both the left and the right pointer
|
||||||
kauanko oikean osoittimen liikkuminen vie aikaa.
|
move $O(n)$ steps during the algorithm,
|
||||||
Tämä vaihtelee, koska oikea osoitin voi liikkua
|
the time complexity is $O(n)$.
|
||||||
minkä tahansa matkan eteenpäin taulukossa.
|
|
||||||
Kuitenkin oikea osoitin liikkuu \textit{yhteensä}
|
|
||||||
$O(n)$ askelta algoritmin aikana, koska se voi
|
|
||||||
liikkua vain eteenpäin.
|
|
||||||
|
|
||||||
Koska sekä vasen että oikea osoitin liikkuvat
|
\subsubsection{Sum of two numbers}
|
||||||
$O(n)$ askelta algoritmin aikana,
|
|
||||||
algoritmin aikavaativuus on $O(n)$.
|
|
||||||
|
|
||||||
\subsubsection{Kahden luvun summa}
|
\index{2SUM problem}
|
||||||
|
|
||||||
\index{2SUM-ongelma}
|
Given an array of $n$ integers and an integer $x$,
|
||||||
|
our task is to find two numbers in array
|
||||||
|
whose sum is $x$ or report that there are no such numbers.
|
||||||
|
This problem is known as the \key{2SUM} problem,
|
||||||
|
and it can be solved efficiently using the
|
||||||
|
two pointers method.
|
||||||
|
|
||||||
Annettuna on taulukko, jossa on $n$ kokonaislukua,
|
First, we sort the numbers in the array in
|
||||||
sekä kokonaisluku $x$.
|
increasing order.
|
||||||
Tehtävänä on etsiä taulukosta kaksi lukua,
|
After this, we iterate through the array using
|
||||||
joiden summa on $x$, tai todeta,
|
two pointers that begin at both ends of the array.
|
||||||
että tämä ei ole mahdollista.
|
The left pointer begins from the first element
|
||||||
Tämä ongelma tunnetaan tunnetaan nimellä
|
and moves one step forward on each turn.
|
||||||
\key{2SUM} ja se ratkeaa tehokkaasti
|
The right pointer begins from the last element
|
||||||
kahden osoittimen tekniikalla.
|
and always moves backward until the sum of the range
|
||||||
|
defined by the pointers is at most $x$.
|
||||||
|
If the sum is exactly $x$, we have found a solution.
|
||||||
|
|
||||||
Taulukon luvut järjestetään ensin
|
For example, consider the following array when
|
||||||
pienimmästä suurimpaan, minkä jälkeen
|
our task is to find two elements whose sum is $x=12$:
|
||||||
taulukkoa aletaan käydä läpi kahdella osoittimella,
|
|
||||||
jotka lähtevät liikkelle taulukon molemmista päistä.
|
|
||||||
Vasen osoitin aloittaa taulukon alusta ja
|
|
||||||
liikkuu joka vaiheessa askeleen eteenpäin.
|
|
||||||
Oikea osoitin taas aloittaa taulukon lopusta
|
|
||||||
ja peruuttaa vuorollaan taaksepäin, kunnes osoitinten
|
|
||||||
määrittämän välin lukujen summa on enintään $x$.
|
|
||||||
Jos summa on tarkalleen $x$, ratkaisu on löytynyt.
|
|
||||||
|
|
||||||
Tarkastellaan algoritmin toimintaa
|
|
||||||
seuraavassa taulukossa, kun tavoitteena on muodostaa
|
|
||||||
summa $x=12$:
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (8,1);
|
\draw (0,0) grid (8,1);
|
||||||
|
@ -310,9 +288,10 @@ summa $x=12$:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Seuraavassa on algoritmin aloitustilanne.
|
The initial positions of the pointers
|
||||||
Lukujen summa on $1+10=11$, joka on pienempi
|
are as follows.
|
||||||
kuin $x$:n arvo.
|
The sum of the numbers is $1+10=11$
|
||||||
|
that is smaller than $x$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -344,9 +323,9 @@ kuin $x$:n arvo.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Seuraavaksi vasen osoitin liikkuu askeleen eteenpäin.
|
Then the left pointer moves one step forward.
|
||||||
Oikea osoitin peruuttaa kolme askelta, minkä jälkeen
|
The right pointer moves three steps backward,
|
||||||
summana on $4+7=11$.
|
and the sum becomes $4+7=11$.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -378,8 +357,9 @@ summana on $4+7=11$.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Sitten vasen osoitin siirtyy jälleen askeleen eteenpäin.
|
After this, the left pointer moves one step forward again.
|
||||||
Oikea osoitin pysyy paikallaan ja ratkaisu $5+7=12$ on löytynyt.
|
The right pointer doesn't move, and the solution
|
||||||
|
$5+7=12$ has been found.
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -411,28 +391,27 @@ Oikea osoitin pysyy paikallaan ja ratkaisu $5+7=12$ on löytynyt.
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Algoritmin alussa taulukon järjestäminen vie
|
At the beginning of the algorithm,
|
||||||
aikaa $O(n \log n)$.
|
the sorting takes $O(n \log n)$ time.
|
||||||
Tämän jälkeen vasen osoitin liikkuu $O(n)$ askelta
|
After this, the left pointer moves $O(n)$ steps
|
||||||
eteenpäin ja oikea osoitin liikkuu $O(n)$ askelta
|
forward, and the right pointer moves $O(n)$ steps
|
||||||
taaksepäin, mihin kuluu aikaa $O(n)$.
|
backward. Thus, the total time complexity
|
||||||
Algoritmin kokonaisaikavaativuus on siis $O(n \log n)$.
|
of the algorithm is $O(n \log n)$.
|
||||||
|
|
||||||
Huomaa, että tehtävän voi ratkaista myös
|
Note that it is possible to solve
|
||||||
toisella tavalla ajassa
|
in another way in $O(n \log n)$ time using binary search.
|
||||||
$O(n \log n)$ binäärihaun avulla.
|
In this solution, we iterate through the array
|
||||||
Tässä ratkaisussa jokaiselle taulukon luvulle
|
and for each number, we try to find another
|
||||||
etsitään binäärihaulla toista lukua niin,
|
number such that the sum is $x$.
|
||||||
että lukujen summa olisi yhteensä $x$.
|
This can be done by performing $n$ binary searches,
|
||||||
Binäärihaku suoritetaan $n$ kertaa ja
|
and each search takes $O(\log n)$ time.
|
||||||
jokainen binäärihaku vie aikaa $O(\log n)$.
|
|
||||||
|
|
||||||
\index{3SUM-ongelma}
|
\index{3SUM-ongelma}
|
||||||
Hieman vaikeampi ongelma on \key{3SUM},
|
A somewhat more difficult problem is
|
||||||
jossa taulukosta tuleekin etsiä kolme lukua,
|
the \key{3SUM} problem where our task is
|
||||||
joiden summa on $x$.
|
to find \emph{three} numbers whose sum is $x$.
|
||||||
Tämä ongelma on mahdollista ratkaista ajassa $O(n^2)$.
|
This problem can be solved in $O(n^2)$ time.
|
||||||
Keksitkö, miten se tapahtuu?
|
Can you see how it is possible?
|
||||||
|
|
||||||
\section{Lähin pienempi edeltäjä}
|
\section{Lähin pienempi edeltäjä}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue