Download Certamen 2 Estructuras de Datos y Algoritmos. Primer Semestre

Document related concepts

Heap Binomial wikipedia , lookup

Montículo de Fibonacci wikipedia , lookup

Montículo (informática) wikipedia , lookup

Heapsort wikipedia , lookup

Árbol biselado wikipedia , lookup

Transcript
Certamen 2
Estructuras de Datos y Algoritmos.
Primer Semestre 2007
1. En un árbol binario, agregar dos enteros a la estructura del nodo, uno denominado cb, en
que se cuente todas las veces que se recorre ese nodo en una búsqueda; otro denominado
be, en que se cuente todas las veces que se recorre ese nodo en una búsqueda exitosa.
Modificar la rutina de búsqueda para mantener los contadores: cb y be.
Solución.
typedef struct moldenode
{
int clave;
int cb;
int be;
struct moldenode *left;
struct moldenode *right;
} nodo, * pnodo;
pnodo BuscarRecursivo( pnodo t, int valor )
{ pnodo p;
if ( t == NULL) return (NULL); /* árbol vacío o hijo de hoja */
else {
(t->cb)++;
if ( t->clave == valor ) {(t->be)++; return(t);} /* lo encontró */
else {
if ( t->clave > valor ) p=BuscarRecursivo(t->left, valor);
else p=BuscarRecursivo(t->right, valor);
if (p!=NULL) (t->be)++;
}
}
return ( p );
}
En el argumento t, se va almacenando en el stack, la ruta de los nodos recorridos desde la
raíz. En la local p, se va retornando el puntero al nodo encontrado exitosamente; o valor
NULL si la búsqueda no fue exitosa.
2. En un árbol AVL, llegan las claves, en el siguiente orden: 50, 70, 80, 75, 77, 76, 40
a) Dibujar el árbol, a medida que se va formando, indicando el tipo de rotaciones que deben
efectuarse para mantenerlo AVL.
b) Descartar los nodos con valores de claves: 75, 76, 70, 40 y dibujar el árbol, indicando el
tipo de rotaciones que deben efectuarse para mantenerlo AVL.
Solución.
a) Después de ingresar el nodo con valor 80, se produce desbalance, el cual se corrige con
una rotación simple a la izquierda (el 50 con el 70), resultando:
t
70
80
50
Después de ingresado el nodo con valor 77, se produce desbalance, el que se corrige con
una rotación doble, primero a la izquierda (el 77 con el 75) y luego a la derecha (el 77 con
el 80), resultando:
t
70
77
50
75
80
Al insertar el 76, se produce desbalance, se corrige con doble rotación, primero a la derecha
(el 75 con 77) y luego a la izquierda (el 70 con el 75), resultando:
t
75
77
70
50
76
80
Finalmente, al insertar el nodo con valor 40, se produce desbalance, que se corrige con una
rotación simple a la derecha (el 50 con el 70), resultando:
t
75
77
50
40
70
76
80
b) Al descartar el nodo con valor 75, se tienen dos soluciones, una de ellas es llevar el
mayor descendiente del subárbol izquierdo a la raíz, se obtiene:
t
70
77
50
40
76
80
El descarte de la hoja con valor 76, no requiere correcciones, resultando:
t
50
40
70
77
80
Descartar el nodo con valor 70, tiene dos soluciones, escogiendo nuevamente el mayor
descendiente del subárbol izquierdo, se obtiene:
t
50
77
40
80
El descarte de la hoja con valor 40, implica desbalance, el cual se corrige con rotación
simple a la izquierda, resultando finalmente:
t
77
50
80
3. Se tiene un árbol binario de búsqueda, en el cual las claves llegaron en el orden: 50, 40,
70, 65, 20, 60, 22. Diseñar una función que recorra el árbol en orden y vaya insertando las
claves en un heap. Los argumentos de la función son un puntero al nodo del árbol y un
puntero a una componente del arreglo. Dibujar un esquema del árbol y del heap. Puede
reutilizar funciones vistas en clases.
Solución.
Se modifica la función RecorraEnOrden, para insertar en un heap, en lugar de imprimir la
clave del nodo.
void RecorraEnOrden(pnodo p, heap r)
{
if (p!= NULL) //si llegó a las hojas o es un árbol vacío.
{
RecorraEnOrden(p->left);
//recorre el árbol en orden.
insert ( p->clave, r);
// inserta la clave en el heap r.
RecorraEnOrden(p->right);
}
}
Como deseamos insertar claves enteras, puede definirse un heap de enteros, de Nmax
posiciones:
typedef int heap[Nmax+1];
heap r;
int last=0;
/* r es arreglo de registros */
/* Apunta al último
*/
Se modifican las funciones, para tratar el heap de enteros:
//inserta en posición n-ésima y lo hace ascender
void siftup(int n, heap r)
{ int i, j;
int temp;
for (j=n; j>1; j=i){
/*Al inicio j apunta al último ( n ) */
i=(j>>1) ;
/* i= j/2
i es el cursor del padre de j*/
if ( r[i] <= r[j] ) break;
// Sale del for si es un heap.
/* invariante: heap(1, n) excepto entre j y su padre i */
temp=r[j], r[j]= r[i], r[i]=temp; /* intercambio sólo si el hijo es mayor que el padre*/
/* La condición de reinicio hace que j apunte a su padre. j=i; */
}
}
void insert(int nuevo, heap r)
{
last++;
r[last]=nuevo;
/* Antes se tenía un heap(1, last-1) */
siftup(last, r);
/* Luego de sift-up se tiene un heap(1, last) */
}
Luego de insertadas las claves, se tiene el siguiente árbol binario de búsqueda:
El recorrido en orden, resulta: 20 22 40 50 60 65 70.
El arreglo visualizado como heap:
20
22
50
40
60
65
70
4. Para el segmento que realiza la partición en el algoritmo quicksort, visto en clases:
a) Efectuar traza de los valores del arreglo y de las variables i y j, luego de los dos while, y
del intercambio de claves, a medida que se desarrolla el algoritmo, para los siguientes
valores de claves:
1 2 1 2 6 2 5
b) Modificar el segmento para que no intercambie una clave consigo misma, ni para que
intercambie claves iguales.
Solución.
Traza con valores repetidos del pivote en la partición.
Veamos un ejemplo con algunos valores repetidos del pivote en la partición.
1 2 1 2 6 2 5
i
j
piv
2
Figura 1. Pivote con repeticiones.
Después de los dos primeros while, queda:
1 2 1 2 6 2 5
i
piv
j
2
Figura 2. Luego de los dos while.
Y después del if, ha realizado un swap innecesario, quedando:
1 2 1 2 6 2 5
i
piv
j
2
Figura 3. Después del if.
Vuelve a iterar, después de los dos while, resulta:
1 2 1 2 6 2 5
i j
piv
2
Figura 4. Segunda iteración.
Con lo cual vuelve a intercambiar consigo mismo, para finalmente dejar:
1 2 1 2 6 2 5
j
piv
i
2
Figura 5 Al terminar la iteración.
De este modo se acortan los subarreglos.
Luego del análisis de estos casos, puede refinarse el segmento que realiza la partición. No
realiza el intercambio si: i es igual a j o si las claves son iguales.
do {
while ( a[i].clave < piv.clave) i++; //encuentra uno mayor o igual que el valor del pivote
while( piv.clave < a[j].clave) j--; //al salir apunta a uno menor o igual que el pivote
if (i<=j)
{ if ( (i!=j) && (a[i].clave!=a[j].clave) ) swap(i, j) ;
i++; j--;
}
} while( i<=j );