Corrections
This commit is contained in:
parent
34f5c3f3b3
commit
af2c942fe5
135
luku03.tex
135
luku03.tex
|
@ -93,21 +93,21 @@ Simple algorithms for sorting an array
|
||||||
work in $O(n^2)$ time.
|
work in $O(n^2)$ time.
|
||||||
Such algorithms are short and usually
|
Such algorithms are short and usually
|
||||||
consist of two nested loops.
|
consist of two nested loops.
|
||||||
A famous $O(n^2)$ time algorithm for sorting
|
A famous $O(n^2)$ time sorting algorithm
|
||||||
is \key{bubble sort} where the elements
|
is \key{bubble sort} where the elements
|
||||||
''bubble'' forward in the array according to their values.
|
''bubble'' in the array according to their values.
|
||||||
|
|
||||||
Bubble sort consists of $n-1$ rounds.
|
Bubble sort consists of $n-1$ rounds.
|
||||||
On each round, the algorithm iterates through
|
On each round, the algorithm iterates through
|
||||||
the elements in the array.
|
the elements in the array.
|
||||||
Whenever two successive elements are found
|
Whenever two consecutive elements are found
|
||||||
that are not in correct order,
|
that are not in correct order,
|
||||||
the algorithm swaps them.
|
the algorithm swaps them.
|
||||||
The algorithm can be implemented as follows
|
The algorithm can be implemented as follows
|
||||||
for array
|
for array
|
||||||
$\texttt{t}[1],\texttt{t}[2],\ldots,\texttt{t}[n]$:
|
$\texttt{t}[1],\texttt{t}[2],\ldots,\texttt{t}[n]$:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int i = 1; i <= n-1; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
for (int j = 1; j <= n-i; j++) {
|
for (int j = 1; j <= n-i; j++) {
|
||||||
if (t[j] > t[j+1]) swap(t[j],t[j+1]);
|
if (t[j] > t[j+1]) swap(t[j],t[j+1]);
|
||||||
}
|
}
|
||||||
|
@ -115,10 +115,10 @@ for (int i = 1; i <= n-1; i++) {
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
After the first round of the algorithm,
|
After the first round of the algorithm,
|
||||||
the largest element is in the correct place,
|
the largest element will be in the correct position,
|
||||||
after the second round the second largest
|
and in general, after $k$ rounds, the $k$ largest
|
||||||
element is in the correct place, etc.
|
elements will be in the correct positions.
|
||||||
Thus, after $n-1$ rounds, all elements
|
Thus, after $n$ rounds, the whole array
|
||||||
will be sorted.
|
will be sorted.
|
||||||
|
|
||||||
For example, in the array
|
For example, in the array
|
||||||
|
@ -263,20 +263,20 @@ as follows:
|
||||||
\index{inversion}
|
\index{inversion}
|
||||||
|
|
||||||
Bubble sort is an example of a sorting
|
Bubble sort is an example of a sorting
|
||||||
algorithm that always swaps successive
|
algorithm that always swaps consecutive
|
||||||
elements in the array.
|
elements in the array.
|
||||||
It turns out that the time complexity
|
It turns out that the time complexity
|
||||||
of this kind of an algorithm is \emph{always}
|
of such an algorithm is \emph{always}
|
||||||
at least $O(n^2)$ because in the worst case,
|
at least $O(n^2)$ because in the worst case,
|
||||||
$O(n^2)$ swaps are required for sorting the array.
|
$O(n^2)$ swaps are required for sorting the array.
|
||||||
|
|
||||||
A useful concept when analyzing sorting
|
A useful concept when analyzing sorting
|
||||||
algorithms is an \key{inversion}.
|
algorithms is an \key{inversion}:
|
||||||
It is a pair of elements
|
a pair of elements
|
||||||
$(\texttt{t}[a],\texttt{t}[b])$
|
$(\texttt{t}[a],\texttt{t}[b])$
|
||||||
in the array such that
|
in the array such that
|
||||||
$a<b$ and $\texttt{t}[a]>\texttt{t}[b]$,
|
$a<b$ and $\texttt{t}[a]>\texttt{t}[b]$,
|
||||||
i.e., they are in wrong order.
|
i.e., the elements are in the wrong order.
|
||||||
For example, in the array
|
For example, in the array
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
|
@ -303,19 +303,19 @@ For example, in the array
|
||||||
\end{center}
|
\end{center}
|
||||||
the inversions are $(6,3)$, $(6,5)$ and $(9,8)$.
|
the inversions are $(6,3)$, $(6,5)$ and $(9,8)$.
|
||||||
The number of inversions indicates
|
The number of inversions indicates
|
||||||
how sorted the array is.
|
how much work is needed to sort the array.
|
||||||
An array is completely sorted when
|
An array is completely sorted when
|
||||||
there are no inversions.
|
there are no inversions.
|
||||||
On the other hand, if the array elements
|
On the other hand, if the array elements
|
||||||
are in reverse order,
|
are in the reverse order,
|
||||||
the number of inversions is maximum:
|
the number of inversions is the largest possible:
|
||||||
\[1+2+\cdots+(n-1)=\frac{n(n-1)}{2} = O(n^2)\]
|
\[1+2+\cdots+(n-1)=\frac{n(n-1)}{2} = O(n^2)\]
|
||||||
|
|
||||||
Swapping successive elements that are
|
Swapping a pair of consecutive elements that are
|
||||||
in wrong order removes exactly one inversion
|
in the wrong order removes exactly one inversion
|
||||||
from the array.
|
from the array.
|
||||||
Thus, if a sorting algorithm can only
|
Hence, if a sorting algorithm can only
|
||||||
swap successive elements, each swap removes
|
swap consecutive elements, each swap removes
|
||||||
at most one inversion and the time complexity
|
at most one inversion and the time complexity
|
||||||
of the algorithm is at least $O(n^2)$.
|
of the algorithm is at least $O(n^2)$.
|
||||||
|
|
||||||
|
@ -324,28 +324,27 @@ of the algorithm is at least $O(n^2)$.
|
||||||
\index{merge sort}
|
\index{merge sort}
|
||||||
|
|
||||||
It is possible to sort an array efficiently
|
It is possible to sort an array efficiently
|
||||||
in $O(n \log n)$ time using an algorithm
|
in $O(n \log n)$ time using algorithms
|
||||||
that is not limited to swapping successive elements.
|
that are not limited to swapping consecutive elements.
|
||||||
One such algorithm is \key{mergesort}
|
One such algorithm is \key{mergesort}
|
||||||
that sorts an array recursively by dividing
|
that is based on recursion.
|
||||||
it into smaller subarrays.
|
|
||||||
|
|
||||||
Mergesort sorts the subarray $[a,b]$ as follows:
|
Mergesort sorts a subarray \texttt{t}$[a,b]$ as follows:
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item If $a=b$, don't do anything because the subarray is already sorted.
|
\item If $a=b$, do not do anything because the subarray is already sorted.
|
||||||
\item Calculate the index of the middle element: $k=\lfloor (a+b)/2 \rfloor$.
|
\item Calculate the index of the middle element: $k=\lfloor (a+b)/2 \rfloor$.
|
||||||
\item Recursively sort the subarray $[a,k]$.
|
\item Recursively sort the subarray \texttt{t}$[a,k]$.
|
||||||
\item Recursively sort the subarray $[k+1,b]$.
|
\item Recursively sort the subarray \texttt{t}$[k+1,b]$.
|
||||||
\item \emph{Merge} the sorted subarrays $[a,k]$ and $[k+1,b]$
|
\item \emph{Merge} the sorted subarrays \texttt{t}$[a,k]$ and \texttt{t}$[k+1,b]$
|
||||||
into a sorted subarray $[a,b]$.
|
into a sorted subarray \texttt{t}$[a,b]$.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
Mergesort is an efficient algorithm because it
|
Mergesort is an efficient algorithm because it
|
||||||
halves the size of the subarray at each step.
|
halves the size of the subarray at each step.
|
||||||
The recursion consists of $O(\log n)$ levels,
|
The recursion consists of $O(\log n)$ levels,
|
||||||
and processing each level takes $O(n)$ time.
|
and processing each level takes $O(n)$ time.
|
||||||
Merging the subarrays $[a,k]$ and $[k+1,b]$
|
Merging the subarrays \texttt{t}$[a,k]$ and \texttt{t}$[k+1,b]$
|
||||||
is possible in linear time because they are already sorted.
|
is possible in linear time because they are already sorted.
|
||||||
|
|
||||||
For example, consider sorting the following array:
|
For example, consider sorting the following array:
|
||||||
|
@ -515,7 +514,7 @@ $x$ and $y$ are compared.
|
||||||
If $x<y$, the process continues to the left,
|
If $x<y$, the process continues to the left,
|
||||||
and otherwise to the right.
|
and otherwise to the right.
|
||||||
The results of the process are the possible
|
The results of the process are the possible
|
||||||
ways to order the array, a total of $n!$ ways.
|
ways to sort the array, a total of $n!$ ways.
|
||||||
For this reason, the height of the tree
|
For this reason, the height of the tree
|
||||||
must be at least
|
must be at least
|
||||||
\[ \log_2(n!) = \log_2(1)+\log_2(2)+\cdots+\log_2(n).\]
|
\[ \log_2(n!) = \log_2(1)+\log_2(2)+\cdots+\log_2(n).\]
|
||||||
|
@ -525,7 +524,7 @@ changing the value of each element to $\log_2(n/2)$.
|
||||||
This yields an estimate
|
This yields an estimate
|
||||||
\[ \log_2(n!) \ge (n/2) \cdot \log_2(n/2),\]
|
\[ \log_2(n!) \ge (n/2) \cdot \log_2(n/2),\]
|
||||||
so the height of the tree and the minimum
|
so the height of the tree and the minimum
|
||||||
possible number of steps in an sorting
|
possible number of steps in a sorting
|
||||||
algorithm in the worst case
|
algorithm in the worst case
|
||||||
is at least $n \log n$.
|
is at least $n \log n$.
|
||||||
|
|
||||||
|
@ -533,7 +532,7 @@ is at least $n \log n$.
|
||||||
|
|
||||||
\index{counting sort}
|
\index{counting sort}
|
||||||
|
|
||||||
The lower bound $n \log n$ doesn't apply to
|
The lower bound $n \log n$ does not apply to
|
||||||
algorithms that do not compare array elements
|
algorithms that do not compare array elements
|
||||||
but use some other information.
|
but use some other information.
|
||||||
An example of such an algorithm is
|
An example of such an algorithm is
|
||||||
|
@ -572,7 +571,7 @@ For example, the array
|
||||||
\node at (7.5,1.4) {$8$};
|
\node at (7.5,1.4) {$8$};
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
produces the following bookkeeping array:
|
corresponds to the following bookkeeping array:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.7]
|
\begin{tikzpicture}[scale=0.7]
|
||||||
\draw (0,0) grid (9,1);
|
\draw (0,0) grid (9,1);
|
||||||
|
@ -600,15 +599,15 @@ produces the following bookkeeping array:
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
For example, the value of element 3
|
For example, the value at index 3
|
||||||
in the bookkeeping array is 2,
|
in the bookkeeping array is 2,
|
||||||
because the element 3 appears two times
|
because the element 3 appears 2 times
|
||||||
in the original array (indices 2 and 6).
|
in the original array (indices 2 and 6).
|
||||||
|
|
||||||
The construction of the bookkeeping array
|
The construction of the bookkeeping array
|
||||||
takes $O(n)$ time. After this, the sorted array
|
takes $O(n)$ time. After this, the sorted array
|
||||||
can be created in $O(n)$ time because
|
can be created in $O(n)$ time because
|
||||||
the amount of each element can be retrieved
|
the number of occurrences of each element can be retrieved
|
||||||
from the bookkeeping array.
|
from the bookkeeping array.
|
||||||
Thus, the total time complexity of counting
|
Thus, the total time complexity of counting
|
||||||
sort is $O(n)$.
|
sort is $O(n)$.
|
||||||
|
@ -622,8 +621,8 @@ be used as indices in the bookkeeping array.
|
||||||
|
|
||||||
\index{sort@\texttt{sort}}
|
\index{sort@\texttt{sort}}
|
||||||
|
|
||||||
It is almost never a good idea to use
|
It is almost never a good idea to implement
|
||||||
an own implementation of a sorting algorithm
|
an own sorting algorithm
|
||||||
in a contest, because there are good
|
in a contest, because there are good
|
||||||
implementations available in programming languages.
|
implementations available in programming languages.
|
||||||
For example, the C++ standard library contains
|
For example, the C++ standard library contains
|
||||||
|
@ -639,8 +638,8 @@ that a home-made sorting function would be better.
|
||||||
|
|
||||||
In this section we will see how to use the
|
In this section we will see how to use the
|
||||||
C++ \texttt{sort} function.
|
C++ \texttt{sort} function.
|
||||||
The following code sorts the numbers
|
The following code sorts
|
||||||
in vector \texttt{t} in increasing order:
|
a vector in increasing order:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
vector<int> v = {4,2,5,3,5,8,3};
|
vector<int> v = {4,2,5,3,5,8,3};
|
||||||
sort(v.begin(),v.end());
|
sort(v.begin(),v.end());
|
||||||
|
@ -648,7 +647,7 @@ sort(v.begin(),v.end());
|
||||||
After the sorting, the contents of the
|
After the sorting, the contents of the
|
||||||
vector will be
|
vector will be
|
||||||
$[2,3,3,4,5,5,8]$.
|
$[2,3,3,4,5,5,8]$.
|
||||||
The default sorting order in increasing,
|
The default sorting order is increasing,
|
||||||
but a reverse order is possible as follows:
|
but a reverse order is possible as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
sort(v.rbegin(),v.rend());
|
sort(v.rbegin(),v.rend());
|
||||||
|
@ -681,7 +680,7 @@ whenever it is needed to find out the order of two elements.
|
||||||
Most C++ data types have a built-in comparison operator
|
Most C++ data types have a built-in comparison operator
|
||||||
and elements of those types can be sorted automatically.
|
and elements of those types can be sorted automatically.
|
||||||
For example, numbers are sorted according to their values
|
For example, numbers are sorted according to their values
|
||||||
and strings are sorted according to alphabetical order.
|
and strings are sorted in alphabetical order.
|
||||||
|
|
||||||
\index{pair@\texttt{pair}}
|
\index{pair@\texttt{pair}}
|
||||||
|
|
||||||
|
@ -770,9 +769,9 @@ sort(v.begin(), v.end(), cmp);
|
||||||
|
|
||||||
A general method for searching for an element
|
A general method for searching for an element
|
||||||
in an array is to use a \texttt{for} loop
|
in an array is to use a \texttt{for} loop
|
||||||
that iterates through all elements in the array.
|
that iterates through the elements in the array.
|
||||||
For example, the following code searches for
|
For example, the following code searches for
|
||||||
an element $x$ in array \texttt{t}:
|
an element $x$ in the array \texttt{t}:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int i = 1; i <= n; i++) {
|
for (int i = 1; i <= n; i++) {
|
||||||
|
@ -780,11 +779,11 @@ for (int i = 1; i <= n; i++) {
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
The time complexity of this approach is $O(n)$
|
The time complexity of this approach is $O(n)$,
|
||||||
because in the worst case, we have to check
|
because in the worst case, it is needed to check
|
||||||
all elements in the array.
|
all elements in the array.
|
||||||
If the array can contain any elements,
|
If the array can contain any elements,
|
||||||
this is also the best possible approach because
|
this is also the best possible approach, because
|
||||||
there is no additional information available where
|
there is no additional information available where
|
||||||
in the array we should search for the element $x$.
|
in the array we should search for the element $x$.
|
||||||
|
|
||||||
|
@ -802,14 +801,14 @@ in $O(\log n)$ time.
|
||||||
The traditional way to implement binary search
|
The traditional way to implement binary search
|
||||||
resembles looking for a word in a dictionary.
|
resembles looking for a word in a dictionary.
|
||||||
At each step, the search halves the active region in the array,
|
At each step, the search halves the active region in the array,
|
||||||
until the desired element is found, or it turns out
|
until the target element is found, or it turns out
|
||||||
that there is no such element.
|
that there is no such element.
|
||||||
|
|
||||||
First, the search checks the middle element in the array.
|
First, the search checks the middle element in the array.
|
||||||
If the middle element is the desired element,
|
If the middle element is the target element,
|
||||||
the search terminates.
|
the search terminates.
|
||||||
Otherwise, the search recursively continues
|
Otherwise, the search recursively continues
|
||||||
to the left half or to the right half of the array,
|
to the left or right half of the array,
|
||||||
depending on the value of the middle element.
|
depending on the value of the middle element.
|
||||||
|
|
||||||
The above idea can be implemented as follows:
|
The above idea can be implemented as follows:
|
||||||
|
@ -832,18 +831,18 @@ so the time complexity is $O(\log n)$.
|
||||||
\subsubsection{Method 2}
|
\subsubsection{Method 2}
|
||||||
|
|
||||||
An alternative method for implementing binary search
|
An alternative method for implementing binary search
|
||||||
is based on a more efficient way to iterate through
|
is based on an efficient way to iterate through
|
||||||
the elements in the array.
|
the elements in the array.
|
||||||
The idea is to make jumps and slow the speed
|
The idea is to make jumps and slow the speed
|
||||||
when we get closer to the desired element.
|
when we get closer to the target element.
|
||||||
|
|
||||||
The search goes through the array from the left to
|
The search goes through the array from left to
|
||||||
the right, and the initial jump length is $n/2$.
|
right, and the initial jump length is $n/2$.
|
||||||
At each step, the jump length will be halved:
|
At each step, the jump length will be halved:
|
||||||
first $n/4$, then $n/8$, $n/16$, etc., until
|
first $n/4$, then $n/8$, $n/16$, etc., until
|
||||||
finally the length is 1.
|
finally the length is 1.
|
||||||
After the jumps, either the desired element has
|
After the jumps, either the target element has
|
||||||
been found or we know that it doesn't exist in the array.
|
been found or we know that it does not appear in the array.
|
||||||
|
|
||||||
The following code implements the above idea:
|
The following code implements the above idea:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -854,10 +853,10 @@ for (int b = n/2; b >= 1; b /= 2) {
|
||||||
if (t[k] == x) // x was found at index k
|
if (t[k] == x) // x was found at index k
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Variable $k$ is the position in the array,
|
The variables $k$ and $b$ contain the position
|
||||||
and variable $b$ is the jump length.
|
in the array and the jump length.
|
||||||
If the array contains the element $x$,
|
If the array contains the element $x$,
|
||||||
the index of the element will be in variable $k$
|
the index of the element will be in the variable $k$
|
||||||
after the search.
|
after the search.
|
||||||
The time complexity of the algorithm is $O(\log n)$,
|
The time complexity of the algorithm is $O(\log n)$,
|
||||||
because the code in the \texttt{while} loop
|
because the code in the \texttt{while} loop
|
||||||
|
@ -866,7 +865,7 @@ is performed at most twice for each jump length.
|
||||||
\subsubsection{Finding the smallest solution}
|
\subsubsection{Finding the smallest solution}
|
||||||
|
|
||||||
In practice, it is seldom needed to implement
|
In practice, it is seldom needed to implement
|
||||||
binary search for array search,
|
binary search for searching elements in an array,
|
||||||
because we can use the standard library instead.
|
because we can use the standard library instead.
|
||||||
For example, the C++ functions \texttt{lower\_bound}
|
For example, the C++ functions \texttt{lower\_bound}
|
||||||
and \texttt{upper\_bound} implement binary search,
|
and \texttt{upper\_bound} implement binary search,
|
||||||
|
@ -874,7 +873,7 @@ and the data structure \texttt{set} maintains a
|
||||||
set of elements with $O(\log n)$ time operations.
|
set of elements with $O(\log n)$ time operations.
|
||||||
|
|
||||||
However, an important use for binary search is
|
However, an important use for binary search is
|
||||||
to find a position where the value of a function changes.
|
to find the position where the value of a function changes.
|
||||||
Suppose that we wish to find the smallest value $k$
|
Suppose that we wish to find the smallest value $k$
|
||||||
that is a valid solution for a problem.
|
that is a valid solution for a problem.
|
||||||
We are given a function $\texttt{ok}(x)$
|
We are given a function $\texttt{ok}(x)$
|
||||||
|
@ -917,11 +916,11 @@ The algorithm calls the function \texttt{ok}
|
||||||
$O(\log z)$ times, so the total time complexity
|
$O(\log z)$ times, so the total time complexity
|
||||||
depends on the function \texttt{ok}.
|
depends on the function \texttt{ok}.
|
||||||
For example, if the function works in $O(n)$ time,
|
For example, if the function works in $O(n)$ time,
|
||||||
the total time complexity becomes $O(n \log z)$.
|
the total time complexity is $O(n \log z)$.
|
||||||
|
|
||||||
\subsubsection{Finding the maximum value}
|
\subsubsection{Finding the maximum value}
|
||||||
|
|
||||||
Binary search can also be used for finding
|
Binary search can also be used to find
|
||||||
the maximum value for a function that is
|
the maximum value for a function that is
|
||||||
first increasing and then decreasing.
|
first increasing and then decreasing.
|
||||||
Our task is to find a value $k$ such that
|
Our task is to find a value $k$ such that
|
||||||
|
@ -930,7 +929,7 @@ Our task is to find a value $k$ such that
|
||||||
\item
|
\item
|
||||||
$f(x)<f(x+1)$ when $x<k$, and
|
$f(x)<f(x+1)$ when $x<k$, and
|
||||||
\item
|
\item
|
||||||
$f(x)>f(x+1)$ when $x >= k$.
|
$f(x)>f(x+1)$ when $x \ge k$.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
The idea is to use binary search
|
The idea is to use binary search
|
||||||
|
@ -948,8 +947,8 @@ for (int b = z; b >= 1; b /= 2) {
|
||||||
int k = x+1;
|
int k = x+1;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Note that unlike in the regular binary search,
|
Note that unlike in the standard binary search,
|
||||||
here it is not allowed that successive values
|
here it is not allowed that consecutive values
|
||||||
of the function are equal.
|
of the function are equal.
|
||||||
In this case it would not be possible to know
|
In this case it would not be possible to know
|
||||||
how to continue the search.
|
how to continue the search.
|
Loading…
Reference in New Issue