Corrections
This commit is contained in:
parent
7a2ac1fbe2
commit
eae5c6bd90
124
luku04.tex
124
luku04.tex
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
A \key{data structure} is a way to store
|
A \key{data structure} is a way to store
|
||||||
data in the memory of the computer.
|
data in the memory of the computer.
|
||||||
It is important to choose a suitable
|
It is important to choose an appropriate
|
||||||
data structure for a problem,
|
data structure for a problem,
|
||||||
because each data structure has its own
|
because each data structure has its own
|
||||||
advantages and disadvantages.
|
advantages and disadvantages.
|
||||||
|
@ -24,13 +24,12 @@ in the standard library.
|
||||||
|
|
||||||
\index{dynamic array}
|
\index{dynamic array}
|
||||||
\index{vector}
|
\index{vector}
|
||||||
\index{vector@\texttt{vector}}
|
|
||||||
|
|
||||||
A \key{dynamic array} is an array whose
|
A \key{dynamic array} is an array whose
|
||||||
size can be changed during the execution
|
size can be changed during the execution
|
||||||
of the code.
|
of the program.
|
||||||
The most popular dynamic array in C++ is
|
The most popular dynamic array in C++ is
|
||||||
the \key{vector} structure (\texttt{vector}),
|
the \texttt{vector} structure,
|
||||||
that can be used almost like a regular array.
|
that can be used almost like a regular array.
|
||||||
|
|
||||||
The following code creates an empty vector and
|
The following code creates an empty vector and
|
||||||
|
@ -107,23 +106,22 @@ uses a regular array.
|
||||||
If the size of the vector increases and
|
If the size of the vector increases and
|
||||||
the array becomes too small,
|
the array becomes too small,
|
||||||
a new array is allocated and all the
|
a new array is allocated and all the
|
||||||
elements are copied to the new array.
|
elements are moved to the new array.
|
||||||
However, this doesn't happen often and the
|
However, this does not happen often and the
|
||||||
time complexity of
|
average time complexity of
|
||||||
\texttt{push\_back} is $O(1)$ on average.
|
\texttt{push\_back} is $O(1)$.
|
||||||
|
|
||||||
\index{string}
|
\index{string}
|
||||||
\index{string@\texttt{string}}
|
|
||||||
|
|
||||||
Also the \key{string} structure (\texttt{string})
|
The \texttt{string} structure
|
||||||
is a dynamic array that can be used almost like a vector.
|
is also a dynamic array that can be used almost like a vector.
|
||||||
In addition, there is special syntax for strings
|
In addition, there is special syntax for strings
|
||||||
that is not available in other data structures.
|
that is not available in other data structures.
|
||||||
Strings can be combined using the \texttt{+} symbol.
|
Strings can be combined using the \texttt{+} symbol.
|
||||||
The function $\texttt{substr}(k,x)$ returns the substring
|
The function $\texttt{substr}(k,x)$ returns the substring
|
||||||
that begins at index $k$ and has length $x$.
|
that begins at index $k$ and has length $x$,
|
||||||
The function $\texttt{find}(\texttt{t})$ finds the position
|
and the function $\texttt{find}(\texttt{t})$ finds the position
|
||||||
where a substring \texttt{t} appears in the string.
|
of the first occurrence of a substring \texttt{t}.
|
||||||
|
|
||||||
The following code presents some string operations:
|
The following code presents some string operations:
|
||||||
|
|
||||||
|
@ -140,11 +138,9 @@ cout << c << "\n"; // tiva
|
||||||
\section{Set structure}
|
\section{Set structure}
|
||||||
|
|
||||||
\index{set}
|
\index{set}
|
||||||
\index{set@\texttt{set}}
|
|
||||||
\index{unordered\_set@\texttt{unordered\_set}}
|
|
||||||
|
|
||||||
A \key{set} is a data structure that
|
A \key{set} is a data structure that
|
||||||
contains a collection of elements.
|
maintains a collection of elements.
|
||||||
The basic operations in a set are element
|
The basic operations in a set are element
|
||||||
insertion, search and removal.
|
insertion, search and removal.
|
||||||
|
|
||||||
|
@ -167,10 +163,10 @@ often more efficient.
|
||||||
|
|
||||||
The following code creates a set
|
The following code creates a set
|
||||||
that consists of integers,
|
that consists of integers,
|
||||||
and shows how to use it.
|
and shows some of the operations.
|
||||||
The function \texttt{insert} adds an element to the set,
|
The function \texttt{insert} adds an element to the set,
|
||||||
the function \texttt{count} returns how many times an
|
the function \texttt{count} returns the number of occurrences
|
||||||
element appears in the set,
|
of an element,
|
||||||
and the function \texttt{erase} removes an element from the set.
|
and the function \texttt{erase} removes an element from the set.
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -201,7 +197,7 @@ for (auto x : s) {
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
An important property of a set is
|
An important property of a set is
|
||||||
that all the elements are distinct.
|
that all the elements are \emph{distinct}.
|
||||||
Thus, the function \texttt{count} always returns
|
Thus, the function \texttt{count} always returns
|
||||||
either 0 (the element is not in the set)
|
either 0 (the element is not in the set)
|
||||||
or 1 (the element is in the set),
|
or 1 (the element is in the set),
|
||||||
|
@ -218,15 +214,12 @@ s.insert(5);
|
||||||
cout << s.count(5) << "\n"; // 1
|
cout << s.count(5) << "\n"; // 1
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\index{multiset@\texttt{multiset}}
|
C++ also has the structures
|
||||||
\index{unordered\_multiset@\texttt{unordered\_multiset}}
|
|
||||||
|
|
||||||
C++ also contains the structures
|
|
||||||
\texttt{multiset} and \texttt{unordered\_multiset}
|
\texttt{multiset} and \texttt{unordered\_multiset}
|
||||||
that work otherwise like \texttt{set}
|
that work otherwise like \texttt{set}
|
||||||
and \texttt{unordered\_set}
|
and \texttt{unordered\_set}
|
||||||
but they can contain multiple copies of an element.
|
but they can contain multiple instances of an element.
|
||||||
For example, in the following code all copies
|
For example, in the following code all three instances
|
||||||
of the number 5 are added to the set:
|
of the number 5 are added to the set:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -252,17 +245,15 @@ cout << s.count(5) << "\n"; // 2
|
||||||
|
|
||||||
\section{Map structure}
|
\section{Map structure}
|
||||||
|
|
||||||
\index{hakemisto@hakemisto}
|
\index{map}
|
||||||
\index{map@\texttt{map}}
|
|
||||||
\index{unordered\_map@\texttt{unordered\_map}}
|
|
||||||
|
|
||||||
A \key{map} is a generalized array
|
A \key{map} is a generalized array
|
||||||
that consists of key-value-pairs.
|
that consists of key-value-pairs.
|
||||||
While the keys in a regular array are always
|
While the keys in a regular array are always
|
||||||
the successive integers $0,1,\ldots,n-1$,
|
the consecutive integers $0,1,\ldots,n-1$,
|
||||||
where $n$ is the size of the array,
|
where $n$ is the size of the array,
|
||||||
the keys in a map can be of any data type and
|
the keys in a map can be of any data type and
|
||||||
they don't have to be successive values.
|
they do not have to be consecutive values.
|
||||||
|
|
||||||
C++ contains two map implementations that
|
C++ contains two map implementations that
|
||||||
correspond to the set implementations:
|
correspond to the set implementations:
|
||||||
|
@ -285,8 +276,8 @@ m["harpsichord"] = 9;
|
||||||
cout << m["banana"] << "\n"; // 3
|
cout << m["banana"] << "\n"; // 3
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
If a value of a key is requested
|
If the value of a key is requested
|
||||||
but the map doesn't contain it,
|
but the map does not contain it,
|
||||||
the key is automatically added to the map with
|
the key is automatically added to the map with
|
||||||
a default value.
|
a default value.
|
||||||
For example, in the following code,
|
For example, in the following code,
|
||||||
|
@ -317,8 +308,7 @@ for (auto x : m) {
|
||||||
\index{iterator}
|
\index{iterator}
|
||||||
|
|
||||||
Many functions in the C++ standard library
|
Many functions in the C++ standard library
|
||||||
are given iterators to data structures,
|
operate with iterators.
|
||||||
and iterators often correspond to ranges.
|
|
||||||
An \key{iterator} is a variable that points
|
An \key{iterator} is a variable that points
|
||||||
to an element in a data structure.
|
to an element in a data structure.
|
||||||
|
|
||||||
|
@ -344,7 +334,7 @@ Note the asymmetry in the iterators:
|
||||||
while \texttt{s.end()} points outside the data structure.
|
while \texttt{s.end()} points outside the data structure.
|
||||||
Thus, the range defined by the iterators is \emph{half-open}.
|
Thus, the range defined by the iterators is \emph{half-open}.
|
||||||
|
|
||||||
\subsubsection{Handling ranges}
|
\subsubsection{Working with ranges}
|
||||||
|
|
||||||
Iterators are used in C++ standard library functions
|
Iterators are used in C++ standard library functions
|
||||||
that work with ranges of data structures.
|
that work with ranges of data structures.
|
||||||
|
@ -402,7 +392,7 @@ auto it = s.begin();
|
||||||
cout << *it << "\n";
|
cout << *it << "\n";
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Iterators can be moved using operators
|
Iterators can be moved using the operators
|
||||||
\texttt{++} (forward) and \texttt{---} (backward),
|
\texttt{++} (forward) and \texttt{---} (backward),
|
||||||
meaning that the iterator moves to the next
|
meaning that the iterator moves to the next
|
||||||
or previous element in the set.
|
or previous element in the set.
|
||||||
|
@ -422,7 +412,7 @@ cout << *it << "\n";
|
||||||
|
|
||||||
The function $\texttt{find}(x)$ returns an iterator
|
The function $\texttt{find}(x)$ returns an iterator
|
||||||
that points to an element whose value is $x$.
|
that points to an element whose value is $x$.
|
||||||
However, if the set doesn't contain $x$,
|
However, if the set does not contain $x$,
|
||||||
the iterator will be \texttt{end}.
|
the iterator will be \texttt{end}.
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -432,16 +422,15 @@ if (it == s.end()) cout << "x is missing";
|
||||||
|
|
||||||
The function $\texttt{lower\_bound}(x)$ returns
|
The function $\texttt{lower\_bound}(x)$ returns
|
||||||
an iterator to the smallest element in the set
|
an iterator to the smallest element in the set
|
||||||
whose value is at least $x$.
|
whose value is \emph{at least} $x$, and
|
||||||
Correspondingly,
|
|
||||||
the function $\texttt{upper\_bound}(x)$
|
the function $\texttt{upper\_bound}(x)$
|
||||||
returns an iterator to the smallest element
|
returns an iterator to the smallest element
|
||||||
in the set whose value is larger than $x$.
|
in the set whose value is \emph{larger than} $x$.
|
||||||
If such elements do not exist,
|
If such elements do not exist,
|
||||||
the return value of the functions will be \texttt{end}.
|
the return value of the functions will be \texttt{end}.
|
||||||
These functions are not supported by the
|
These functions are not supported by the
|
||||||
\texttt{unordered\_set} structure that
|
\texttt{unordered\_set} structure that
|
||||||
doesn't maintain the order of the elements.
|
does not maintain the order of the elements.
|
||||||
|
|
||||||
\begin{samepage}
|
\begin{samepage}
|
||||||
For example, the following code finds the element
|
For example, the following code finds the element
|
||||||
|
@ -450,7 +439,7 @@ nearest to $x$:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
auto a = s.lower_bound(x);
|
auto a = s.lower_bound(x);
|
||||||
if (a == s.begin() && a == s.end()) {
|
if (a == s.begin() && a == s.end()) {
|
||||||
cout << "joukko on tyhjä\n";
|
cout << "the set is empty\n";
|
||||||
} else if (a == s.begin()) {
|
} else if (a == s.begin()) {
|
||||||
cout << *a << "\n";
|
cout << *a << "\n";
|
||||||
} else if (a == s.end()) {
|
} else if (a == s.end()) {
|
||||||
|
@ -473,7 +462,7 @@ If \texttt{a} equals \texttt{begin},
|
||||||
the corresponding element is nearest to $x$.
|
the corresponding element is nearest to $x$.
|
||||||
If \texttt{a} equals \texttt{end},
|
If \texttt{a} equals \texttt{end},
|
||||||
the last element in the set is nearest to $x$.
|
the last element in the set is nearest to $x$.
|
||||||
If none of the previous cases is true,
|
If none of the previous cases holds,
|
||||||
the element nearest to $x$ is either the
|
the element nearest to $x$ is either the
|
||||||
element that corresponds to $a$ or the previous element.
|
element that corresponds to $a$ or the previous element.
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
|
@ -483,9 +472,8 @@ element that corresponds to $a$ or the previous element.
|
||||||
\subsubsection{Bitset}
|
\subsubsection{Bitset}
|
||||||
|
|
||||||
\index{bitset}
|
\index{bitset}
|
||||||
\index{bitset@\texttt{bitset}}
|
|
||||||
|
|
||||||
A \key{bitset} (\texttt{bitset}) is an array
|
A \texttt{bitset} is an array
|
||||||
where each element is either 0 or 1.
|
where each element is either 0 or 1.
|
||||||
For example, the following code creates
|
For example, the following code creates
|
||||||
a bitset that contains 10 elements:
|
a bitset that contains 10 elements:
|
||||||
|
@ -536,12 +524,11 @@ cout << (a|b) << "\n"; // 1011111110
|
||||||
cout << (a^b) << "\n"; // 1001101110
|
cout << (a^b) << "\n"; // 1001101110
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\subsubsection{Pakka}
|
\subsubsection{Deque}
|
||||||
|
|
||||||
\index{deque}
|
\index{deque}
|
||||||
\index{deque@\texttt{deque}}
|
|
||||||
|
|
||||||
A \key{deque} (\texttt{deque}) is a dynamic array
|
A \texttt{deque} is a dynamic array
|
||||||
whose size can be changed at both ends of the array.
|
whose size can be changed at both ends of the array.
|
||||||
Like a vector, a deque contains functions
|
Like a vector, a deque contains functions
|
||||||
\texttt{push\_back} and \texttt{pop\_back}, but
|
\texttt{push\_back} and \texttt{pop\_back}, but
|
||||||
|
@ -565,12 +552,11 @@ For this reason, a deque is slower than a vector.
|
||||||
Still, the time complexity of adding and removing
|
Still, the time complexity of adding and removing
|
||||||
elements is $O(1)$ on average at both ends.
|
elements is $O(1)$ on average at both ends.
|
||||||
|
|
||||||
\subsubsection{Pino}
|
\subsubsection{Stack}
|
||||||
|
|
||||||
\index{stack}
|
\index{stack}
|
||||||
\index{stack@\texttt{stack}}
|
|
||||||
|
|
||||||
A \key{stack} (\texttt{stack})
|
A \texttt{stack}
|
||||||
is a data structure that provides two
|
is a data structure that provides two
|
||||||
$O(1)$ time operations:
|
$O(1)$ time operations:
|
||||||
adding an element to the top,
|
adding an element to the top,
|
||||||
|
@ -591,12 +577,11 @@ cout << s.top(); // 2
|
||||||
\subsubsection{Queue}
|
\subsubsection{Queue}
|
||||||
|
|
||||||
\index{queue}
|
\index{queue}
|
||||||
\index{queue@\texttt{queue}}
|
|
||||||
|
|
||||||
A \key{queue} (\texttt{queue}) also
|
A \texttt{queue} also
|
||||||
provides two $O(1)$ time operations:
|
provides two $O(1)$ time operations:
|
||||||
adding a new element to the end,
|
adding a element to the end of the queue,
|
||||||
and removing the first element.
|
and removing the first element in the queue.
|
||||||
It is only possible to access the first
|
It is only possible to access the first
|
||||||
and the last element of a queue.
|
and the last element of a queue.
|
||||||
|
|
||||||
|
@ -615,9 +600,8 @@ cout << s.front(); // 2
|
||||||
|
|
||||||
\index{priority queue}
|
\index{priority queue}
|
||||||
\index{heap}
|
\index{heap}
|
||||||
\index{priority\_queue@\texttt{priority\_queue}}
|
|
||||||
|
|
||||||
A \key{priority queue} (\texttt{priority\_queue})
|
A \texttt{priority\_queue}
|
||||||
maintains a set of elements.
|
maintains a set of elements.
|
||||||
The supported operations are insertion and,
|
The supported operations are insertion and,
|
||||||
depending on the type of the queue,
|
depending on the type of the queue,
|
||||||
|
@ -657,7 +641,8 @@ q.pop();
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
|
|
||||||
The following definition creates a priority queue
|
Using the following declaration,
|
||||||
|
we can create a priority queue
|
||||||
that supports finding and removing the minimum element:
|
that supports finding and removing the minimum element:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
|
@ -666,7 +651,7 @@ priority_queue<int,vector<int>,greater<int>> q;
|
||||||
|
|
||||||
\section{Comparison to sorting}
|
\section{Comparison to sorting}
|
||||||
|
|
||||||
Often it's possible to solve a problem
|
Often it is possible to solve a problem
|
||||||
using either data structures or sorting.
|
using either data structures or sorting.
|
||||||
Sometimes there are remarkable differences
|
Sometimes there are remarkable differences
|
||||||
in the actual efficiency of these approaches,
|
in the actual efficiency of these approaches,
|
||||||
|
@ -682,7 +667,7 @@ For example, for the lists
|
||||||
the answer is 3 because the numbers 2, 5
|
the answer is 3 because the numbers 2, 5
|
||||||
and 9 belong to both of the lists.
|
and 9 belong to both of the lists.
|
||||||
|
|
||||||
A straightforward solution for the problem is
|
A straightforward solution to the problem is
|
||||||
to go through all pairs of numbers in $O(n^2)$ time,
|
to go through all pairs of numbers in $O(n^2)$ time,
|
||||||
but next we will concentrate on
|
but next we will concentrate on
|
||||||
more efficient solutions.
|
more efficient solutions.
|
||||||
|
@ -690,7 +675,7 @@ more efficient solutions.
|
||||||
\subsubsection{Solution 1}
|
\subsubsection{Solution 1}
|
||||||
|
|
||||||
We construct a set of the numbers in $A$,
|
We construct a set of the numbers in $A$,
|
||||||
and after this, iterate through the numbers
|
and after this, we iterate through the numbers
|
||||||
in $B$ and check for each number if it
|
in $B$ and check for each number if it
|
||||||
also belongs to $A$.
|
also belongs to $A$.
|
||||||
This is efficient because the numbers in $A$
|
This is efficient because the numbers in $A$
|
||||||
|
@ -704,8 +689,8 @@ It is not needed to maintain an ordered set,
|
||||||
so instead of the \texttt{set} structure
|
so instead of the \texttt{set} structure
|
||||||
we can also use the \texttt{unordered\_set} structure.
|
we can also use the \texttt{unordered\_set} structure.
|
||||||
This is an easy way to make the algorithm
|
This is an easy way to make the algorithm
|
||||||
more efficient because we only have to change
|
more efficient, because we only have to change
|
||||||
the data structure that the algorithm uses.
|
the underlying data structure.
|
||||||
The time complexity of the new algorithm is $O(n)$.
|
The time complexity of the new algorithm is $O(n)$.
|
||||||
|
|
||||||
\subsubsection{Solution 3}
|
\subsubsection{Solution 3}
|
||||||
|
@ -738,10 +723,9 @@ $5 \cdot 10^6$ & $10{,}0$ s & $2{,}3$ s & $0{,}9$ s \\
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
Solutions 1 and 2 are equal except that
|
Solutions 1 and 2 are equal except that
|
||||||
solution 1 uses the \texttt{set} structure
|
they use different set structures.
|
||||||
and solution 2 uses the \texttt{unordered\_set} structure.
|
In this problem, this choice has an important effect on
|
||||||
In this case, this choice has a big effect on
|
the running time, because solution 2
|
||||||
the running time becase solution 2
|
|
||||||
is 4–5 times faster than solution 1.
|
is 4–5 times faster than solution 1.
|
||||||
|
|
||||||
However, the most efficient solution is solution 3
|
However, the most efficient solution is solution 3
|
||||||
|
@ -750,7 +734,7 @@ It only uses half of the time compared to solution 2.
|
||||||
Interestingly, the time complexity of both
|
Interestingly, the time complexity of both
|
||||||
solution 1 and solution 3 is $O(n \log n)$,
|
solution 1 and solution 3 is $O(n \log n)$,
|
||||||
but despite this, solution 3 is ten times faster.
|
but despite this, solution 3 is ten times faster.
|
||||||
The explanation for this is that
|
This can be explained by the fact that
|
||||||
sorting is a simple procedure and it is done
|
sorting is a simple procedure and it is done
|
||||||
only once at the beginning of solution 3,
|
only once at the beginning of solution 3,
|
||||||
and the rest of the algorithm works in linear time.
|
and the rest of the algorithm works in linear time.
|
||||||
|
|
Loading…
Reference in New Issue