\documentclass[11pt]{article}
\usepackage{amsmath,amssymb,fancybox,fancyhdr,lastpage,a4,mdwlist,multicol}
\usepackage[french]{babel}
\usepackage[utf8x]{inputenc}
\usepackage{tikz}
\usepackage{tikz,tkz-tab,tkz-fct}
\usepackage[algoruled,french,onelanguage,linesnumbered]{algorithm2e}
\usepackage{mdwlist}
\topmargin-0.5cm
\headheight20pt
\textheight23cm
\textwidth17.5cm
\oddsidemargin-1cm
\evensidemargin0cm
\parindent0pt
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{amsfonts,graphicx,fancybox,fancyhdr,lastpage}
\usepackage{boxedminipage}
%TCIDATA{OutputFilter=LATEX.DLL}
%TCIDATA{Version=5.50.0.2953}
%TCIDATA{}
%TCIDATA{BibliographyScheme=Manual}
%TCIDATA{Created=Monday, October 27, 2014 10:00:28}
%TCIDATA{LastRevised=Sunday, February 12, 2017 08:45:02}
%TCIDATA{}
%TCIDATA{}
%TCIDATA{CSTFile=POLY.cst}
\input{tcilatex}
\begin{document}
%\SetTitle{Th\`{e}me info 1 -- Arbres binaires et tri par tas}
%\SetAuthor{}
%\Setdate{}
%\TitlePage{}
%~\vspace{-1.1cm}
\pagestyle{fancy}
\lhead{{\bf MP}}
\chead{}
\rhead{TP : arbres binaires}
\lfoot{\sl Lyc\'ee Chateaubriand}
\cfoot{}
\rfoot{\thepage/\pageref{LastPage}}
\renewcommand{\footrulewidth}{0.4pt}
\section{Structure d'arbre binaire - repr\'{e}sentations par une liste}
%\subsection{Les primitives du type abstrait de donn\'{e}es \textquotedblleft
%arbre binaire\textquotedblright}
%
%Les primitives permettant de cr\'{e}er et de manipuler (souvent r\'{e}%
%cursivement) les arbres binaires sont~:
%
%\begin{itemize}
%\item \texttt{est\_vide(a)}~: renvoie \texttt{Vrai} si l'arbre \texttt{a}
%est vide, \texttt{Faux} sinon~;
%
%\item \texttt{arbre\_vide()}~: renvoie l'arbre vide~;
%
%\item \texttt{cons(e,g,d)}~: renvoie l'arbre d'\'{e}tiquette \texttt{e}, de
%fils gauche (\emph{resp.} droit) \texttt{g} (\emph{resp.} \texttt{d})~;
%
%\item \texttt{contenu(a)}~: renvoie l'\'{e}tiquette de l'arbre \textbf{non
%vide} \texttt{a}~;
%
%\item \texttt{f\_g(a)} (\emph{resp.} \texttt{f\_d(a)})~: renvoie le fils
%gauche (\emph{resp.} droit) de l'arbre \textbf{non vide} \texttt{a}.\vspace{%
%-0.3cm}
%\end{itemize}
\textbf{Question 1~: }\'{e}crire en Python les primitives ci-dessous
dans cette repr\'{e}sentation.\vspace{0.2cm}
\begin{itemize}
\item \texttt{est\_vide(a)}~: renvoie \texttt{Vrai} si l'arbre \texttt{a}
est vide, \texttt{Faux} sinon~;
\item \texttt{arbre\_vide()}~: renvoie l'arbre vide~;
\item \texttt{cons(e,g,d)}~: renvoie l'arbre d'\'{e}tiquette \texttt{e}, de
fils gauche (\emph{resp.} droit) \texttt{g} (\emph{resp.} \texttt{d})~;
\item \texttt{contenu(a)}~: renvoie l'\'{e}tiquette de l'arbre \textbf{non
vide} \texttt{a}~;
\item \texttt{f\_g(a)} (\emph{resp.} \texttt{f\_d(a)})~: renvoie le fils
gauche (\emph{resp.} droit) de l'arbre \textbf{non vide} \texttt{a}.
\end{itemize}
\vspace{.3cm}
\textbf{Question 2~: }programmer (r\'{e}cursivement~!) dans ce contexte le
calcul de la hauteur d'un arbre.\vspace{0.2cm}
\textbf{Question 3~: }l'arbre suivant repr\'{e}sente graphiquement une
expression alg\'{e}brique.\vspace{0.1cm}%
\[
\includegraphics[scale=1]{expr.eps}
\]%
Programmer le \emph{parcours} (r\'{e}cursif~!) d'un tel arbre pour \'{e}%
crire dans une cha\^{\i}ne de caract\`{e}res l'expression alg\'{e}brique
associ\'{e}e. Il sera sage d'ajouter des parenth\`{e}ses. Tester le programme avec l'exemple ci-dessus.
{\bf Remarque}
\begin{itemize}
\item l'op\'{e}ration inverse consistant \`{a} reconstruire l'arbre \`{a}
partir de la cha\^{\i}ne de caract\`{e}res est plus d\'{e}licate.
\end{itemize}
\section{Tri par tas (\emph{heapsort} en anglais)\protect\vspace{-0.3cm}}
Nous avons vu en cours la notion de tas.
Nous allons voir que l'impl\'{e}mentation se fait tr\`{e}s bien avec les
tableaux (et de fa\c{c}on \textbf{it\'{e}rative}\ldots ), mais la vision
arborescente est fondamentale pour imaginer et visualiser les algorithmes. Penser donc à utiliser la fonction {\tt afficher} pour tester les fonctions.
Pour la suite, on suppose donn\'{e} un tableau initial $ti$ contenant les $n$
valeurs \`{a} trier. Pour stocker $n$ valeurs dans un tas, on utilisera un
tableau de taille $n+1$, les cases \textquotedblleft
utiles\textquotedblright\ \'{e}tant index\'{e}es de 1 \`{a} $n$ comme expliqu%
\'{e} en cours.
\subsection{Tri par tas, premi\`{e}re version}
La version la plus naturelle consiste \`{a} utiliser un tableau auxilaire $%
tas$ que l'on remplira avec les donn\'{e}es initiales, avant de le vider.%
\vspace{0.2cm}
\textbf{Question 4}~: \'{e}crire une proc\'{e}dure $entasser$, recevant pour
param\`{e}tres un tas $t$ et une valeur $x$ et ajoutant $x$ dans $t$ en pr%
\'{e}servant la structure de tas. On pourra ajouter $x$ \`{a} la fin du tas
et le faire \textquotedblleft remonter vers la racine\textquotedblright\ par
\'{e}changes successifs $fils\leftrightarrow p\grave{e}re$ autant que n\'{e}%
cessaire (cf. l'id\'{e}e du tri par insertion\ldots ).\vspace{0.2cm}
\textbf{Question 5}~: \'{e}crire une proc\'{e}dure $suppr\_min$, recevant
pour param\`{e}tres un tas $t$ et supprimant de $t$ sa racine en pr\'{e}%
servant la structure de tas. On pourra remplacer la racine par la derni\`{e}%
re valeur de $t$, supprimer cette derni\`{e}re valeur et faire
\textquotedblleft redescendre\textquotedblright\ cette nouvelle racine dans
le tas \`{a} l'aide d'\'{e}changes $p\grave{e}re\leftrightarrow fils$
(attention \`{a} choisir le bon fils lorsqu'il y en a deux\ldots ).\vspace{%
0.2cm}
\textbf{Question 6~}: d\'{e}duire des deux questions pr\'{e}c\'{e}dentes une
fonction $tri\_tas1$, recevant pour param\`{e}tre le tableau initial $ti$ et
le renvoyant tri\'{e}. Seules les valeurs index\'{e}es \`{a} partir de 1
seront tri\'{e}es.
\subsection{Tri par tas en place}
\textbf{Question 7}~: modifier les programmes des questions \textbf{4},
\textbf{5}, \textbf{6} afin d'obtenir une fonction $tri\_tas2$ triant
\textquotedblleft en place\textquotedblright\ le tableau re\c{c}u en param%
\`{e}tre (\emph{i.e.} sans utiliser de tableau auxiliaire). L\`{a} encore,
seules les valeurs index\'{e}es \`{a} partir de 1 seront tri\'{e}es, mais on pourra utiliser {\tt t[0]} pour stocker le nombre d'éléments considérés dans le tas (différent de la taille du tableau qui sera constante cette fois).
\subsection{Optimisation de la cr\'{e}ation du tas}
Si l'on s'y prend bien, la complexit\'{e} de la fonction $entasser$ de la
question \textbf{4} est major\'{e}e par $\log _{2}i$, o\`{u} $i$ est le
nombre de valeurs dans le tas initial. Il en r\'{e}sulte une cr\'{e}ation
progressive du tas contenant les $n$ valeurs avec une complexit\'{e} major%
\'{e}e par $\log _{2}\left( n!\right) $, soit un $O\left( n\ln n\right) $ gr%
\^{a}ce \`{a} la formule de Stirling. En fait cette cr\'{e}ation du tas peut
se faire avec une complexit\'{e} lin\'{e}aire.\vspace{0.2cm}
Pour cela, on part du tableau $t$ contenant les valeurs \`{a} trier
(initialement dans un ordre quelconque) dans les cases index\'{e}es de 1
\`{a} $n$, on pose $d=\left\lfloor n/2\right\rfloor $, indice du n\oe ud le
plus \`{a} droite de l'avant-dernier niveau du tas. L'id\'{e}e est
d'ordonner successivement les sous-arbres dont les racines sont les n\oe uds
d'indices $d,d-1,\ldots ,1$.\vspace{0.2cm}
Pour ce faire on \'{e}crit une fonction $ordonner(t,r)$ qui traite le
sous-arbre dont la racine a pour indice $r$, en supposant que les
sous-arbres de ce dernier ont d\'{e}j\`{a} \'{e}t\'{e} trait\'{e}s (ce qui
justifiera une preuve par r\'{e}currence descendante~!). Le traitement se d%
\'{e}roule ainsi~:
\begin{itemize}
\item on recherche l'indice $f$ du plus petit fils de $t\left[ r\right] $
\item si besoin on \'{e}change $t\left[ r\right] $ et $t\left[ f\right] $ et
dans ce cas, si $t\left[ f\right] $ n'est pas une feuille, on appelle (r\'{e}%
cursivement) $ordonner\left( t,f\right) $ puisque le sous-arbre de racine $t%
\left[ f\right] $ n'est peut-\^{e}tre plus un tas\ldots
\end{itemize}
Voici un exemple \`{a} partir du tableau $t=\left[ 0,14,9,8,4,10,7\right] $ o%
\`{u} l'on appelle successivement $ordonner$ avec $r=3,2,1$ (on a fait
figurer son num\'{e}ro \`{a} gauche de chaque n\oe ud)~:\vspace{-0.1cm}%
\[
\begin{tabular}{ccc}
\includegraphics[scale=1]{ord1.eps} & \includegraphics[scale=1]{ord2.eps} & \includegraphics[scale=1]{ord3.eps} \\
\'{e}tat initial & \'{e}tape 1~: $r=3$ & \'{e}tape 2~: $r=2$\vspace{0.2cm}
\\
\includegraphics[scale=1]{ord4.eps} & \includegraphics[scale=1]{ord5.eps} & \includegraphics[scale=1]{ord6.eps} \\
\'{e}tape 3~: $r=1$ & appel r\'{e}cursif~: $f=2$ & \'{e}tat final%
\end{tabular}%
\]%
%\vspace{0.5cm}
\textbf{Question 8}~: programmer la fonction $ordonner$ et l'utiliser pour
\'{e}crire une fonction $tri\_tas3$.\vspace{0.2cm}
\textbf{Question 9} (facultative)~: montrer que la complexit\'{e} de la cr%
\'{e}ation du tas \`{a} l'aide de la fonction $ordonner$ est un $O\left(
n\right) $.
\section{Tri par arbres binaires de recherche - programmation objet}
Un {\it arbre binaire de recherche}, est un arbre binaire tel que l'étiquette de tout n\oe ud est :
\begin{itemize}
\item supérieure à toutes les étiquettes du sous-arbre gauche ;
\item inférieure à toutes les étiquettes du sous-arbre droit.
\end{itemize}
Dans cette partie, on propose d'implémenter une classe {\tt Abr}.
Les attributs de cette classe sont :
\begin{itemize}
\item {\tt vide} : un booléen indiquant si l'arbre est vide ;
\item {\tt gauche} : {\tt None} ou un {\tt Abr} qui code le sous-arbre gauche ;
\item {\tt droite} : {\tt None} ou un {\tt Abr} qui code le sous-arbre droite ;
\item {\tt e} : un {\tt int} qui code l'étiquette de la racine de l'arbre.
\end{itemize}
\`A sa création, un {\tt Abr} est vide, ses sous-arbres ne sont pas créés et l'étiquette de la racine (inexistante) a une valeur nulle par défaut.
\begin{verbatim}
class Abr:
def __init__(self):
self.vide = True
self.gauche = None
self.droite = None
self.e = 0
\end{verbatim}
La commande {\tt a = Abr()} crée un arbre vide.
Pour accéder aux attributs d'un arbre {\tt a} donné, on écrit {\tt a.nom$\_$attribut}.
Pour définir les {\it méthodes} de la classe {\tt Abr}, on définit des fonctions comme d'habitude, indentées d'un cran pour signifier l'appartenance à la classe. Le premier paramètre dans la définition est toujours {\tt self}, mais il n'est pas utilisé dans les appels dont la syntaxe est {\tt a.methode(arg2,arg3,...)}.
\vspace{.5cm}
\textbf{Question 10}~: écrire une méthode dont l'entête est {\tt def ajoute(self,x):} qui ajoute l'élément {\tt x} dans l'arbre (s'il n'y est pas déjà présent) en maintenant la structure d'arbre binaire de recherche.\vspace{.2cm}
\textbf{Question 11}~: écrire une méthode dont l'entête est {\tt def recherche(self,x):} qui renvoie un booléen indiquant si {\tt x} est une étiquette de l'arbre.\vspace{.2cm}
\textbf{Question 12}~: écrire une méthode dont l'en-tête est {\tt def hauteur(self):} qui renvoie la hauteur de l'arbre (un arbre vide étant de hauteur.\vspace{.2cm}
\textbf{Question 13}~: écrire une méthode dont l'entête est {\tt def affiche(self):} qui va construire un tableau représentant l'arbre puis utiliser la fonction d'affichage définie en cours pour afficher l'arbre. (Ne pas hésiter à définir des méthodes auxiliaires)\vspace{.2cm}
\textbf{Question 14}~: écrire une méthode dont l'en-tête est {\tt def taille(self):} qui renvoie le nombre de n\oe uds dans l'arbre.\vspace{.2cm}
\textbf{Question 15}~: écrire une méthode dont l'en-tête est {\tt def tri(self):} qui renvoie un tableau contenant l'ensemble des étiquettes de l'arbre dans l'ordre croissant (il suffit de faire un parcours infixe).\vspace{.2cm}
\textbf{Question 16}~: Estimer la complexité du tri par arbre binaire de recherche en fonction de la hauteur de l'arbre. Que peut-on en conclure ?\vspace{.2cm}
\textbf{Question 17 (subsidiaire)}~: écrire une méthode dont l'en-tête est {\tt def supprime(self,x):} qui supprime l'élément {\tt x} dans l'arbre (s'il y est présent) en maintenant la structure d'arbre binaire de recherche. Cette question est difficile et nécessite la définition de méthodes auxiliaires.
\end{document}