JAVA3elatestCapitulo1 en Es

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 20

Traducido del inglés al español - www.onlinedoctranslator.

com

Estructuras de datos y algoritmos

¿Cuántas ciudades con más de 250.000 habitantes se encuentran a 500 millas de Dallas, Texas?
¿Cuántas personas en mi empresa ganan más de 100.000 dólares al año? ¿Podemos conectar a
todos nuestros clientes telefónicos con menos de 1,000 millas de cable? Para responder a
preguntas como éstas no basta con tener la información necesaria. Debemos organizar esa
información de una manera que nos permita encontrar las respuestas a tiempo para satisfacer
nuestras necesidades.
Representar información es fundamental para la informática. El objetivo principal de
la mayoría de los programas informáticos no es realizar cálculos, sino almacenar y
recuperar información, normalmente lo más rápido posible. Por esta razón, el estudio de
las estructuras de datos y los algoritmos que las manipulan está en el corazón de la
informática. Y de eso se trata este libro: ayudarle a comprender cómo estructurar la
información para respaldar un procesamiento eficiente.
Este libro tiene tres objetivos principales. El primero es presentar las estructuras de datos comúnmente

utilizadas. Estos forman el “kit de herramientas” de estructura de datos básica de un programador. Para muchos

problemas, alguna estructura de datos del kit de herramientas proporciona una buena solución.

El segundo objetivo es introducir la idea de las compensaciones y reforzar el concepto de


que existen costos y beneficios asociados con cada estructura de datos. Esto se hace
describiendo, para cada estructura de datos, la cantidad de espacio y tiempo necesarios para
operaciones típicas.
El tercer objetivo es enseñar cómo medir la efectividad de una estructura de datos o algoritmo.
Sólo a través de dicha medición se puede determinar qué estructura de datos de su conjunto de
herramientas es la más apropiada para un nuevo problema. Las técnicas presentadas también le
permiten juzgar los méritos de nuevas estructuras de datos que usted u otros puedan inventar.

A menudo existen muchos enfoques para resolver un problema. ¿Cómo elegimos


entre ellos? En el centro del diseño de programas informáticos hay dos objetivos (a veces
contradictorios):

1.Diseñar un algoritmo que sea fácil de entender, codificar y depurar.


2.Diseñar un algoritmo que haga un uso eficiente de los recursos del ordenador.

3
4 Cap. 1 Estructuras de datos y algoritmos

Idealmente, el programa resultante es fiel a ambos objetivos. Podríamos decir que un programa
así es “elegante”. Si bien los algoritmos y ejemplos de código de programa presentados aquí intentan
ser elegantes en este sentido, no es el propósito de este libro tratar explícitamente cuestiones
relacionadas con el objetivo (1). Estas son principalmente preocupaciones de la disciplina de Ingeniería
de Software. Más bien, este libro trata principalmente de cuestiones relacionadas con el objetivo (2).

¿Cómo medimos la eficiencia? El Capítulo 3 describe un método para evaluar la


eficiencia de un algoritmo o programa de computadora, llamadoanálisis asintótico. El
análisis asintótico también permite medir la dificultad inherente de un problema. Los
capítulos restantes utilizan técnicas de análisis asintótico para estimar el costo de tiempo
de cada algoritmo presentado. Esto le permite ver cómo se compara cada algoritmo con
otros algoritmos para resolver el mismo problema en términos de eficiencia.
Este primer capítulo sienta las bases para lo que sigue, al presentar algunas cuestiones de orden
superior relacionadas con la selección y el uso de estructuras de datos. Primero examinamos el
proceso mediante el cual un diseñador selecciona una estructura de datos apropiada para la tarea en
cuestión. Luego consideramos el papel de la abstracción en el diseño de programas. Consideramos
brevemente el concepto de patrón de diseño y vemos algunos ejemplos. El capítulo termina con una
exploración de la relación entre problemas, algoritmos y programas.

1.1 Una filosofía de las estructuras de datos

1.1.1 La necesidad de estructuras de datos

Se podría pensar que con computadoras cada vez más potentes, la eficiencia de los programas es
cada vez menos importante. Después de todo, la velocidad del procesador y el tamaño de la memoria
siguen mejorando. ¿No se resolverá cualquier problema de eficiencia que podamos tener hoy con el
hardware del mañana?

A medida que desarrollamos computadoras más potentes, nuestra historia hasta ahora siempre
ha sido la de utilizar esa potencia informática adicional para abordar problemas más complejos, ya sea
en forma de interfaces de usuario más sofisticadas, problemas de mayor tamaño o problemas nuevos
que antes se consideraban computacionalmente inviables. Los problemas más complejos exigen más
cálculos, lo que hace aún mayor la necesidad de programas eficientes. Peor aún, a medida que las
tareas se vuelven más complejas, se vuelven menos parecidas a nuestra experiencia cotidiana. Los
científicos informáticos de hoy deben estar capacitados para tener una comprensión profunda de los
principios detrás del diseño eficiente de programas, porque sus experiencias de vida ordinarias a
menudo no se aplican al diseñar programas informáticos.

En el sentido más general, una estructura de datos es cualquier representación de datos y sus
operaciones asociadas. Incluso un número entero o de punto flotante almacenado en la computadora puede
verse como una estructura de datos simple. Más comúnmente, la gente usa el término "estructura de datos"
para referirse a una organización o estructuración de una colección de elementos de datos. Una lista
ordenada de números enteros almacenados en una matriz es un ejemplo de tal estructuración.
Segundo. 1.1 Una filosofía de las estructuras de datos 5

Dado suficiente espacio para almacenar una colección de elementos de datos, siempre es posible buscar
elementos específicos dentro de la colección, imprimir o procesar de otro modo los elementos de datos en
cualquier orden deseado, o modificar el valor de cualquier elemento de datos en particular. Por tanto, es
posible realizar todas las operaciones necesarias en cualquier estructura de datos. Sin embargo, utilizar la
estructura de datos adecuada puede marcar la diferencia entre un programa que se ejecuta en unos pocos
segundos y uno que requiere muchos días.
Se dice que una solución eseficientesi resuelve el problema dentro del plazo requerido
limitaciones de recursos.Ejemplos de limitaciones de recursos incluyen el espacio total
disponible para almacenar los datos (posiblemente dividido en limitaciones separadas de
memoria principal y espacio en disco) y el tiempo permitido para realizar cada subtarea. A veces
se dice que una solución es eficiente si requiere menos recursos que las alternativas conocidas,
independientemente de si cumple con algún requisito particular. Elcostode una solución es la
cantidad de recursos que consume la solución. Muy a menudo, el costo se mide en términos de
un recurso clave, como el tiempo, con el supuesto implícito de que la solución cumple con las
otras limitaciones de recursos.
No hace falta decir que la gente escribe programas para resolver problemas. Sin embargo, es
fundamental tener en cuenta esta perogrullada al seleccionar una estructura de datos para resolver
un problema en particular. Sólo analizando primero el problema para determinar los objetivos de
desempeño que deben alcanzarse puede haber alguna esperanza de seleccionar la estructura de
datos adecuada para el trabajo. Los diseñadores de programas deficientes ignoran este paso del
análisis y aplican una estructura de datos con la que están familiarizados pero que es inapropiada
para el problema. El resultado suele ser un programa lento. Por el contrario, no tiene sentido adoptar
una representación compleja para "mejorar" un programa que puede cumplir sus objetivos de
desempeño cuando se implementa utilizando un diseño más simple.
Al seleccionar una estructura de datos para resolver un problema, debes seguir estos
pasos.

1.Analice su problema para determinar las operaciones básicas que deben ser compatibles.
Ejemplos de operaciones básicas incluyen insertar un elemento de datos en la estructura de
datos, eliminar un elemento de datos de la estructura de datos y encontrar un elemento de
datos específico.
2.Cuantificar las limitaciones de recursos para cada operación.
3.Seleccione la estructura de datos que mejor cumpla con estos requisitos.

Este enfoque de tres pasos para seleccionar una estructura de datos pone en práctica una visión
centrada en los datos del proceso de diseño. La primera preocupación es por los datos y las
operaciones que se realizarán con ellos, la siguiente preocupación es la representación de esos datos
y la última preocupación es la implementación de esa representación.
Las limitaciones de recursos en ciertas operaciones clave, como la búsqueda, la inserción y
eliminación de registros de datos, normalmente impulsan el proceso de selección de la
estructura de datos. Muchas cuestiones relacionadas con la importancia relativa de estas
operaciones se abordan mediante las siguientes tres preguntas, que usted debe hacerse
siempre que deba elegir una estructura de datos:
6 Cap. 1 Estructuras de datos y algoritmos

• ¿Se insertan todos los elementos de datos en la estructura de datos al principio o las
inserciones se intercalan con otras operaciones? Las aplicaciones estáticas (donde los
datos se cargan al principio y nunca cambian) normalmente requieren sólo estructuras
de datos más simples para lograr una implementación eficiente que las aplicaciones
dinámicas.
• ¿Se pueden eliminar elementos de datos? Si es así, esto probablemente complicará la
implementación.
• ¿Se procesan todos los elementos de datos en algún orden bien definido o se permite la búsqueda de
elementos de datos específicos? La búsqueda de “acceso aleatorio” generalmente requiere
estructuras de datos más complejas.

1.1.2 Costos y Beneficios


Cada estructura de datos tiene costos y beneficios asociados. En la práctica, casi nunca es cierto
que una estructura de datos sea mejor que otra para su uso en todas las situaciones. Si una
estructura de datos o algoritmo es superior a otro en todos los aspectos, el inferior
normalmente habrá sido olvidado hace mucho tiempo. Para casi todas las estructuras de datos
y algoritmos presentados en este libro, verá ejemplos de dónde es la mejor opción. Algunos de
los ejemplos pueden sorprenderle.
Una estructura de datos requiere una cierta cantidad de espacio para cada elemento de datos
que almacena, una cierta cantidad de tiempo para realizar una única operación básica y una cierta
cantidad de esfuerzo de programación. Cada problema tiene limitaciones de espacio y tiempo
disponibles. Cada solución a un problema hace uso de las operaciones básicas en alguna proporción
relativa, y el proceso de selección de la estructura de datos debe tener en cuenta esto. Sólo después
de un análisis cuidadoso de las características de su problema podrá determinar la mejor estructura
de datos para la tarea.

Ejemplo 1.1Un banco debe admitir muchos tipos de transacciones con sus
clientes, pero examinaremos un modelo simple en el que los clientes desean abrir
cuentas, cerrar cuentas y agregar dinero o retirar dinero de las cuentas. Podemos
considerar este problema en dos niveles distintos: (1) los requisitos para la
infraestructura física y el proceso de flujo de trabajo que el banco utiliza en sus
interacciones con sus clientes, y (2) los requisitos para el sistema de base de datos
que administra las cuentas.
El cliente típico abre y cierra cuentas con mucha menos frecuencia de la que
accede a ellas. Los clientes están dispuestos a esperar muchos minutos mientras se
crean o eliminan cuentas, pero normalmente no están dispuestos a esperar más que
un breve tiempo para transacciones de cuentas individuales, como un depósito o un
retiro. Estas observaciones pueden considerarse como especificaciones informales
para las limitaciones de tiempo del problema.
Es una práctica común que los bancos proporcionen dos niveles de servicio.
Cajeros humanos o cajeros automáticos (ATM) respaldan el acceso de los clientes
Segundo. 1.1 Una filosofía de las estructuras de datos 7

a saldos de cuentas y actualizaciones como depósitos y retiros. Por lo general, se


proporcionan representantes de servicios especiales (durante horarios restringidos) para
manejar la apertura y el cierre de cuentas. Se espera que las transacciones en cajeros
automáticos y cajeros automáticos tomen poco tiempo. Abrir o cerrar una cuenta puede
llevar mucho más tiempo (quizás hasta una hora desde la perspectiva del cliente).
Desde la perspectiva de la base de datos, vemos que las transacciones en cajeros
automáticos no modifican la base de datos significativamente. Para simplificar, supongamos
que si se agrega o elimina dinero, esta transacción simplemente cambia el valor almacenado
en un registro de cuenta. Agregar una nueva cuenta a la base de datos puede tardar varios
minutos. Eliminar una cuenta no tiene por qué tener ninguna limitación de tiempo, porque
desde el punto de vista del cliente lo único que importa es que se le devuelva todo el dinero
(equivalente a un retiro). Desde el punto de vista del banco, el registro de la cuenta podría
eliminarse del sistema de base de datos después del horario laboral o al final del ciclo
mensual de la cuenta.
Al considerar la elección de la estructura de datos a utilizar en el sistema de base de datos que
administra las cuentas de los clientes, vemos que una estructura de datos que tiene poca
preocupación por el costo de eliminación, pero que es altamente eficiente para la búsqueda y
moderadamente eficiente para la inserción, debería cumplir con el recurso. restricciones impuestas
por este problema. Se puede acceder a los registros mediante un número de cuenta único (a veces
llamadoconsulta de coincidencia exacta).Una estructura de datos que cumple con estos requisitos
es la tabla hash descrita en el Capítulo 9.4. Las tablas hash permiten una búsqueda de
coincidencias exactas extremadamente rápida. Un registro se puede modificar rápidamente
cuando la modificación no afecta sus requisitos de espacio. Las tablas hash también admiten la
inserción eficiente de nuevos registros. Si bien las eliminaciones también se pueden admitir de
manera eficiente, demasiadas eliminaciones provocan cierta degradación en el rendimiento de las
operaciones restantes. Sin embargo, la tabla hash se puede reorganizar periódicamente para
restaurar el sistema a su máxima eficiencia. Esta reorganización puede realizarse fuera de línea
para no afectar las transacciones en cajeros automáticos.

Ejemplo 1.2Una empresa está desarrollando un sistema de base de datos que contiene
información sobre ciudades y pueblos de Estados Unidos. Hay miles de ciudades y pueblos, y
el programa de base de datos debería permitir a los usuarios encontrar información sobre
un lugar en particular por su nombre (otro ejemplo de consulta de coincidencia exacta). Los
usuarios también deberían poder encontrar todos los lugares que coincidan con un valor
particular o rango de valores para atributos como la ubicación o el tamaño de la población.
Esto se conoce como unconsulta de rango.
Un sistema de base de datos razonable debe responder consultas con la suficiente rapidez para

satisfacer la paciencia de un usuario típico. Para una consulta de coincidencia exacta, unos segundos son

satisfactorios. Si la base de datos está destinada a admitir consultas de rango que pueden devolver

muchas ciudades que coinciden con la especificación de la consulta, toda la operación


8 Cap. 1 Estructuras de datos y algoritmos

Es posible que se permita que la operación tarde más tiempo, tal vez del orden de un minuto. Para
cumplir con este requisito, será necesario respaldar operaciones que procesen consultas de rango
de manera eficiente procesando todas las ciudades del rango como un lote, en lugar de como una
serie de operaciones en ciudades individuales.

La tabla hash sugerida en el ejemplo anterior no es apropiada para implementar


nuestra base de datos de la ciudad porque no puede realizar consultas de rango
eficientes. El b+-El árbol de la Sección 10.5.1 admite bases de datos grandes, inserción y
eliminación de registros de datos y consultas de rango. Sin embargo, un índice lineal
simple como el descrito en la Sección 10.1 sería más apropiado si la base de datos se
crea una vez y luego nunca se modifica, como un atlas distribuido en un CD o al que se
accede desde un sitio web.

1.2 Tipos de datos abstractos y estructuras de datos


La sección anterior utilizó los términos “elemento de datos” y “estructura de datos” sin definirlos
adecuadamente. Esta sección presenta la terminología y motiva el proceso de diseño
incorporado en el enfoque de tres pasos para seleccionar una estructura de datos. Esta
motivación surge de la necesidad de gestionar la tremenda complejidad de los programas
informáticos.
Atipoes una colección de valores. Por ejemplo, el tipo booleano consta de los valores
verdaderoyFALSO. Los números enteros también forman un tipo. Un número entero es untipo
simpleporque sus valores no contienen subpartes. Un registro de cuenta bancaria normalmente
contendrá varios datos, como nombre, dirección, número de cuenta y saldo de cuenta. Un
registro de este tipo es un ejemplo detipo agregadootipo compuesto.Aelemento de datoses una
información o un registro cuyo valor se extrae de un tipo. Se dice que un elemento de datos es
unmiembrode un tipo.
Atipo de datoses un tipo junto con una colección de operaciones para manipular el
tipo. Por ejemplo, una variable entera es miembro del tipo de datos entero. La suma es un
ejemplo de una operación en el tipo de datos entero.
Debe hacerse una distinción entre el concepto lógico de un tipo de datos y su implementación
física en un programa de computadora. Por ejemplo, existen dos implementaciones tradicionales para
el tipo de datos de lista: la lista vinculada y la lista basada en matrices. Por lo tanto, el tipo de datos de
lista se puede implementar utilizando una lista vinculada o una matriz. Incluso el término "matriz" es
ambiguo en el sentido de que puede referirse a un tipo de datos o a una implementación. "Matriz" se
usa comúnmente en programación de computadoras para referirse a un bloque contiguo de
ubicaciones de memoria, donde cada ubicación de memoria almacena un elemento de datos de
longitud fija. Según este significado, una matriz es una estructura de datos física. Sin embargo, una
matriz también puede significar un tipo de datos lógico compuesto por una colección (normalmente
homogénea) de elementos de datos, con cada elemento de datos identificado por un número de
índice. Es posible implementar matrices de muchas maneras diferentes. Para examen-
Segundo. 1.2 Tipos de datos abstractos y estructuras de datos 9

Por ejemplo, la Sección 12.2 describe la estructura de datos utilizada para implementar una matriz
dispersa, una gran matriz bidimensional que almacena sólo relativamente pocos valores distintos de
cero. Esta implementación es bastante diferente de la representación física de una matriz como
ubicaciones de memoria contiguas.
Untipo de datos abstractos (ADT) es la realización de un tipo de datos como
componente de software. La interfaz del ADT se define en términos de un tipo y un
conjunto de operaciones sobre ese tipo. El comportamiento de cada operación está
determinado por sus entradas y salidas. Un ADT no especificacómose implementa el tipo
de datos. Estos detalles de implementación están ocultos para el usuario del ADT y
protegidos del acceso externo, un concepto conocido comoencapsulación.
Aestructura de datoses la implementación de un ADT. En un lenguaje orientado a
objetos como Java, un ADT y su implementación juntos forman unclase. Cada operación
asociada con el ADT es implementada por unfunción miembroo método.Las variables que
definen el espacio requerido por un elemento de datos se denominanmiembros de datos.
Unobjetoes una instancia de una clase, es decir, algo que se crea y ocupa almacenamiento
durante la ejecución de un programa de computadora.
El término "estructura de datos" a menudo se refiere a los datos almacenados en la memoria principal de una

computadora. El término relacionado fila estructuraA menudo se refiere a la organización de datos en un

almacenamiento periférico, como una unidad de disco o un CD.

Ejemplo 1.3El concepto matemático de número entero, junto con las operaciones que
manipulan números enteros, forman un tipo de datos. la javaEn tEl tipo de variable es
una representación física del entero abstracto. ElEn ttipo de variable, junto con las
operaciones que actúan sobre unaEn tvariable, forma un TDA. Desafortunadamente, el
En tLa implementación no es completamente fiel al entero abstracto, ya que existen
limitaciones en el rango de valores.En tvariable puede almacenar. Si estas limitaciones
resultan inaceptables, entonces se debe idear alguna otra representación para el
“entero” ADT y se debe utilizar una nueva implementación para las operaciones
asociadas.

Ejemplo 1.4Un ADT para una lista de números enteros podría especificar las siguientes
operaciones:

• Insertar un nuevo número entero en una posición particular de la lista.

• Devolververdaderosi la lista está vacía.


• Reinicializar la lista.
• Devuelve el número de números enteros actualmente en la lista.

• Eliminar el número entero en una posición particular de la lista.

A partir de esta descripción, la entrada y salida de cada operación deben quedar


claras, pero no se ha especificado la implementación para las listas.
10 Cap. 1 Estructuras de datos y algoritmos

Una aplicación que utiliza algún ADT podría utilizar funciones miembro particulares de
ese ADT más que una segunda aplicación, o las dos aplicaciones podrían tener diferentes
requisitos de tiempo para las distintas operaciones. Estas diferencias en los requisitos de
las aplicaciones son la razón por la que un determinado ADT puede ser compatible con
más de una implementación.

Ejemplo 1.5Dos implementaciones populares para aplicaciones de bases de datos basadas


en discos grandes son el hash (Sección 9.4) y el B+-árbol (Sección 10.5). Ambos admiten la
inserción y eliminación eficiente de registros y ambos admiten consultas de coincidencia
exacta. Sin embargo, el hash es más eficiente que el B+-árbol para consultas de coincidencia
exacta. Por otro lado, la B+-tree puede realizar consultas de rango de manera eficiente,
mientras que el hash es irremediablemente ineficiente para consultas de rango. Por lo tanto,
si la aplicación de base de datos limita las búsquedas a consultas de coincidencia exacta, se
prefiere el hash. Por otro lado, si la aplicación requiere soporte para consultas de rango, el B
+-Se prefiere el árbol. A pesar de estos problemas de rendimiento, ambas implementaciones
resuelven versiones del mismo problema: actualizar y buscar en una gran colección de
registros.

El concepto de ADT puede ayudarnos a centrarnos en cuestiones clave incluso en aplicaciones no


informáticas.

Ejemplo 1.6Al operar un automóvil, las actividades principales son dirigir, acelerar y frenar.
En casi todos los turismos, se conduce girando el volante, se acelera pisando el pedal del
acelerador y se frena pisando el pedal del freno. Este diseño para automóviles puede verse
como un ADT con operaciones de “dirección”, “aceleración” y “freno”. Dos automóviles
podrían implementar estas operaciones de maneras radicalmente diferentes, digamos con
diferentes tipos de motor, o con tracción delantera versus tracción trasera. Sin embargo, la
mayoría de los conductores pueden operar muchos automóviles diferentes porque el ADT
presenta un método de operación uniforme que no requiere que el conductor comprenda
los detalles de ningún motor o diseño de transmisión en particular. Estas diferencias se
ocultan deliberadamente.

El concepto de ADT es un ejemplo de un principio importante que debe comprender


cualquier científico informático exitoso: gestionar la complejidad a través de la abstracción. Un
tema central de la informática es la complejidad y las técnicas para manejarla. Los humanos
lidian con la complejidad asignando una etiqueta a un conjunto de objetos o conceptos y luego
manipulando la etiqueta en lugar del conjunto. Los psicólogos cognitivos llaman a esta etiqueta
metáfora.Una etiqueta particular puede estar relacionada con otros datos u otras etiquetas. A
esta colección, a su vez, se le puede dar una etiqueta, formando una jerarquía de conceptos y
etiquetas. Esta jerarquía de etiquetas nos permite centrarnos en cuestiones importantes
ignorando detalles innecesarios.
Segundo. 1.2 Tipos de datos abstractos y estructuras de datos 11

Ejemplo 1.7Aplicamos la etiqueta "disco duro" a un conjunto de hardware que


manipula datos en un tipo particular de dispositivo de almacenamiento, y aplicamos la
etiqueta "CPU" al hardware que controla la ejecución de instrucciones de la
computadora. Estas y otras etiquetas se agrupan bajo la etiqueta "computadora".
Debido a que hoy en día incluso las computadoras domésticas más pequeñas tienen
millones de componentes, es necesaria alguna forma de abstracción para comprender
cómo funciona una computadora.

Considere cómo podría abordar el proceso de diseño de un programa informático complejo


que implemente y manipule un ADT. El ADT se implementa en una parte del programa
mediante una estructura de datos particular. Al diseñar aquellas partes del programa que
utilizan el ADT, puede pensar en términos de operaciones en el tipo de datos sin preocuparse
por la implementación de la estructura de datos. Sin esta capacidad de simplificar su
pensamiento acerca de un programa complejo, no tendría ninguna esperanza de comprenderlo
o implementarlo.

Ejemplo 1.8Considere el diseño de un sistema de base de datos relativamente simple almacenado


en disco. Normalmente, se accede a los registros en disco en un programa de este tipo a través de
un grupo de buffer (consulte la Sección 8.3) en lugar de hacerlo directamente. Los registros de
longitud variable pueden utilizar un administrador de memoria (consulte la Sección 12.3) para
encontrar una ubicación adecuada dentro del archivo del disco para colocar el registro.
Normalmente se utilizarán múltiples estructuras de índice (consulte el Capítulo 10) para acceder a
los registros de diversas maneras. Así, tenemos una cadena de clases, cada una con sus propias
responsabilidades y privilegios de acceso. Una consulta de base de datos por parte de un usuario
se implementa buscando una estructura de índice. Este índice solicita acceso al registro mediante
una solicitud al buffer pool. Si se inserta o elimina un registro, dicha solicitud pasa por el
administrador de memoria, que a su vez interactúa con el grupo de búfer para obtener acceso al
archivo del disco. Un programa como este es demasiado complejo para que casi cualquier
programador humano pueda mantener todos los detalles en su cabeza a la vez. La única manera
de diseñar e implementar un programa de este tipo es mediante el uso adecuado de la abstracción
y las metáforas. En la programación orientada a objetos, dicha abstracción se maneja mediante
clases.

Los tipos de datos tienen tanto unlógicoy unfísicoforma. La definición del tipo de
datos en términos de un ADT es su forma lógica. La implementación del tipo de datos
como estructura de datos es su forma física. La Figura 1.1 ilustra esta relación entre las
formas lógicas y físicas de los tipos de datos. Cuando implementas un ADT, estás tratando
con la forma física del tipo de datos asociado. Cuando utiliza un ADT en otra parte de su
programa, le preocupa la forma lógica del tipo de datos asociado. Algunas secciones de
este libro se centran en implementaciones físicas para un
12 Cap. 1 Estructuras de datos y algoritmos

Tipo de datos

TMD:
Tipo Elementos de datos:

Forma lógica
Operaciones

Estructura de datos:
Elementos de datos:
Espacio de almacenamiento
Forma física
Subrutinas

Figura 1.1La relación entre elementos de datos, tipos de datos abstractos y


estructuras de datos. El ADT define la forma lógica del tipo de datos. La estructura de
datos implementa la forma física del tipo de datos.

estructura de datos dada. Otras secciones utilizan el ADT lógico para la estructura de datos en el
contexto de una tarea de nivel superior.

Ejemplo 1.9Un entorno Java particular podría proporcionar una biblioteca que
incluya una clase de lista. La forma lógica de la lista está definida por las
funciones públicas, sus entradas y salidas que definen la clase. Esto podría ser
todo lo que sabes sobre la implementación de la clase de lista, y esto debería ser
todo lo que necesitas saber. Dentro de la clase, es posible una variedad de
implementaciones físicas para listas. Varios se describen en la Sección 4.1.

1.3 Patrones de diseño


En un nivel de abstracción más alto que los ADT se encuentran las abstracciones para describir el
diseño de programas, es decir, las interacciones de objetos y clases. Los diseñadores de software
experimentados aprenden y reutilizan patrones para combinar componentes de software. Estos han
llegado a ser conocidos comopatrones de diseño.
Un patrón de diseño incorpora y generaliza conceptos de diseño importantes para un
problema recurrente. Un objetivo principal de los patrones de diseño es transferir rápidamente
el conocimiento adquirido por los diseñadores expertos a los programadores más nuevos. Otro
objetivo es permitir una comunicación eficiente entre programadores. Es mucho más fácil
discutir un tema de diseño cuando se comparte un vocabulario técnico relevante para el tema.
Los patrones de diseño específicos surgen de la comprensión de que un problema de diseño
particular aparece repetidamente en muchos contextos. Están destinados a resolver problemas reales.
lemas. Diseño Los patrones son un poco como los genéricos. Describen la estructura de un
solución de diseño problema determinado y completan los detalles de este. Patrones de diseño de
son un poco como d estructuras: Cada uno proporciona costos y beneficios, lo que implica
Segundo. 1.3 Patrones de diseño 13

que las compensaciones son posibles. Por lo tanto, un patrón de diseño determinado podría tener variaciones en su

aplicación para adaptarse a las diversas compensaciones inherentes a una situación determinada.

El resto de esta sección presenta algunos patrones de diseño simples que se utilizan más
adelante en el libro.

1.3.1 Peso mosca

El patrón de diseño Flyweight está destinado a resolver el siguiente problema. Tienes una
aplicación con muchos objetos. Algunos de estos objetos son idénticos en la información que
contienen y en el papel que desempeñan. Pero hay que llegar a ellos desde varios lugares, y
conceptualmente son realmente objetos distintos. Debido a que hay tanta duplicación de la
misma información, nos gustaría aprovechar la oportunidad de reducir el costo de la memoria
compartiendo ese espacio. Un ejemplo proviene de la representación del diseño de un
documento. La letra "C" podría razonablemente representarse mediante un objeto que describa
los trazos y el cuadro delimitador de ese carácter. Sin embargo, no queremos crear un objeto
"C" separado en todas partes del documento donde aparece una "C". La solución es asignar una
única copia de la representación compartida para los objetos "C". Luego, cada lugar del
documento que necesite una “C” en una fuente, tamaño y tipo de letra determinados hará
referencia a esta única copia. Los diversos casos de referencias a una forma específica de "C" se
denominan pesos mosca.
Podríamos describir el diseño del texto en una página utilizando una estructura de árbol. La
raíz del árbol representa la página completa. La página tiene varios nodos secundarios, uno
para cada columna. Los nodos de columna tienen nodos secundarios para cada fila. Y las filas
tienen nodos secundarios para cada personaje. Estas representaciones de personajes son los
pesos mosca. El peso mosca incluye la referencia a la información de forma compartida y puede
contener información adicional específica de esa instancia. Por ejemplo, cada instancia de "C"
contendrá una referencia a la información compartida sobre trazos y formas, y también podría
contener la ubicación exacta de esa instancia del carácter en la página.

Los pesos mosca se utilizan en la implementación de la estructura de datos PR quadtree para


almacenar colecciones de objetos puntuales, descrita en la Sección 13.3. En un quadtree PR,
nuevamente tenemos un árbol con nodos de hojas. Muchos de estos nodos hoja representan áreas
vacías y, por lo tanto, la única información que almacenan es el hecho de que están vacías. Estos
nodos idénticos se pueden implementar utilizando una referencia a una única instancia de flyweight
para una mejor eficiencia de la memoria.

1.3.2 Visitante

Dado un árbol de objetos para describir el diseño de una página, es posible que deseemos
realizar alguna actividad en cada nodo del árbol. La sección 5.2 analiza el recorrido del árbol,
que es el proceso de visitar cada nodo del árbol en un orden definido. Un ejemplo sencillo para
nuestra aplicación de composición de texto podría ser contar el número de nodos en el árbol.
14 Cap. 1 Estructuras de datos y algoritmos

que representa la página. En otro momento, es posible que deseemos imprimir una lista de todos los
nodos para fines de depuración.
Podríamos escribir una función transversal separada para cada actividad que pretendemos
realizar en el árbol. Un mejor enfoque sería escribir una función transversal genérica y pasar la
actividad que se realizará en cada nodo. Esta organización constituye el patrón de diseño de
visitantes. El patrón de diseño de visitantes se utiliza en las Secciones 5.2 (recorrido de árboles)
y 11.3 (recorrido de gráficos).

1.3.3 Compuesto
Hay dos enfoques fundamentales para abordar la relación entre una colección de acciones y
una jerarquía de tipos de objetos. Consideremos primero el enfoque procesal típico. Digamos
que tenemos una clase base para entidades de diseño de página, con una jerarquía de
subclases para definir subtipos específicos (página, columnas, filas, figuras, caracteres, etc.). Y
digamos que hay acciones que se deben realizar en una colección de dichos objetos (como
representar los objetos en la pantalla). El enfoque de diseño procedimental es que cada acción
se implemente como un método que toma como parámetro un puntero al tipo de clase base.
Cada acción de este método atravesará la colección de objetos, visitando cada objeto por turno.
Cada método de acción contiene algo así como una declaración de cambio que define los
detalles de la acción para cada subclase de la colección (por ejemplo, página, columna, fila,
carácter). Podemos reducir un poco el código usando el patrón de diseño de visitante para que
solo necesitemos escribir el recorrido una vez y luego escribir una subrutina de visitante para
cada acción que pueda aplicarse a la colección de objetos. Pero cada subrutina de visitante debe
contener lógica para tratar con cada una de las posibles subclases.

En nuestra aplicación de composición de páginas, sólo hay unas pocas actividades que nos
gustaría realizar en la representación de la página. Podríamos representar los objetos con todo
detalle. O podríamos querer una representación de “borrador” que imprima solo los cuadros
delimitadores de los objetos. Si se nos ocurre una nueva actividad para aplicar a la colección de
objetos, no necesitamos cambiar nada del código que implementa las actividades existentes. Pero
agregar nuevas actividades no será frecuente para esta aplicación. Por el contrario, podría haber
muchos tipos de objetos y con frecuencia podríamos agregar nuevos tipos de objetos a nuestra
implementación. Desafortunadamente, agregar un nuevo tipo de objeto requiere que modifiquemos
cada actividad, y las subrutinas que implementan las actividades obtienen declaraciones de cambio
bastante largas para distinguir el comportamiento de las muchas subclases.
Un diseño alternativo es hacer que cada subclase de objeto en la jerarquía encarne la acción de
cada una de las diversas actividades que podrían realizarse. Cada subclase tendrá código para realizar
cada actividad (como la representación completa o la representación del cuadro delimitador). Luego, si
deseamos aplicar la actividad a la colección, simplemente llamamos al primer objeto de la colección y
especificamos la acción (como una llamada a un método en ese objeto). En el caso de nuestro diseño
de página y su colección jerárquica de objetos, aquellos objetos que contienen otros objetos (como
una fila de objetos que contiene letras)
Segundo. 1.3 Patrones de diseño 15

llamará al método apropiado para cada niño. Si queremos agregar una nueva actividad con esta
organización, tenemos que cambiar el código para cada subclase. Pero esto es relativamente raro en
nuestra aplicación de composición de texto. Por el contrario, agregar un nuevo objeto a la jerarquía de
subclases (lo cual para esta aplicación es mucho más probable que agregar una nueva función de
representación) es fácil. Agregar una nueva subclase no requiere cambiar ninguna de las subclases
existentes. Simplemente requiere que definamos el comportamiento de cada actividad que se puede
realizar en la nueva subclase.

Este segundo enfoque de diseño de enterrar la actividad funcional en las subclases se


denomina patrón de diseño compuesto. En la Sección 5.3.1 se presenta un ejemplo detallado
del uso del patrón de diseño compuesto.

1.3.4 Estrategia

Nuestro ejemplo final de patrón de diseño nos permite encapsular y hacer intercambiable un
conjunto de acciones alternativas que podrían realizarse como parte de una actividad más
amplia. Continuando nuevamente con nuestro ejemplo de composición de texto, cada
dispositivo de salida al que deseemos renderizar requerirá su propia función para realizar la
renderización real. Es decir, los objetos se dividirán en píxeles o trazos constituyentes, pero la
mecánica real de renderizar un píxel o un trazo dependerá del dispositivo de salida. No
queremos incorporar esta funcionalidad de representación en las subclases de objetos. En lugar
de eso, queremos pasar a la subrutina que realiza la acción de renderizado un método o clase
que realice los detalles de renderizado apropiados para ese dispositivo de salida. Es decir,
deseamos entregarle al objeto la "estrategia" adecuada para realizar los detalles de la tarea de
renderizado. Por lo tanto, este enfoque se denomina patrón de diseño de estrategia.
El patrón de diseño Estrategia se puede utilizar para crear funciones de clasificación
generalizadas. La función de clasificación se puede llamar con un parámetro adicional. Este parámetro
es una clase que comprende cómo extraer y comparar los valores clave de los registros que se van a
ordenar. De esta manera, la función de clasificación no necesita conocer ningún detalle sobre cómo se
implementa su tipo de registro.

Uno de los mayores desafíos para comprender los patrones de diseño es que a veces
uno sólo se diferencia sutilmente de otro. Por ejemplo, es posible que se sienta
confundido acerca de la diferencia entre el patrón compuesto y el patrón de visitante. La
distinción es que el patrón de diseño compuesto trata sobre si se debe dar el control del
proceso transversal a los nodos del árbol o al árbol mismo. Ambos enfoques pueden hacer
uso del patrón de diseño de visitantes para evitar reescribir la función transversal muchas
veces, al encapsular la actividad realizada en cada nodo.
¿Pero no hace lo mismo el patrón de diseño de estrategias? La diferencia entre el patrón de
visitante y el patrón de estrategia es más sutil. Aquí la diferencia es principalmente de intención
y enfoque. Tanto en el patrón de diseño de estrategia como en el patrón de diseño de visitante,
se pasa una actividad como parámetro. El patrón de diseño de estrategia se centra en
encapsular una actividad que es parte de un proceso más amplio, de modo que
dieciséis Cap. 1 Estructuras de datos y algoritmos

que se pueden sustituir diferentes formas de realizar esa actividad. El patrón de diseño de
visitantes se centra en encapsular una actividad que se realizará en todos los miembros de una
colección para que se puedan sustituir actividades completamente diferentes dentro de un
método genérico que acceda a todos los miembros de la colección.

1.4 Problemas, algoritmos y programas


Los programadores suelen ocuparse de problemas, algoritmos y programas informáticos. Estos
son tres conceptos distintos.

Problemas:Como sugeriría su intuición, unproblemaes una tarea a realizar. Es mejor pensarlo en


términos de insumos y resultados coincidentes. La definición de un problema no debe incluir ninguna
restricción sobrecómoel problema está por resolver. El método de solución debe desarrollarse sólo
después de que el problema esté definido con precisión y comprendido en profundidad. Sin embargo,
una definición de problema debe incluir restricciones sobre los recursos que puede consumir
cualquier solución aceptable. Para que cualquier problema sea resuelto por una computadora,
siempre existen tales restricciones, ya sean explícitas o implícitas. Por ejemplo, cualquier programa de
computadora puede utilizar sólo la memoria principal y el espacio en disco disponible, y debe
ejecutarse en un período de tiempo “razonable”.
Los problemas pueden verse como funciones en el sentido matemático. Afunción es una
coincidencia entre entradas (ladominio)y salidas (elrango).Una entrada a una función puede ser
un valor único o una colección de información. Los valores que componen una entrada se
llamanparámetrosde la función. Una selección específica de valores para los parámetros se
llamainstanciadel problema. Por ejemplo, el parámetro de entrada de una función de
clasificación podría ser una matriz de números enteros. Un conjunto particular de números
enteros, con un tamaño dado y valores específicos para cada posición en el conjunto, sería un
ejemplo del problema de clasificación. Diferentes instancias pueden generar el mismo
resultado. Sin embargo, cualquier instancia de problema siempre debe generar el mismo
resultado cada vez que la función se calcula utilizando esa entrada en particular.
Este concepto de que todos los problemas se comportan como funciones matemáticas puede no
coincidir con su intuición sobre el comportamiento de los programas de computadora. Es posible que
conozca programas a los que se les puede dar el mismo valor de entrada en dos ocasiones distintas y
se obtendrán dos resultados diferentes. Por ejemplo, si escribe "fecha”a una línea de comando típica
de UNIX, obtendrá la fecha actual. Naturalmente, la fecha será diferente en días diferentes, aunque se
dé la misma orden. Sin embargo, obviamente hay más en la entrada del programa de fecha que el
comando que escribe para ejecutar el programa. El programa de fecha calcula una función. En otras
palabras, en cualquier día en particular, un programa de fechas que se ejecute correctamente solo
puede devolver una única respuesta en una entrada completamente especificada. Para todos los
programas de computadora, la salida está completamente determinada por el conjunto completo de
entradas del programa. Incluso un “generador de números aleatorios” está completamente
determinado por sus entradas (aunque algunos sistemas de generación de números aleatorios
parecen evitar esto aceptando un generador de números aleatorios).
Segundo. 1.4 Problemas, algoritmos y programas 17

entrada de un proceso físico más allá del control del usuario). La relación entre programas
y funciones se explora con más detalle en la Sección 17.3.

Algoritmos:UnalgoritmoEs un método o proceso que se sigue para resolver un problema.


Si el problema se ve como una función, entonces un algoritmo es una implementación de
la función que transforma una entrada en la salida correspondiente. Un problema puede
resolverse mediante muchos algoritmos diferentes. Un algoritmo dado resuelve sólo un
problema (es decir, calcula una función particular). Este libro cubre muchos problemas y
para varios de ellos presento más de un algoritmo. Para el importante problema de la
clasificación, presento casi una docena de algoritmos.
La ventaja de conocer varias soluciones a un problema es que la soluciónA podría ser más
eficiente que la soluciónBpara una variación específica del problema, o para una clase específica
de entradas al problema, mientras que la soluciónBpodría ser más eficiente queApara otra
variación o clase de insumos. Por ejemplo, un algoritmo de clasificación podría ser el mejor para
ordenar una pequeña colección de números enteros (lo cual es importante si necesita hacer
esto muchas veces). Otro podría ser el mejor para ordenar una gran colección de números
enteros. Un tercero podría ser el mejor para ordenar una colección de cadenas de longitud
variable.
Por definición, algo sólo puede denominarse algoritmo si tiene todas las
propiedades siguientes.

1.debe sercorrecto. En otras palabras, debe calcular la función deseada, convirtiendo


cada entrada en la salida correcta. Tenga en cuenta que cada algoritmo
implementa alguna función, porque cada algoritmo asigna cada entrada a alguna
salida (incluso si esa salida es una falla del programa). La cuestión aquí es si un
algoritmo dado implementa eldestinadofunción.
2.Está compuesto por una serie depasos concretos. Concreto significa que la acción descrita
en ese paso es completamente entendida (y factible) por la persona o máquina que debe
realizar el algoritmo. Cada paso también debe poder realizarse en un tiempo finito. Por
lo tanto, el algoritmo nos brinda una "receta" para resolver el problema realizando una
serie de pasos, cada uno de los cuales está dentro de nuestra capacidad de realizar. La
capacidad de realizar un paso puede depender de quién o qué debe ejecutar la receta.
Por ejemplo, los pasos de una receta de galletas en un libro de cocina podrían
considerarse suficientemente concretos para instruir a un cocinero humano, pero no
para programar una fábrica automatizada de fabricación de galletas.

3.Puede habersin ambigüedaden cuanto a qué paso se realizará a continuación. A menudo


es el siguiente paso en la descripción del algoritmo. Selección (por ejemplo, elsi
declaración en Java) normalmente es parte de cualquier lenguaje para describir
algoritmos. La selección permite elegir qué paso se realizará a continuación, pero el
proceso de selección es inequívoco en el momento en que se realiza la elección.
4.Debe estar compuesto por unfinochenumero de pasos. Si la descripción del algoritmo
estuviera compuesta por un número infinito de pasos, nunca podríamos esperar
18 Cap. 1 Estructuras de datos y algoritmos

escribirlo ni implementarlo como programa informático. La mayoría de los lenguajes


para describir algoritmos (incluido el inglés y el "pseudocódigo") proporcionan alguna
forma de realizar acciones repetidas, conocida como iteración. Ejemplos de iteración en
lenguajes de programación incluyen elmientrasyparaconstrucciones de bucle de Java.
La iteración permite descripciones breves, y la cantidad de pasos realmente realizados
está controlada por la entrada.
5.DeberíaTerminar. En otras palabras, no puede entrar en un bucle infinito.

Programas:A menudo pensamos en unprograma de computadoracomo una instancia, o


representación concreta, de un algoritmo en algún lenguaje de programación. En este libro, casi
todos los algoritmos se presentan en términos de programas o partes de programas.
Naturalmente, hay muchos programas que son instancias del mismo algoritmo, porque
cualquier lenguaje de programación de computadoras moderno puede usarse para
implementar la misma colección de algoritmos (aunque algunos lenguajes de programación
pueden hacer la vida más fácil al programador). Para simplificar la presentación, suelo utilizar
los términos "algoritmo" y "programa" indistintamente, a pesar de que en realidad son
conceptos separados. Por definición, un algoritmo debe proporcionar suficientes detalles para
poder convertirlo en un programa cuando sea necesario.
El requisito de que un algoritmo debe terminar significa que no todos los programas informáticos
cumplen con la definición técnica de algoritmo. Su sistema operativo es uno de esos programas. Sin
embargo, puede pensar en las diversas tareas de un sistema operativo (cada una con entradas y
salidas asociadas) como problemas individuales, cada uno resuelto mediante algoritmos específicos
implementados por una parte del programa del sistema operativo, y cada uno de los cuales termina
una vez que se termina su salida. producido.
Para resumir: Unproblemaes una función o un mapeo de entradas a salidas. Unalgoritmoes
una receta para resolver un problema cuyos pasos son concretos e inequívocos. Los algoritmos
deben ser correctos, de longitud finita y deben terminar para todas las entradas. Aprogramaes
una instanciación de un algoritmo en un lenguaje de programación.

1.5 Lecturas adicionales


Uno de los primeros trabajos autorizados sobre estructuras de datos y algoritmos fue la
serie de librosEl arte de la programación informáticapor Donald E. Knuth, siendo los
volúmenes 1 y 3 los más relevantes para el estudio de estructuras de datos [Knu97,
Knu98]. Un enfoque enciclopédico moderno de estructuras de datos y algoritmos que
debería ser fácil de entender una vez que domine este libro esAlgoritmospor Robert
Sedgewick [Sed11]. Para obtener una introducción didáctica excelente y muy legible (pero
más avanzada) a los algoritmos, su diseño y su análisis, consulteIntroducción a los
algoritmos: un enfoque creativopor Udi Manber [Man89]. Para un enfoque enciclopédico
avanzado, consulteIntroducción a los algoritmospor Cormen, Leiserson y Rivest [CLRS09].
Steven S. SkienaEl manual de diseño de algoritmos[Ski10] pro-
Segundo. 1.5 Lecturas adicionales 19

Proporciona sugerencias para muchas implementaciones de estructuras de datos y algoritmos que están
disponibles en la Web.

La afirmación de que todos los lenguajes de programación modernos pueden implementar los
mismos algoritmos (dicho con mayor precisión, cualquier función que sea computable por un lenguaje
de programación es computable por cualquier lenguaje de programación con ciertas capacidades
estándar) es un resultado clave de la teoría de la computabilidad. Para una introducción sencilla a este
campo, consulte James L. Hein,Estructuras discretas, lógica y computabilidad[Hola09].
Gran parte de la informática se dedica a la resolución de problemas. De hecho, esto es lo
que atrae a mucha gente al campo.Cómo resolverlode George Pólya [Pól57] se considera el
trabajo clásico sobre cómo mejorar la capacidad de resolución de problemas. Si quieres ser un
mejor estudiante (y también un mejor solucionador de problemas en general), consulta
Estrategias para la resolución creativa de problemaspor Folger y LeBlanc [FL95],Resolución
eficaz de problemaspor Marvin Levine [Lev94], yResolución de problemas y comprensiónpor
Arthur Whimbey y Jack Lochhead [WL99], yAprendizaje basado en rompecabezas por Zbigniew y
Matthew Michaelewicz [MM08].
VerEl origen de la conciencia en la ruptura de la mente bicameralde Julian Jaynes [Jay90]
para una buena discusión sobre cómo los humanos usan el concepto de metáfora para manejar
la complejidad. Más directamente relacionado con la educación y la programación en
informática, consulte “Cogito, Ergo Sum! Cognitive Processes of Students Dealing with Data
Structures” de Dan Aharoni [Aha00] para una discusión sobre cómo pasar del pensamiento en
contexto de programación a un pensamiento libre de programación de nivel superior (y más
orientado al diseño).
En un nivel más pragmático, la mayoría de la gente estudia estructuras de datos para
escribir mejores programas. Si espera que su programa funcione correcta y eficientemente,
primero debe ser comprensible para usted y sus compañeros de trabajo. Kernighan y PikeLa
práctica de la programación[KP99] analiza una serie de cuestiones prácticas relacionadas con la
programación, incluido un buen estilo de codificación y documentación. Para obtener una
introducción excelente (¡y entretenida!) a las dificultades que implica escribir programas
grandes, lea el clásicoEl mítico mes del hombre: ensayos sobre ingeniería de software por
Frederick P. Brooks [Bro95].
Si desea ser un programador Java exitoso, necesita buenos manuales de referencia a mano.
David FlanaganJava en pocas palabras[Fla05] proporciona una buena referencia para quienes
están familiarizados con los conceptos básicos del idioma.
Después de adquirir competencia en la mecánica de la redacción de programas, el
siguiente paso es dominar el diseño de programas. El buen diseño es difícil de aprender en
cualquier disciplina, y el buen diseño para software orientado a objetos es una de las artes más
difíciles. El diseñador novato puede impulsar el proceso de aprendizaje estudiando patrones de
diseño bien conocidos y utilizados. La referencia clásica sobre patrones de diseño esPatrones de
diseño: elementos de software orientado a objetos reutilizablespor Gamma, Helm, Johnson y
Vlissides [GHJV95] (comúnmente conocido como el libro de la “banda de los cuatro”).
Desafortunadamente, este es un libro extremadamente difícil de entender,
20 Cap. 1 Estructuras de datos y algoritmos

en parte porque los conceptos son inherentemente difíciles. Hay varios sitios web
disponibles que analizan patrones de diseño y que proporcionan guías de estudio para el
Patrones de diseñolibro. Otros dos libros que analizan el diseño de software orientado a
objetos sonDiseño y construcción de software orientado a objetos conC++por Dennis
Kafura [Kaf98], yHeurística de diseño orientada a objetospor Arthur J. Riel [Rie96].

1.6 Ejercicios
Los ejercicios de este capítulo son diferentes de los del resto del libro. La mayoría de
estos ejercicios se responden en los siguientes capítulos. Sin embargo, deberías no
Busque las respuestas en otras partes del libro. Estos ejercicios pretenden hacerle
pensar en algunos de los temas que se tratarán más adelante. Respóndelas lo mejor
que puedas con tus conocimientos actuales.

1.1Piense en un programa que haya utilizado y que sea inaceptablemente lento. Identifique las
operaciones específicas que hacen que el programa sea lento. Identifique otras operaciones básicas
que el programa realiza con la suficiente rapidez.

1.2La mayoría de los lenguajes de programación tienen un tipo de datos entero incorporado.
Normalmente, esta representación tiene un tamaño fijo, lo que impone un límite al tamaño que se
puede almacenar un valor en una variable entera. Describa una representación de números enteros
que no tenga restricción de tamaño (aparte de los límites de la memoria principal disponible de la
computadora) y, por lo tanto, no tenga límite práctico sobre el tamaño de un número entero que se
puede almacenar. Muestre brevemente cómo se puede utilizar su representación para implementar
las operaciones de suma, multiplicación y exponenciación.

1.3Defina un ADT para cadenas de caracteres. Su ADT debe consistir en funciones típicas
que se pueden realizar en cadenas, con cada función definida en términos de su
entrada y salida. Luego defina dos representaciones físicas diferentes para
cadenas.
1.4Defina un ADT para una lista de números enteros. Primero, decida qué funcionalidad
debe proporcionar su ADT. El ejemplo 1.4 debería darle algunas ideas. Luego,
especifique su ADT en Java en forma de declaración de clase abstracta, mostrando
las funciones, sus parámetros y sus tipos de retorno.
1.5Describa brevemente cómo se representan típicamente las variables enteras en una
computadora. (Busque aritmética en complemento a uno y en complemento a dos en un libro
de texto de introducción a la informática si no está familiarizado con ellas.) ¿Por qué esta
representación de números enteros califica como una estructura de datos como se define en
la Sección 1.2?

1.6Defina un ADT para una matriz bidimensional de números enteros. Especifique con precisión las
operaciones básicas que se pueden realizar en dichas matrices. A continuación, imagine una
aplicación que almacena una matriz con 1000 filas y 1000 columnas, donde menos
Segundo. 1.6 Ejercicios 21

de 10.000 de los valores de la matriz son distintos de cero. Describa dos implementaciones
diferentes para dichos arreglos que serían más eficientes en cuanto a espacio que una
implementación de arreglo bidimensional estándar que requiere un millón de posiciones.

1.7Imagine que le han asignado la tarea de implementar un programa de clasificación. El objetivo es hacer
que este programa sea de uso general, en el sentido de que no desea definir de antemano qué
registros o tipos de claves se utilizan. Describa formas de generalizar un algoritmo de clasificación
simple (como la clasificación por inserción o cualquier otra clasificación con la que esté familiarizado)
para respaldar esta generalización.

1.8Imagine que le han asignado la tarea de implementar una búsqueda secuencial simple en
una matriz. El problema es que quieres que la búsqueda sea lo más general posible. Esto
significa que debe admitir tipos de claves y registros arbitrarios. Describa formas de
generalizar la función de búsqueda para respaldar este objetivo. Considere la posibilidad
de que la función se utilice varias veces en el mismo programa, en diferentes tipos de
registros. Considere la posibilidad de que la función deba usarse en diferentes claves
(posiblemente con el mismo o diferentes tipos) del mismo registro. Por ejemplo, se
puede buscar un registro de datos de un estudiante por código postal, por nombre, por
salario o por GPA.
1.9¿Todos los problemas tienen un algoritmo?
1.10¿Cada algoritmo tiene un programa Java?
1.11Considere el diseño de un programa de revisión ortográfica destinado a ejecutarse en una
computadora personal. El corrector ortográfico debería poder manejar rápidamente un
documento de menos de veinte páginas. Supongamos que el corrector ortográfico viene
con un diccionario de unas 20.000 palabras. ¿Qué operaciones primitivas deben
implementarse en el diccionario y cuál es una restricción de tiempo razonable para cada
operación?
1.12Imagine que lo han contratado para diseñar un servicio de base de datos que contiene
información sobre ciudades y pueblos de Estados Unidos, como se describe en el
ejemplo 1.2. Sugiera dos posibles implementaciones para la base de datos.
1.13Imagine que se le proporciona una serie de registros ordenados con respecto a algún
campo clave contenido en cada registro. Proporcione dos algoritmos diferentes
para buscar en la matriz para encontrar el registro con un valor clave específico.
¿Cuál consideras “mejor” y por qué?
1.14¿Cómo harías para comparar dos algoritmos propuestos para ordenar una serie de
números enteros? En particular,
(a)¿Cuáles serían las medidas de costo apropiadas para usar como base para comparar
los dos algoritmos de clasificación?
(b)¿Qué pruebas o análisis realizaría para determinar cómo se desempeñan los
dos algoritmos bajo estas medidas de costos?
1.15Un problema común para los compiladores y editores de texto es determinar si los paréntesis (u
otros corchetes) en una cadena están equilibrados y anidados correctamente.
22 Cap. 1 Estructuras de datos y algoritmos

Por ejemplo, la cadena “((())())()” contiene pares de paréntesis anidados


correctamente, pero la cadena “)()(” no; y la cadena “())” no contiene paréntesis
que coincidan correctamente. .
(a)Dar un algoritmo que devuelvaverdaderosi una cadena contiene paréntesis
correctamente anidados y equilibrados, yFALSOsi no.Pista: En ningún momento,
mientras escanea una cadena legal de izquierda a derecha, encontrará
tenía más paréntesis derechos que izquierdos.
(b)Proporcione un algoritmo que devuelva la posición en la cadena del primero de-
defender el paréntesis si la cadena no está anidada y equilibrada correctamente. Es
decir, si se encuentra un paréntesis derecho sobrante, se devuelve su posición; si hay
demasiados paréntesis izquierdos, devuelve la posición del primer paréntesis izquierdo
sobrante. Devolver−1si la cuerda está correctamente equilibrada y anidada.

1.16Un gráfico consta de un conjunto de objetos (llamados vértices) y un conjunto de aristas,


donde cada arista conecta dos vértices. Cualquier par de vértices dado puede estar
conectado por una sola arista. Describe al menos dos formas diferentes de representar
las conexiones definidas por los vértices y aristas de un gráfico.
1.17Imagine que es un empleado de envío de una gran empresa. Le acaban de
entregar unas 1.000 facturas, cada una de las cuales es una única hoja de
papel con un gran número en la esquina superior derecha. Las facturas deben
ordenarse por este número, de menor a mayor. Anota tantos enfoques
diferentes para clasificar las facturas como puedas imaginar.
1.18¿Cómo ordenaría una matriz de aproximadamente 1000 números enteros desde el valor más
bajo hasta el valor más alto? Escriba al menos cinco enfoques para ordenar la matriz. No
escriba algoritmos en Java o pseudocódigo. Simplemente escriba una oración o dos para cada
enfoque para describir cómo funcionaría.
1.19Piense en un algoritmo para encontrar el valor máximo en una matriz (sin clasificar). Ahora,
piense en un algoritmo para encontrar el segundo valor más grande de la matriz. ¿Cuál
es más difícil de implementar? ¿Cuál tarda más en ejecutarse (según la cantidad de
comparaciones realizadas)? Ahora, piense en un algoritmo para encontrar el tercer valor
más grande. Finalmente, piense en un algoritmo para encontrar el valor medio. ¿Cuál es
el más difícil de estos problemas de resolver?
1.20Una lista sin ordenar permite la inserción en tiempo constante agregando un nuevo elemento al
final de la lista. Desafortunadamente, buscar el elemento con valor claveX requiere una
búsqueda secuencial a través de la lista desordenada hastaXse encuentra, lo que en promedio
requiere mirar la mitad del elemento de la lista. Por otro lado, una lista ordenada basada en
matrices denorteLos elementos se pueden buscar enregistronortetiempo con una búsqueda
binaria. Desafortunadamente, insertar un nuevo elemento requiere mucho tiempo porque
muchos elementos pueden desplazarse en la matriz si queremos mantenerla ordenada.
¿Cómo podrían organizarse los datos para apoyar tanto la inserción como la búsqueda en
registronorte¿tiempo?

También podría gustarte

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy