\documentclass[11pt]{article}
\usepackage{amsmath,amssymb,amsthm,graphicx}
\usepackage{fullpage}
\usepackage[capitalise,nameinlink]{cleveref}
\usepackage{float}
\crefname{lemma}{Lemma}{Lemmas}
\crefname{fact}{Fact}{Facts}
\crefname{theorem}{Theorem}{Theorems}
\crefname{corollary}{Corollary}{Corollaries}
\crefname{claim}{Claim}{Claims}
\crefname{example}{Example}{Examples}
\crefname{problem}{Problem}{Problems}
\crefname{setting}{Setting}{Settings}
\crefname{definition}{Definition}{Definitions}
\crefname{assumption}{Assumption}{Assumptions}
\crefname{subsection}{Subsection}{Subsections}
\crefname{section}{Section}{Sections}
\DeclareMathOperator*{\E}{\mathbb{E}}
\let\Pr\relax
\DeclareMathOperator*{\Pr}{\mathbb{P}}
\newcommand{\eps}{\varepsilon}
\newcommand{\inprod}[1]{\left\langle #1 \right\rangle}
\newcommand{\R}{\mathbb{R}}
\newcommand{\handout}[5]{
\noindent
\begin{center}
\framebox{
\vbox{
\hbox to 5.78in { {\bf CS 270: Combinatorial Algorithms and Data Structures
} \hfill #2 }
\vspace{4mm}
\hbox to 5.78in { {\Large \hfill #5 \hfill} }
\vspace{2mm}
\hbox to 5.78in { {\em #3 \hfill #4} }
}
}
\end{center}
\vspace*{4mm}
}
\newcommand{\lecture}[4]{\handout{#1}{#2}{#3}{Scribe: #4}{Lecture #1}}
\newtheorem{theorem}{Theorem}[section]
\newtheorem*{theorem*}{Theorem}
\newtheorem{itheorem}{Theorem}
\newtheorem{subclaim}{Claim}[theorem]
\newtheorem{proposition}[theorem]{Proposition}
\newtheorem*{proposition*}{Proposition}
\newtheorem{lemma}[theorem]{Lemma}
\newtheorem*{lemma*}{Lemma}
\newtheorem{corollary}[theorem]{Corollary}
\newtheorem*{conjecture*}{Conjecture}
\newtheorem{fact}[theorem]{Fact}
\newtheorem*{fact*}{Fact}
\newtheorem{exercise}[theorem]{Exercise}
\newtheorem*{exercise*}{Exercise}
\newtheorem{hypothesis}[theorem]{Hypothesis}
\newtheorem*{hypothesis*}{Hypothesis}
\newtheorem{conjecture}[theorem]{Conjecture}
\theoremstyle{definition}
\newtheorem{definition}[theorem]{Definition}
\newtheorem{setting}[theorem]{Setting}
\newtheorem{construction}[theorem]{Construction}
\newtheorem{example}[theorem]{Example}
\newtheorem{question}[theorem]{Question}
\newtheorem{openquestion}[theorem]{Open Question}
\newtheorem{algorithm}[theorem]{Algorithm}
\newtheorem{problem}[theorem]{Problem}
\newtheorem{protocol}[theorem]{Protocol}
\newtheorem{assumption}[theorem]{Assumption}
\newtheorem{exercise-easy}[theorem]{Exercise}
\newtheorem{exercise-med}[theorem]{Exercise}
\newtheorem{exercise-hard}[theorem]{Exercise$^\star$}
\newtheorem{claim}[theorem]{Claim}
\newtheorem*{claim*}{Claim}
\newtheorem{remark}[theorem]{Remark}
\newtheorem*{remark*}{Remark}
\newtheorem{observation}[theorem]{Observation}
\newtheorem*{observation*}{Observation}
% 1-inch margins, from fullpage.sty by H.Partl, Version 2, Dec. 15, 1988.
% \topmargin 0pt
% \advance \topmargin by -\headheight
% \advance \topmargin by -\headsep
% \textheight 8.9in
% \oddsidemargin 0pt
% \evensidemargin \oddsidemargin
% \marginparwidth 0.5in
% \textwidth 6.5in
% \parindent 0in
% \parskip 1.5ex
\begin{document}
\lecture{7 --- February 7, 2023}{Spring 2023}{Prof.\ Jelani Nelson}{Chris Liu, Serena Zhang}
\section{Overview}
Next many (5) lectures: Data Structures \\
Today: Heaps (Priority Queues)\\
Store database of key-value pairs subject to:
\begin{enumerate}
\item \emph{insert(k, v)}: adds key-value pair $(k,v)$ to database of items
\item \emph{decKey(*v, k)}: reduces old key of $v$ to new key $k$, do nothing if $k$ is bigger than old key
\item \emph{delMin()}: return item with smallest key, delete it from database
\end{enumerate}
\section{Runtime of Algorithms that use Heaps}
Assume there are $n$ vertices and $m$ edges in the graph.\\
Dijkstra : $n \cdot t_I + n \cdot t_{DM} + m \cdot t_{DK}$\\
Prim : $n \cdot t_I + n \cdot t_{DM} + m \cdot t_{DK}$\\
\begin{center}
\begin{tabular}{ |c|c|c|c| }
\hline
DS Name & $t_I$ & $t_{DM}$ & $t_{DK}$ \\
\hline
Binary & $\log n$ & $\log n$ & $\log n$\\
Binomial & $1^*$ & $\log n$ & $\log n$\\
Fibonacci & $1^*$ & $\log n ^*$ & $1^*$\\
\hline
\end{tabular}
\end{center}
Note: $n$ is the size of the heap. $^*$ means amortized.\\
More Recent:
\begin{itemize}
\item Brodal $'96$ matched Fibonacci bounds in all worst case without amortization. Caveat is it's not a "pointer-machine," meaning it doesnâ€™t store items in a tree/graph.
\item Brodal, Lagogianis, Tarjan STOC $'12$ matched Fibonacci bounds in all worst case and using "pointer-machine."
\end{itemize}
\section{Analysis based on Potential Function}
Recall that potential function $\Phi:\{state\} \rightarrow \mathbb{R}$. We define $\Phi\text{-cost} = \text{true cost} + \Delta\Phi$. We assume that $\Phi \ge 0$ and $\Phi$ of an empty heap is $0$. Total $\Phi\text{-cost} = \text{total cost} + \Phi_{final} - \Phi_{initial} = \text{total cost} + \Phi_{final} \ge \text{total cost}$. We conclude that $\Phi$-cost of an operation is a valid upper bound on amortized cost of that operation.
\section{Binomial Heaps}
\begin{itemize}
\item Forest of trees
\item Each tree is heap-ordered, $key(x) \ge key(parent(x))$
\item Define rank of a tree is degree of root
\item Invariant: at most one tree of each rank
\end{itemize}
Note that largest rank $\le \log n$ in Binomial Heaps. And a rank $k$ tree will have rank $j$ trees hanging off the root for $j = 0, 1, ..., k-1$. A rank $k$ tree has $2^k$ items.
\begin{figure}[H]
\centering
\includegraphics[scale=0.6]{forest.PNG}
\caption{Binomial Heap: forest of trees}
\end{figure}
Consider adding an item to the binary heap shown above. When adding a new node to the heap, we create a rank $0$ tree but there is already a rank $0$ tree so we merge them such that the heap order is preserved. Now there are two rank $1$ trees so we merger again. Repeat the process above and we will eventually merge two rank $3$ trees into one rank $4$ tree. Now if we add a new item, there would be a rank $0$ tree and a rank $4$ tree.
\begin{figure}[H]
\centering
\includegraphics[scale=1]{addtoforest1.PNG}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[scale=1]{addtoforest2.PNG}
\caption{Add an item to Binary Heap}
\end{figure}
We can think of this process as binary addition. We represent the heap as a binary number, each digit represents a rank (rightmost digit represents rank $0$ and so on, for example, $1101$ means there is a rank $0$ tree, a rank $2$ tree, and a rank $3$ tree in the heap). Adding a new node to binary heap is like adding $1$ to the binary number that represents the heap.
\subsection{Implementation}
\begin{itemize}
\item \emph{insert}: add the new node as a rank 0 tree, then keep merging
\item \emph{decKey}: decrement key, if it's smaller than its parent, keep bubbling it up until the heap property is not violated
\item \emph{delMin}: query every root to get the min node, return and delete the min node. Now we have $k$ trees, iteratively merge them.
\end{itemize}
Note that merging $k$ trees can again be thought of as binary addition. Instead of adding $1$ to a binary number, we are now adding an arbitrary binary number of the binary number that represents the heap.
\subsection{Analysis}
We define potential function $\Phi(H) := $ number of trees in $H$, denote as $T(H)$.
\begin{itemize}
\item \emph{insert}: $\underbrace{\text{actual cost}}_{1+(T-t)} + \underbrace{\Delta \Phi}_{t-T} = \mathcal{O}(1)$ \\
$T$ is the number of trees before operation, $t$ is the number of trees after operation. $T-t$ tells us how many "carry bits" there were.
\item \emph{decKey}: $\underbrace{\text{actual cost}}_{\log n} + \underbrace{\Delta \Phi}_{0} = \mathcal{O}(\log n)$ \\
$\mathcal{O}(\log n)$ to bubble up the decremented node, and no trees were created or destroyed.
\item \emph{delMin}: $\underbrace{\text{actual cost}}_{\log n} + \underbrace{\Delta \Phi}_{\log n} = \mathcal{O}(\log n)$ \\
$\mathcal{O}(\log n)$ to scan through all roots since there are at most $\mathcal{O}(\log n)$ different ranks, $\mathcal{O}(\log n)$ to merge the trees.
\end{itemize}
\section{Fibonacci Heaps}
\subsection{Implementation}
\begin{itemize}
\item \emph{insert}: add a new rank 0 tree
\item \emph{decKey}: if decreasing the key of $x$ violates the heap property, cut the edge between $x$ and its parent and make $x$ the root of a new tree
\item \emph{delMin}: scan through roots of all trees and find the min root, return and delete from tree, then consolidate by merging trees with equal ranks
\end{itemize}
The problem with \emph{decKey} is that number of items in rank $k$ trees is too small (not $2^k$). So we fix it by doing the following : the first time a node $x$ loses a child, no problem, but the second time it loses a child, cut $x$ off from its parent
\begin{figure}[H]
\centering
\includegraphics[scale=0.6]{ktree.PNG}
\caption{Rank $k$ Tree with bad $decKey$}
\end{figure}
If we use the \emph{decKey} function defined above, we would keep cutting subtrees and leave the rank $k$ tree with very few nodes. So we construct a better \emph{decKey}:
\begin{itemize}
\item each node maintains a "mark" bit
\item mark $= 1$ if $x$ has lost one child
\item when $x$ loses $2$ children, make $x$ its own tree, then set mark $= 0$
\end{itemize}
\subsection{Minimum Possible Size of a Tree as a Function of its Rank}
\begin{claim}
rank $k$ tree has size $\ge F_{k+2}$ ($F_{k+2}$ is the $(k+2)$th Fibonacci number)
\end{claim}
\begin{proof}
For the rank $0$ and rank $1$ tree, the size is at least $1 = F_{2}$ and $2 = F_{3}$ respectively.
For a tree of rank $k$ where $k > 1$, suppose the children of the root $x$ are $y_0, \cdots y_{k-1}$ (sorted in increasing order by when they became children of $x$). For $i>2$, at the point in time that $i$ became a child of $x$, $x$ had rank at least $i$, so $y_i$ must have as well (we only make a node a parent of another if they are both roots of equal rank). Since then $y_i$ must have lost at most $1$ child, and thus has rank at least $i-1$. Thus if $S_i$ is the minimum size of a subtree whose root has $i$ children, then $S_k \ge 2 + \sum_{i=0}^{k-2} S_i$ (the ``$2+$'' comes from the root, and the child $y_0$). The subtree sizes are minimized when all $\ge$ are equalities, in which we get $S_k - S_{k-1} = S_{k-2}$, or rearranging, $S_k = S_{k-1} + S_{k-2}$, which is the Fibonacci sequence recurrence.
\end{proof}
Note : $F_k = F_{k+1} + F_{k+2} \ge 2F_{k-2} \ge 2^{\frac{k}{2}} = \sqrt{2}^k$, so $F_k$ grows exponentially.
\begin{figure}[H]
\centering
\includegraphics[scale=0.6]{fib.PNG}
\caption{Minimum Possible Size of a Tree}
\end{figure}
Black numbers are ranks, pink numbers are minimum possible number of nodes in the tree. Edges crossed off are the ones that can be cut off without changing the rank of the trees.
\subsection{Analysis}
Define $\Phi(H) := T(H) + 2 \cdot M(H)$, where $M(H)$ is the number of marked items.
\begin{itemize}
\item \emph{insert}: $\mathcal{O}(1) + \underbrace{\Delta T(H)}_1 + 2\underbrace{\Delta M(H)}_0 = \mathcal{O}(1)$
\item \emph{decKey}: Say there are $C$ cascading cuts. $\mathcal{O}(1) + C + \underbrace{\Delta T(H)}_{C} + 2 \underbrace{\Delta M(H)}_{-(C-1)+1} = \mathcal{O}(1)$ \\
Since when after $C$ cuts, $C$ new trees were created, $C-1$ nodes got unmarked, node at the "top" got marked.
\item \emph{delMin}: $T(H) + \underbrace{\Delta T(H)}_{\mathcal{O}(\log n)-T(H)} + 2\underbrace{\Delta M(H)}_0 = \mathcal{O}(\log n)$ \\
Actual cost is $T(H)$ to scan through all roots. Max number of new trees you can create is $\log n$ since the max rank is $\log n$. Change in the number of marked nodes is zero because \emph{delMin} doesn't mark or unmark any nodes.
\end{itemize}
\bibliographystyle{alpha}
\begin{thebibliography}{42}
% \bibitem{BNW22}
% Aaron Bernstein, Danupon Nanongkai, and Christian Wulff-Nilsen.
% \newblock Negative-weight
% single-source shortest paths in near-linear time.
% \newblock {\em Proceedings of the 63rd Annual IEEE
% Symposium on Foundations of Computer Science (FOCS)}, 600--611, 2022.
% \bibitem{FF56}
% Lester R. Ford and Delbert R. Fulkerson.
% \newblock Maximal flow through a network..
% \newblock {\em . Canadian Journal
% of Mathematics}, 8(3):399404, 1956
\bibitem{tango}
Daniel D. Sleator and Robert E. Tarjan.
\newblock Self-adjusting binary search trees.
\newblock {\em Journal of the ACM}, 32(3):652--686, 1985.
\end{thebibliography}
\end{document}
\end{document}