Los ordenadores son muy buenos en tratar con grandes cantidades de información.
Pueden repetir una tarea una y otra vez sin aburrirse. Cuando repiten una tarea que
generalmente están haciendo lo mismo a los datos u objetos similares. Es natural
querer organizar los objetos en algún tipo de estructura para que nuestro programa
puede cambiar fácilmente de un objeto a otro. ¿Cómo se añaden objetos a una
secuencia o una colección y cómo nos movemos de un elemento al siguiente tiene
cierto impacto en la forma en que podríamos querer organizar la recogida de datos
en un programa.
En este capítulo nos fijamos en las diferentes formas de organización de los
datos en una secuencia. También examinaremos cómo utilizar Python para
facilitar el trabajo con secuencias convenientes. La sobrecarga de operadores en
Python nos permite construir secuencias que podemos manipular con las
operaciones intuitivas. Por último, también examinaremos cómo la organización
de una secuencia afecta a la complejidad de cálculo de las operaciones en él.
Un tipo abstracto de datos es un término que se utiliza para describir una forma
de organizar los datos. Las listas son una forma de organizar una secuencia de
datos, pero en este capítulo vamos a descubrir otras maneras de organizar
secuencias también. Ascendiendo y descendiendo secuencias, listas enlazadas,
pilas y colas son todos los tipos de datos abstractos que vamos a explorar en este
capítulo.
En este capítulo usted leerá acerca de las diferentes formas de organización de los datos
dentro de un programa.
Al final del capítulo usted debe ser capaz de responder a estas preguntas.
4.2 Liza
Para concatenar dos listas hay que construir una nueva lista que contiene los
contenidos de ambos. Se trata de un método de acceso, ya que no muta cualquiera
de las listas. En su lugar, se construye una nueva lista. Podemos hacer esta
operación en tiempo O (n), donde n es la suma de las longitudes de las dos listas.
Aquí hay un código para lograr esto.
En la Sección. 4.2.5el tamaño se establece en el tamaño necesario para el resultado
de la concatenación de las dos listas. La complejidad del método __add__ es O (n),
donde n es la longitud de las dos listas. El tamaño inicial de la lista no tiene que ajustar
porque append tiene O (1) la complejidad como vimos en la Sección.2.10. Sin
embargo, ya sabemos que el tamaño de la lista resultante, el ajuste del tamaño inicial
debe acelerar la operación de concatenación ligeramente.
Para insertar en esta lista secuencial tenemos que hacer espacio para el nuevo
elemento. Dada la forma en que la lista está organizada, no hay más remedio que
copiar cada elemento después del punto donde queremos insertar el nuevo valor a la
siguiente ubicación en la lista. Esto funciona mejor si empezamos desde el extremo
derecho de la lista y trabajamos nuestro camino de regreso al punto donde se insertará
el nuevo valor. La complejidad de esta operación es O (n), donde n es el número de
elementos en la lista después de que el punto de inserción.
El índice i es el lugar donde el nuevo valor de e se va a insertar. Si el índice de pro-
RESPETA es más grande que el tamaño de la lista el nuevo elemento, e, se añade al final de
la lista.
4.2Lists 97
Comprobación de la igualdad de dos listas requiere las dos listas sean del
mismo tipo. Si son de diferentes tipos, a continuación, vamos a decir que no son
iguales. Además, las dos listas deben tener la misma longitud. Si ellos no tienen la
misma longitud, que no pueden ser iguales. Si se cumplen estas dos condiciones,
entonces las listas son iguales si todos los elementos en las dos listas son iguales.
Aquí está el código que implementa las pruebas de igualdad de dos objetos PyList.
pruebas de igualdad es un O (n) la operación.
Las pruebas para la pertenencia a una lista de medios de comprobación para ver
si un artículo es uno de los elementos de la lista. La única manera de hacerlo es
examinar cada punto en la secuencia en la lista. Si el artículo se encuentra a
continuación, se devuelve true, de lo contrario se devuelve Falso. Esto resulta en
O (n) la complejidad.
Esta idea de la búsqueda de un elemento en una secuencia es tan común que los
informáticos han nombrado. Esto se conoce como búsqueda lineal. Se llama así
porque de su O (n) la complejidad.
Es conveniente ser capaz de convertir una lista en una cadena para que pueda
ser impreso. Python incluye dos métodos que se pueden utilizar para la conversión
de una cadena. La primera probablemente estará ya familiarizado. La función str
llama al método __str__ en un objeto para crear una representación de cadena de
por sí apta para la impresión. Aquí está el código que implementa el método
__str__ para la clase PyList.
4.2Lists 99
Es interesante notar que ahora tenemos un método de hacer una copia de un objeto. Si
x es un objeto PyList, entonces eval (repr (x)) es una copia o clon de este objeto. Dado
que todos los elementos del objeto PyList también se clonan mediante la evaluación de
la representación del objeto, la clonación de un objeto como esto se llama un clon
profunda o copia en profundidad del objeto x.
También es posible hacer lo que se llama una copia superficial de un objeto.
Una copia superficial se produce cuando se copia el objeto, pero los elementos del
objeto son compartidos con el clon. Si deseamos crear una copia superficial de un
objeto PyList llamados x, escribiríamos lo siguiente.
x = PyList ([1,2,3])
y = PyList (x)
Comparando LST y LST2 no funcionó porque los elementos de las dos listas no
pueden ser pedidos. No se puede comparar un entero y una cadena. Sin embargo,
dos listas con los elementos similares se pueden comparar. Lista de comparación
se realiza lexicographi-camente como cuerdas. Tenga en cuenta el último ejemplo.
Las dos listas [1,2,3] y [1,1 'a'] se puede comparar porque el orden lexicográfico
no requiere que 3 y 'a' pueden comparar.
Hay algunos tipos de Python que no tienen un orden natural. Por ejemplo, no
hay un orden natural para los diccionarios de Python. No es posible ordenar una
lista de diccionarios. Por supuesto, es poco claro por qué querría así. Puesto que
no hay orden natural no parece ser una razón para ordenarlos. Sin embargo, si
había una manera de crear un orden en un conjunto de diccionarios se podría
definir por sí mismo escribiendo su propia clase que hereda de la clase dict y
definir su propio método __lt__ tal como lo hicimos para la clase Point.
Una vez que tenemos un ordenamiento de los elementos en una lista, podemos
ordenar los elementos de acuerdo a ese pedido. Las listas tienen un método para
ordenar que ordenar los elementos de una lista en función de su pedido. El código
en la Sección.4.4.2 ilustra cómo los elementos de una lista se pueden ordenar.
Pero, lo que hace este tipo método de trabajo y cuál es su costo? En otras
palabras, ¿qué tipo de algoritmo de ordenación hace uso de Python y cuál es su
complejidad computacional? Exploramos estas preguntas en las siguientes
secciones.
Dividir y conquistar, como los antiguos romanos podrían haber dicho, es una
estrategia eficaz de batalla. Resulta que este concepto es muy importante a la hora
de escribir los algoritmos. El algoritmo de ordenamiento por mezcla es un ejemplo
de un algoritmo de divide y vencerás. Dividir y conquistar algoritmos suelen ser
escrita de forma recursiva, pero no necesariamente tiene que ser. La premisa
básica es que dividimos un problema en dos partes. Cada una de las dos piezas es
fácil de resolver que tratar de abordar todo el problema a la vez porque las dos
piezas son cada uno más pequeño.
106 4 secuencias
de los dos sublistas según se copian, en tiempo O (n), a una nueva lista. A
continuación, la lista ordenada se copia de nuevo en la secuencia original, de nuevo en
tiempo O (n). En la función de fusión, el primer bucle mientras se ocupa de la fusión
de las dos sublistas hasta la una o la otra sub-lista está vacía. El segundo y el tercero,
mientras que los bucles se encargan de terminar lo que tenía sublista los elementos
sobrantes. Sólo una lista secundaria habrá sobras de elementos tan sólo una condición
en el segundo y tercer ciclos while volverá a ser cierto.
Observe que el tercer bucle while en el código está comentado. Copiar elementos de
J a tope en la tercera mientras bucle no es necesario, ya que sólo se copian la derecha
de nuevo al mismo lugar cuando el contenido de lst se copian de nuevo a la secuencia
SEC. Esta optimización acelera ordenamiento por mezcla un poco. Otra optimización
consiste en asignar previamente una lista más en la que copiar los valores y luego se
alternan entre la fusión en el original y la copia preasignados. De esta manera se evita
la sobrecarga de crear y añadiendo a las listas. Codificación de cualquiera de estos dos
optimizaciones no mejora la complejidad computacional del algoritmo, pero puede
mejorar su rendimiento general ligeramente. Una de las críticas del algoritmo de
ordenamiento por mezcla es que los elementos de las dos sublistas no se pueden
combinar sin copiar a una nueva lista y luego de vuelta otra vez.
La función mergesort llama a una función de ayuda para conseguir todo comenzó.
Se llama a la función mergeSortRecursively con la secuencia y los valores de inicio y
de parada que indican la lista entera debe ser ordenada. Los parámetros de inicio y de
parada se utilizan cuando se divide la lista. La lista no está dividida físicamente al
llamar mergeSortRecursively. En cambio, los valores de inicio y de parada se utilizan
para calcular el punto medio entre ellos y luego las dos mitades están ordenados de
forma recursiva mitad. Dado que cada sublista es más pequeño, podemos estar seguros
de que la llamada recursiva hace su trabajo y ordena los dos sublistas. Entonces nos
queda más que combinar los dos sub-listas ordenadas llamando a la función de fusión.
El caso base para la función recursiva es cuando el tamaño de la lista secundaria es 1.
En ese momento tenemos una sublista ordenada.
En la Fig. 4.5toda la mitad izquierda de la lista ha sido ordenada. Además, tres
sublistas en la mitad derecha han sido ordenados y la tercera y cuarta lista
secundaria están en proceso de ser fusionado. Los puntos verdes representan las
porciones de la secuencia que se ordenan y los puntos negros indican la parte no
seleccionada de la secuencia original. Las líneas rojas en la parte inferior reflejan
las llamadas recursivas que se encuentran actualmente en la pila de tiempo de
ejecución. La longitud de la línea roja muestra la porción de la secuencia que se
está ordenada por su llamada recursiva correspondiente. La línea azul subraya
actualmente las dos sublistas se fusionaron.
El argumento que se funden tipo se ejecuta en O (n log n) tiempo necesita sólo un
poco de explicación. La división repetitiva de los resultados de la lista en O (log n) se
divide. Al final tenemos listas de tamaño 1. Si tuviéramos que contar cada
combinación que se produce no habría n / 2 se funde en la parte inferior, seguido de n /
4 se funde en el siguiente nivel, y así sucesivamente conduce a esta suma.
Iniciar
sesión2no
rte
yo Iniciar
El número de fusiones ≥ 2 =2
sesión norte
2 -1≈
norte
yo =0
Este análisis parece sugerir que la complejidad del algoritmo de ordenamiento por
2
mezcla es O (n ), Ya que hay más o menos n se funde cada uno de los cuales es O (n)
en sí. sin embargo, el
108 4 secuencias
2
algoritmo no es O (n ). Para ver por qué, considere la clasificación de la lista [5 8 2 6
9 1 0 7]. Después de dividir en varias ocasiones las listas nos ponemos manos a las
listas de tamaño de uno como se muestra en la primera lista de la Fig.4.6. Los artículos
individuales se combinan de dos en dos para formar listas ordenadas de dos, que se
muestran en la segunda lista de elementos. Mientras que hay cuatro fusiones que tienen
lugar en el nivel más bajo de la fusión tipo, las cuatro fusiones son cada uno para las
listas de dos elementos (es decir, uno de cada lista) y juntos forman una lista de n
elementos. Así que podemos agrupar todos estos cuatro combina junto al descubrir que
todas las fusiones en que toma más profundo nivel de O (n). No cada uno, pero todas
las fusiones en el nivel más profundo cuando se combina son O (n).
Ordenar 4.6Merge 109
Una vez que la lista se asignaron al azar, recogiendo un pivote al azar se vuelve más
fácil. La función de partición recoge el primer elemento de la secuencia que el pivote.
La partición se inicia desde
4.7Quicksort 111
ambos extremos y que funciona a la manera de la media. Esencialmente cada vez que
se encuentra un valor más grande que el pivote en el lado izquierdo y un valor menor
que el pivote se encuentra en el lado derecho, los dos valores se intercambian. Una vez
que se llega a la media de los dos lados, el pivote se intercambia en su lugar. Una vez
que se divide la secuencia, el algoritmo quicksort se llama de forma recursiva sobre las
dos mitades. Variables i y j son los índices de los valores de izquierda y derecha,
respectivamente, durante el proceso de partición.
Si nos fijamos en el código de partición, los dos comentaron mientras que las
condiciones de bucle son probablemente más fácil de entender que el código sin
comentar. Sin embargo, el código de menú reduci-mentado sólo utiliza el menor de
operador. Quicksort es el algoritmo de ordenación utilizado por el método de
clasificación en las listas. Sólo se requiere que se defina el menos de operador entre los
elementos de la secuencia. Al escribir los dos bucles while como lo hemos hecho, la
única orden requerido es definido por el operador menor que al igual que requiere
Python.
La instantánea en la Fig. 4.7muestra el efecto de partición en una secuencia. En
esta figura, la secuencia ha sido dividido dos veces. La primera partición recogió
un pivote que era centro casi muerto. Sin embargo, la segunda partición recogió un
pivote que no era tan bueno. La línea roja indica la parte de la secuencia que se
está repartió actualmente. Ver cómo el más a la izquierda valor en ese sub-
secuencia es el valor de pivote. Los dos puntos verdes son los valores de pivote
que ya están en sus posiciones correctas. Todos los valores por encima del pivote
va a terminar en la partición a la derecha del pivote y todos los valores a la
izquierda del pivote son menores que el pivote. Esta es la naturaleza de la
clasificación rápida. Una vez más, por la complejidad amortizado podemos
encontrar que el algoritmo de ordenación rápida se ejecuta en O (n log n) tiempo.
Considere la clasificación de la lista [5 8 2 6 9 1 0 7] utilizando quicksort.
Figura4.8representa la lista después de cada llamada a la función de partición. El
pivote en cada llamada se identifica por el elemento de color naranja. La función de
partición divide de la lista que se extiende a la derecha de su pivote. Después de la
partición, el pivote se trasladó a su emplazamiento definitivo mediante el canje
Fig. 4.7 ordenación rápida instantánea
112 4 secuencias
con el último elemento que es menor que el pivote. Luego de partición se realiza
sobre los dos sublistas resultantes.
La aleatorización hecho en el primer paso de la clasificación rápida ayuda a
elegir un valor de pivote más al azar. Esto tiene consecuencias reales en el
algoritmo de ordenación rápida, especialmente cuando la secuencia pasa a la
clasificación rápida tiene una oportunidad de ser ya ordenados. Si la secuencia
dada a quicksort está ordenada, o casi ordenada, ya sea en orden ascendente o
descendente, entonces quicksort no logrará O (n log n) la complejidad. De hecho,
2
el peor de los casos la complejidad del algoritmo es O (n ). Si el pivote elegido es
el siguiente valor de menos o más grande, entonces la partición no dividir el
problema en que sublistas más pequeñas como ocurrió cuando 9 fue elegido como
un pivote para una sublista en la Fig.4.8. El algoritmo simplemente poner un valor
en su lugar y terminar con una gran partición de todo el resto de los valores. Si
2
esto sucedió cada vez que un pivote fue elegido daría lugar a O (n ) Complejidad.
La aleatorización de la lista antes de la quicksorting que ayudará a garantizar que
esto no suceda.
Combinar especie no se ve afectada por la elección de un pivote, ya que no es necesaria
ninguna elección. Por lo tanto, combinar especie no tiene un caso peor o mejor de los casos
a considerar. Siempre va a lograr O (n log n) la complejidad. Aun así, la clasificación
rápida tiene un mejor rendimiento en la práctica que se funden especie porque el algoritmo
de ordenación rápida no tiene que copiar a una nueva lista y luego de vuelta otra vez. El
algoritmo de ordenación rápida es el estándar de facto de los algoritmos de ordenación.
Debido a que cada fila es en sí una lista en la clase Junta, sólo puede utilizar la
clase incorporado en la lista de las filas de la matriz. Cada ubicación en cada fila
de la matriz puede contener o bien un X, un O, o un objeto ficticio. Los objetos
ficticios están allí por conveniencia y representan un lugar abierto en el tablero. La
igualdad, eval, y los métodos completos se dejan como ejercicio para el estudiante.
El propósito de cada se describirá en la siguiente sección.
4.8 Secuencias bidimensionales 115
Muchos juegos, tanto de animación como de otro tipo, son fáciles de implementar el
uso de Tkinter y gráficos de tortuga. Los personajes animados o fichas en un juego
pueden ser implementados como una tortuga que se mueve por la pantalla según sea
necesario. Para el juego de tres en raya las X y O del pueden ser implementados como
RawTurtles. Un RawTurtle es igual que un objeto tortuga excepto que debemos
proporcionar el lienzo donde un RawTurtle se mueve alrededor. El código en la
Sección.4.8.2 contiene las tres clases que definen las X, la junta de la clase, y maniquí
especial que es un marcador de posición para lugares abiertos en el tablero.
El maniquí, X, y las clases O todos tienen un método eval que devuelve un 1 para
un movimiento de ordenador, una -1 para un movimiento humano, o un 0 para
ningún movimiento todavía. Los valores de estos movimientos se utilizan en un
algoritmo llamado Minimax. El algoritmo Minimax es un algoritmo recursivo que
se utiliza en dos personas de juego juego en el que un jugador es el equipo y cada
jugador tiene la opción de algún número de movimientos que se pueden hacer al
tomar las curvas. El algoritmo minimax es simple. La idea es que cuando es el
turno de la computadora debe elegir el movimiento que será lo mejor para el
equipo. Cada movimiento posible es analizado para encontrar el valor que sería lo
mejor para el equipo. Vamos a dejar que un 1 representan el mejor movimiento
para el ordenador. La peor jugada del ordenador podría hacer que estará
representada por una-1. Los valores de movimiento puede variar entre 1 y -1.
Cuando es el turno de la computadora que captará el movimiento que da como
resultado el valor máximo movimiento. Esa es la porción máxima de Minimax.
Para encontrar la mejor jugada, el equipo va a jugar el juego, alternando entre el
mejor movimiento que podría hacer el mejor movimiento y el ser humano podría
hacer. Una mejor movimiento para un ser humano sería una-1. Cuando es el turno de
los humanos, el equipo asumirá que el ser humano hará el mejor movimiento que él /
ella puede hacer. Esta es la parte min de minimax.
La función Minimax se da dos argumentos, el jugador (ya sea una -1 para el ser
humano o un 1 por equipo) y el tablero para el juego. El caso base para esta
función recursiva comprueba si se ha producido una de las tres cosas.
1. La actual junta es una victoria para el equipo. En ese caso Minimax devuelve
un 1 por una victoria del equipo.
2. La actual junta es una victoria para el ser humano. En ese caso devuelve un
Minimax-1 para una victoria humana.
3. La actual junta está llena. En ese caso, ya que ganaron ni humano o un
ordenador, Minimax devuelve un 0.
ese tablero. Sin embargo, no le dice que se mueven es la mejor para hacer. Para hacer
frente a esto podemos tener el código que se ejecuta el turno del equipo hacer un poco
de la obra. Para el código de ordenador a su vez para encontrar el mejor movimiento
que hace un movimiento en una copia de la junta, las llamadas Minimax con el ser
humano como el siguiente jugador para hacer un movimiento, y luego se registra el
valor que regresa de la llamada a Minimax. El código de ordenador a su vez utiliza el
patrón de respuesta y verificación para encontrar el valor máximo para todos los
movimientos posibles y el movimiento asociado que dio lugar a ese valor. Después de
encontrar el mejor movimiento, a su vez de la computadora termina por la toma de
ordenador que se mueven en el tablero y regresar por lo que el ser humano puede hacer
su siguiente movimiento. El código de tres en raya, que se puede encontrar en el sitio
web que acompaña al texto o en la Sección.20.5, Contiene el esquema para el juego.
La función Minimax y el código computerTurn se dejan como ejercicio para el lector.
Minimax puede ser utilizado en muchos juegos de dos personas de información
perfecta, tales como las damas y conectar cuatro. La información perfecta término
significa que ambos jugadores pueden ver el estado del juego [3]. El poker es un juego
de información imperfecta por lo que no sería adecuado para el algoritmo minimax.
Cabe señalar que de tres en raya tiene un pequeño espacio de búsqueda basta con que
el equipo puede resolver el juego. Eso significa que nunca se perderá. La mayoría de
los juegos sin embargo, no tienen solución, al menos con el medio de la computadora.
Por ejemplo, conectar cuatro tiene un espacio de búsqueda mucho más grande y no
puede ser resuelto por completo. La heurística se aplican a juegos como Conecta
Cuatro para estimar lo bueno o malo es un tablero después de que el algoritmo
Minimax ha buscado tan profunda como la que puede dadas sus limitaciones de
tiempo. Juegos como estos se estudian a menudo en el campo de la Inteligencia
Artificial [3]. Inteligencia Artificial incluye el estudio de los algoritmos de búsqueda
que puede ser utilizado incluso cuando el espacio de búsqueda es demasiado grande
para usar una búsqueda exhaustiva. En este texto, Cap.12 abarca unos algoritmos de
búsqueda heurística, junto con una heurística aplicada al algoritmo minimax para el
juego de Conecta Cuatro.
de las operaciones parecen tener la misma complejidad que las operaciones de la lista
de tipos de datos presentados en la Fig. 4.11. Hay algunas diferencias importantes sin
embargo. Las siguientes secciones proporcionan las implementaciones para algunas de
estas operaciones y señalar las diferencias en comparación con las operaciones lista de
tipos de datos dados en la Fig.4.11.
Las otras operaciones de lista enlazada se dejan como ejercicio para el lector. En
muchos casos, la clave para trabajar con una lista enlazada es para obtener una
referencia al nodo que preceds la ubicación que desea trabajar. Por ejemplo, para
eliminar un nodo de una lista vinculada solamente desea hacer el siguiente campo
del punto precedente nodo al nodo siguiente nodo que desea eliminar. Considere la
lista de muestras vinculado en la Fig.4.10. Para eliminar el segundo material (es
decir, la “b”) de esta lista queremos eliminar el nodo que contiene la referencia a
la “b”. Para ello podemos utilizar un cursor para encontrar el nodo anterior al que
hace referencia a la “b”. Una vez encontrado, el siguiente campo del cursor puede
hacerse para que apunte al nodo después de la “b” como se muestra en la Fig.4.12.
Cambiar el siguiente puntero de nodo del cursor para apuntar al nodo después
de los resultados “B” en el nodo que contiene el “b” abandono de la lista. Dado
que no existen otras referencias al nodo que contiene la “b”, y de hecho a la “b” en
sí, los dos objetos son basura recogida.
Listas 4.10Linked 123
Hay otras dos estructuras de datos secuenciales que son muy comunes en la
programación de computadoras. Una pila es una estructura de datos, donde el acceso
es solamente en un extremo de la secuencia. Los nuevos valores se insertan en la pila
para añadirlos a la secuencia y extraen de la pila para eliminarlos de la secuencia. La
pila de tiempo de ejecución, que se describe en el Cap.3, Era un tal caso de una pila.
Las pilas se utilizan en muchos algoritmos en la informática. Las pilas se puede utilizar
para evaluar expresiones numéricas. Son útiles al analizar la información. Pueden ser
utilizados para que coincida con paréntesis en los programas y expresiones. Las
operaciones en una pila se dan en la Fig.4.13.
Las pilas se llaman estructuras de datos LIFO último en entrar / primero en salir o.
El último elemento empujado es el primer elemento reventado. Una clase de pila puede
ser implementado en al menos un par diferente
maneras de alcanzar la complejidad de cálculo indicados en esta tabla. Una lista o una
lista enlazada será suficiente. El código en la Sección.4.11.2es una implementación de
una pila con una lista. La aplicación es bastante sencilla. El programa principal en la
Sección.4.11.2 comprueba el tipo de datos pila con un par de pruebas para asegurarse
de que el código funciona correctamente.
50 tratar:
51 s.pop ()
52 impresión( "Test 3 Failed")
53
54 excepto Error de tiempo de ejecución:
55 impresión( "Test 3 Aprobado")
56 excepto:
57 impresión( "Test 3 Failed")
58
59 tratar:
60 detener()
61 impresión( "Test 4 Failed")
62
63 excepto Error de tiempo de ejecución:
64 impresión( "Test 4 Aprobado")
65 excepto:
66 impresión( "Test 4 Failed")
67
68 Si __name __ == "__ main__":
69 principal()
1 cola de clase:
2 def __en si mismo):
3 self.items = []
4 self.frontIdx = 0
5
6 def __compress (self):
7 newlst = []
8 para i en el rango (self.frontIdx, Len (self.items)):
9 newlst.append (self.items [i])
10
11 self.items = newlst
12 self.frontIdx = 0
13
14 def dequeue (self):
15 Si self.isEmpty ():
diec
aumento RuntimeError ( "intento de quitar de la cola
iséis una cola vacía")
17
18 # Cuando la cola está medio lleno, comprimirlo. Esta
19 # Logra una complejidad amortizado de O (1), mientras
20 # No dejar que la lista siga creciendo sin control.
21 Si self.frontIdx * 2> len (self.items):
self .__ compresa
22 ()
23
24 item = self.items [self.frontIdx]
25 self.frontIdx + = 1
26 regreso ít
27
28 def poner en cola (sí, artículo):
29 self.items.append (punto)
30
31 def delantera (self):
32 Si self.isEmpty ():
aumento RuntimeError ( "Intento de acceso frontal de cola
33 vacía")
34
35 regreso self.items [self.frontIdx]
36
37 def estaVacia (self):
38 regreso self.frontIdx == len (self.items)
39
40 def principal():
41 q = Queue ()
42 lst = lista (rango de (10))
43 LST2 = []
44
45 para k en lst:
46 q.enqueue (k)
47
48 Si q.front () == 0:
49 impresión( "Test 1 Aprobado")
50 más:
51 impresión( "Test 1 Failed")
52
53 mientras no q.isEmpty ():
54 lst2.append (q.dequeue ())
55
56 Si LST2 = lst:
57 impresión( "Test 2 Failed")
58 más:
59 impresión( "Test 2 Aprobado")
4.11Stacks y colas 127
60
61 para k en lst:
62 q.enqueue (k)
63
64 LST2 = []
sesenta y cinco
66 mientras no q.isEmpty ():
67 lst2.append (q.dequeue ())
68
69 Si LST2 = lst:
70 impresión( "Test 3 Failed")
71 más:
72 impresión( "Test 3 Aprobado")
73
74 tratar:
75 q.dequeue ()
76 impresión( "Test 4 Failed")
77
78 excepto Error de tiempo de ejecución:
79 impresión( "Test 4 Aprobado")
80 excepto:
81 impresión( "Test 4 Failed")
82
83 tratar:
84 q.front ()
85 impresión( "Test 5 Failed")
86
87 excepto Error de tiempo de ejecución:
88 impresión( "Test 5 Aprobado")
89 excepto:
90 impresión( "Test 5 Failed")
91
92 Si __name __ == "__ main__":
93 principal()
Una expresión es una expresión infija donde los operadores aparecen en entre sus
operandos. Por ejemplo, (6+ 5) * 4 es una expresión infija porque +aparece entre el 6 y
5 * y aparece entre sus operandos. Python, al ser un lenguaje de programación, se
puede evaluar expresiones infijos. Sin embargo, digamos que nos gustaría escribir un
programa que permita evaluar las expresiones introducidas por el usuario. Podemos
hacer esto en un programa en Python? Resulta que podamos, y muy fácilmente. Python
incluye una función que va a tratar una cadena como una expresión que se evalúa y se
devolverá el resultado de dicha evaluación. La función se llama eval. Aquí es un
programa que utiliza la función eval.
1 def principal():
2 expr = input ( "Por favor, introduzca una expresión infija:")
3 resultado = eval (expr)
4 impresión( "El resultado de", expr, "es", resultado)
5
6 Si __name__ == "__main__":
7 principal()
128 4 secuencias
Esta es sin duda una muy interesante, aunque corto, programa. La función eval
hace un montón de trabajo para nosotros. ¿Pero como funciona? Resulta que
podemos escribir nuestra propia función eval usando un par de pilas. En esta
sección describimos un evaluador de expresiones infija. Para hacer nuestro trabajo
un poco más fácil, vamos a insistir en que el usuario introduzca espacios entre
todos los operadores (incluyendo parens) y operandos. Así, por ejemplo, el usuario
puede interactuar como sigue.
Por favor, introduzca una expresión infija: (6 +
5) * 4 - 9 El resultado de (6 + 5) * 4 - 9 = 35,0
El algoritmo infija evaluador utiliza dos pilas, una pila de operador y una pila
de operandos. El registro del operador llevará a cabo los operadores y parens
izquierda. La pila de operandos contiene números.
El algoritmo procede mediante el escaneo de las fichas de la expresión infija de
izquierda a derecha. Usted puede hacer esto con bastante facilidad en Python
mediante la división de la cadena de entrada y luego iterar sobre la lista de cadenas
desde la entrada. Las fichas son operadores (incluyendo parens) y números.
Cada operador tiene una prioridad asociada a ella. operadores de multiplicación
y división tienen la prioridad más alta, mientras que la suma y la resta son
siguiente. Finalmente los paréntesis de la izquierda y la derecha paren prioridad
son los más bajos. Una función llamada de precedencia puede escribirse de
manera que un operador dado, la función devuelve el valor de precedencia
prioridad adecuada. Usted puede decidir sobre sus valores de precedencia dadas
las restricciones prescritas.
Para comenzar el stack y el registro del operador se inicializan con pilas vacías
y un paréntesis de la izquierda se empuja en el registro del operador.
Para cada ficha en la entrada que hacemos lo siguiente:
4.11.2.2 Ejemplo
Para ver cómo funciona este algoritmo que le ayudará a ver un ejemplo. En las
figuras, el símbolo más a la derecha subrayada es la señal que está siendo
procesado y todas las fichas a la izquierda ya han ser procesada. La pila de
operandos y la pila de operador están etiquetados en los diagramas. El operador
topOp y el operador actual se dan también.
En la Fig. 4.15 las fichas (6 +5 han sido procesados y empujado sobre sus respectivas
pilas. Los paren correctas ahora se está procesando y operar fue llamado para procesar este
operador. En el procedimiento de operar el topOp es un “+”Y“+”Tiene precedencia mayor
que‘)’por lo que los paren derecho no puede ser empujado en la parte superior del operador
de suma. Por lo tanto, nos pop el registro del operador y operar. Desde que nos metimos un
“+”Entonces estallar los dos números de la pila de operandos, sumarlos, y empuje el
resultado como se muestra en la Fig. 4.16.
Después de pulsar el 11 en la pila, se encontró que el operador de la parte superior
era un paréntesis de la izquierda. En ese caso, nos fuimos los paren fue y regresó de
inmediato dejando sólo los paren individuales izquierda en el registro del operador. A
continuación, el evaluador procedió al encontrar el operador “*” y empujándolo y
luego encontrar el 4 y empujándolo en la pila de operandos. A continuación, el “-
”Operador no puede ser empujado sobre el registro del operador, ya que su prioridad
es menor que‘*’. Operar se llama y el topOp dicta que nos pop dos números,
multiplicamos ellos, y empuje el resultado como se muestra en la Fig.4.17. Los "-
”Operador tiene mayor prioridad que la‘(’lo que es sólo inserta en la pila del operador.
El 9 se procesa y se inserta en la pila de operando como se muestra en la Fig. 4.17.
Para finalizar la evaluación de la expresión infija, funcionan se llama una vez
más con “)” como el operador. Esto obliga al 9 que se debe restar del 44 dejando
35 en la pila operando y el registro del operador vacía. El resultado final es 35 y el
evaluador aparece el resultado de la pila de operandos y devuelve como el
resultado y así es como funciona la función eval Python. Resulta que la función
eval Python es un poco más sofisticado que este ejemplo. Sin embargo, el efecto
de la evaluación de expresiones simples infijos es el mismo.
leídos desde la fuente (por ejemplo, un archivo o internet) y se colocan en una cola
llamada mainQueue. A medida que las cuerdas se leen y se colocan en la cola del
algoritmo realiza un seguimiento de la longitud de la cadena más larga que serán
ordenadas. Vamos a llamar a esta longitud más larga.
Para el algoritmo radix tipo funcione correctamente, todas las cadenas en el
mainQueue tienen que tener la misma longitud, que no es probable, por supuesto.
Si una cadena es más corta que la cadena más larga que puede ser rellenado con
espacios en blanco. Para asistir, es útil tener una función que devuelve un carácter
de una cadena como la función charAt en la Sección. 4.11.6.
Radix sort es bastante simple. Se llama especie radix debido a una raíz es como
un punto decimal se mueve hacia atrás a través de la cadena. Movemos el punto
decimal un carácter a la vez hasta que lleguemos al primer carácter de cada
cadena.
Resumen 4.12Chapter 135
5. Al ordenar los elementos de una lista, ¿qué método debe estar definido para
esos elementos? ¿Por qué?
6. ¿Por qué la clasificación rápida funcionan mejor que fusionar tipo?
7. ¿En qué condiciones sería posible para el tipo de combinación obtenga
mejores resultados que la clasificación rápida?
8. Resumir lo que sucede cuando una lista se divide.
9. Resumir lo que sucede cuando dos listas se combinan en el algoritmo de
ordenamiento por mezcla.
10. ¿Cuál es el propósito del parámetro de inicio a la función de selección del
algoritmo de ordenación por selección?
11. ¿Cuáles son las ventajas de una lista enlazada más de un acceso al azar lista
imple-mentación de un tipo de lista de datos?
12. ¿Cuáles son las ventajas de una lista accesible al azar más de una lista
enlazada imple-mentación de un tipo de lista de datos?
13. ¿Cómo se diferencia una pila de una cola en la manera de acceder a él?
14. ¿Cuál es la complejidad del algoritmo radix tipo?
algoritmo para utilizar una copia adicional de la lista en lugar de asignar una
nueva lista cada vez que dos listas se combinan.
La lista extra se utiliza antes de llamar a la parte recursiva del algoritmo de
ordenamiento por mezcla. Luego, con cada nivel de recursividad alterna el
algoritmo de combinación de ordenar copias a la otra lista, mover de un tirón
entre las dos listas. Para lograr esto, la función mergeSortRecursively se debe
dar una nueva lista de listas de llamada en lugar de las listas de la lista ss. En
las listas [0] es la lista siguientes y en las listas [1] es la copia extra de la lista.
Un parámetro adicional se da a la función mergeSortRecursively. El índice de
la lista de fusionar a partir también se proporciona. Este índice le dará la
vuelta de ida y vuelta entre 0 y 1 como se produce cada llamada recursiva a
mergeSortRecursively. Este mover de un tirón de 0 a 1 a 0 y así
sucesivamente se puede lograr utilizando la aritmética modular por escrito:
listToMergeFromIndex = (listToMergeFromIndex + 1)% 2