Se tiene un árbol binario de búsqueda enhebrado por la derecha. En el cual los punteros derechos de las hojas apuntan al sucesor. El nodo de mayor valor tiene sucesor nulo. 3 2 1 6 8 4 5 7 9 a) Indicar el número de punteros nulos en un árbol de n nodos. b) Modificar la definición de tipos, agregando un campo de nombre nd, el cual toma valor 1 si el puntero derecho apunta a un hijo, y 0 si apunta a un sucesor. c) Describir las propiedades del nodo sucesor para un nodo que tiene hijo derecho. Cómo se determina, y cuáles son sus características. d) Determinar cuándo un nodo tiene una hebra derecha apuntándolo. e) Diseñar la función iterativa buscar en un árbol enhebrado: pnodo buscar(pnodo t, int valor). f) Analizar la inserción en un árbol enhebrado por la derecha, y diseñar la función iterativa insertar, con prototipo: pnodo Insertar( pnodo *praiz, int valor). A la cual se le pasa por referencia el árbol, y retorna un puntero al insertado, o un puntero nulo si no lo pudo insertar. g) Diseñar rutina iterativa, que imprima el árbol en orden, empleando la información del campo nd. 2. Se tiene datos definidos según: typedef struct { int dia; int mes; struct otros * po; } reg, *preg; Donde la estructura otros puede ser cualquiera. El número máximo de eventos que pueden estar almacenados, durante la ejecución de una aplicación es de 200. a) Diseñar función insertar datos, con prototipo void insertar(preg nuevo); dar un ejemplo para insertar la fecha 28/12. b) Diseñar función para extraer el dato con el mínimo valor de tiempo, según calendario, con prototipo: int extraemin(void); La función imprime en una línea el mes seguido del día; y retorna 1 si pudo extraer el registro; y 0 si se intenta extraer desde un heap vacío. Dar un ejemplo de invocación, extrayendo el contenido completo del heap. 09-11-2007 Solución. 1. a) En un árbol con n elementos, se tienen (n+1) punteros que tienen almacenados valores nulos b) typedef struct tnode { int clave; int nd; //nodo derecho es: Hebra o Hijo struct tnode *left; struct tnode *right; } nodo, * pnodo; c) Si un nodo tiene hijo derecho, su sucesor pertenecerá al subárbol derecho y será un nodo sin hijo izquierdo; es decir el sucesor de ese nodo será el menor descendiente del subárbol derecho. d) Un nodo tiene un hebra derecha apuntándolo si y sólo si tiene hijo izquierdo, es el caso de los nodos con valores 2, 3, 6, y 8 en la Figura 17.1. e) La búsqueda falla si al descender por la izquierda se llega a un puntero nulo, o si se llega a una hebra descendiendo por la derecha. En forma excepcional la búsqueda falla si el árbol está vacío. pnodo buscar(pnodo t, int valor) /* algoritmo iterativo */ { while ( t != NULL) { if ( t->clave == valor ) return (t); else if (t->clave > valor) t = t->left; else if (t->ndes==HIJO) t = t->right; //asimétrico por la derecha else return (NULL); } return (t); /* NULL No lo encontró*/ } f) Como es usual se inserta en una hoja. Si p apunta a la hoja donde debe insertarse el nuevo nodo, y t es el puntero que indica la posición para insertar, pueden ocurrir dos situaciones. a) Inserción en hoja, descendiendo por la izquierda. Se inserta el nuevo nodo y su puntero derecho se marca como hebra, apuntando a p. p->left=t; t->ndes=HEBRA; t->right=p; p p t t Inserción en hoja, descendiendo por la izquierda. El nuevo nodo tendrá como sucesor al apuntado por p. 09-11-2007 b) Inserción en hoja, con hebra derecha. Es preciso marcar como hebra el nuevo nodo, y copiar el valor del puntero derecho de p, en el nuevo nodo, hilvanándole el sucesor. Además debe marcarse que p ahora apunta a un hijo derecho, y enlazar p con el nuevo nodo. t->right=p->right; t->nd=HEBRA; p->right=t; p->nd=HIJO; p p t t Inserción en hoja, descendiendo por la derecha. El nuevo nodo tendrá clave mayor que la del apuntado por p, y menor que la apuntada por la hebra de p; por esta razón el nuevo nodo tiene como sucesor al sucesor de p, antes de la inserción. typedef enum {left, right, vacio} modo; //modos de descenso pnodo Insertar(pnodo *praiz, int valor) { pnodo t= *praiz; //se pasa la raíz por referencia, para modificarla si el árbol está vacío. pnodo p; //mantiene un puntero al padre del que será insertado modo porlado=vacio; //dirección en el descenso. if (t==NULL) //inserta en la raíz { p=getnodo(valor); if (p!=NULL) {*praiz=p; p->nd=HEBRA;} return(p); } while ( t != NULL) { if ( t->clave == valor ) {/*lo encontró, no inserta. No se aceptan claves repetidas en conjuntos*/ return (t); //devuelve el encontrado. } else { p=t ; if (t->clave > valor) {t = t->left; porlado=left;} else { porlado=right; if(t->nd==HIJO) t = t->right; else break; } } } 09-11-2007 /*Al salir del while p apunta al nodo donde se insertará el nuevo, y porlado la dirección */ /* El argumento t apunta a NULL o al sucesor de p */ t = getnodo(valor); //se pega el nuevo nodo en t. if(t==NULL) return(NULL); if (porlado==left) {p->left=t; t->nd=HEBRA; t->right=p;} else if(porlado==right) {t->right=p->right; t->nd=HEBRA; p->right=t; p->nd=HIJO;} return (t); /* Apunta al recién insertado. Null si no se pudo insertar*/ } g) pnodo MasIzquierdista(pnodo t) { if (t == NULL) { return NULL;} while (t->left != NULL) t = t->left; return t; } void EnOrden( pnodo t) { pnodo p = MasIzquierdista(t); while (p != NULL) { printf(" %d ", p->clave); if (p->nd==HEBRA) p = p->right; else p = MasIzquierdista(p->right); } putchar('\n'); } 2. #include <stdio.h> typedef struct { int c1; int c2; } otros; typedef struct { int dia; int mes; struct otros * po; } reg, *preg; #define nmax 200 typedef reg heap[nmax+1]; heap r; int last=0; /*r es arreglo de registros */ #define fecha(i) r[(i)].mes*100 + r[(i)].dia 09-11-2007 #define swap(i,j) temp=r[j], r[j]= r[i], r[i]=temp; void siftup(int n) { int i, j; reg temp; for (j=n; j>1; j=i) { i=(j>>1); if ( fecha(i) <= fecha(j) ) break; swap(i,j); } } void insertar(preg nuevo) { last++; r[last]=*nuevo; siftup(last); } Ejemplo: reg t; preg p=&t; t.dia=28; t.mes=12; t.po=NULL; insertar(p); void siftdown(int n) { int i=1, j; reg temp; while (( j=(i<<1)) <= n ) { if ( (j+1)<=n && fecha(j+1) < fecha(j) ) j++; if (fecha(i) <= fecha(j)) break; swap(i,j); i=j; } } int extraemin(void) { if (last>=1) { printf(" %2d %2d \n", r[1].mes, r[1].dia); r[1]=r[last--]; siftdown(last); return(1); } else return(0); /*error intento extracción en heap vacio*/ } Ejemplo: while( extraemin() ); 09-11-2007