Estructuras de Datos y Algoritmos Listas, Pilas y Colas Contenido Listas Tipos abstractos de datos (ADTs( ADTs) El ADT lista Implementación n usando arreglos Listas encadenadas Aplicaciones Implementación n mediante cursores El ADT pila (Stack( Stack) Implementación n de pilas Aplicaciones El ADT cola Implementación n de colas Aplicaciones 1
Contenido Los puntos resaltantes de este capítulo son: El concepto de Tipo de Dato Abstracto (ADT) Como realizar operaciones eficientes en listas El ADT Pila y sus aplicaciones Al ADT Cola y sus aplicaciones Tipos de Datos Abstractos (ADTs( ADTs) Un ADT es un conjunto de objetos acompañado ado de un conjunto de operaciones definidas sobre ellos. Los ADTs son abstracciones matemáticas ticas independientes de la implementación Enteros, reales y booleanos son ATDs así como lo son listas, pilas y colas 2
El ADT Lista Las listas generalizadas tienen la forma A 1, A 2, A 3,... A N donde N es el tamaño o de la lista. Si N es cero la lista esta vacía. a. Para toda lista excepto la lista vacía, a, A i+1 sigue a A i. El primer elemento es A1 y el último A N La posición n del elemento A i es i Operaciones típicas t en listas printlist() ():: imprimir la lista. makeempty() ():: crear una lista vacía. a. pos find(elem x): : buscar la primera ocurrencia del elemento x en la lista. elem findkth(pos k): : buscar el elemento en la posición k. Insert (elem x, pos k): : insertar el elemento x en la posición k. Remove(elem x): eliminar la primera ocurrencia del elemento x. 3
Lista usando arreglos Se de estimar el tamaño o del arreglo, lo que puede resultar en un desperdicio de espacio. Las operaciones printlist, find, insert y remove son O(N), mientras que findkth es O(1). (Si la lista esta ordenada por una clave comparable el tiempo de find es O(log N)). Crear una lista mediante sucesivas inserciones es O(N 2 ). Ejemplo: una lista ordenada de números class OrdArray private double[] a; // ref to array a private int nelems; // number of data items //--------------------------------------------------------- public OrdArray(int max) // constructor a = new double[max]; // create array nelems = 0; //--------------------------------------------------------- public int size() return nelems; //--------------------------------------------------------- 4
Ejemplo: una lista ordenada de números public int find(double searchkey) int lowerbound = 0; int upperbound = nelems-1; int curin; while(true) curin = (lowerbound + upperbound ) / 2; if(a[curin]==searchkey) return curin; // found it else if(lowerbound > upperbound) return nelems; // can't find it else // divide range if(a[curin] < searchkey) lowerbound = curin + 1; // it's in upper half else upperbound = curin - 1; // it's in lower half // end else divide range // end while // end find() Ejemplo: una lista ordenada de números public void insert(double value) // put element into array int j; for(j=0; j<nelems; j++) // find where it goes if(a[j] > value) // (linear search) break; for(int k=nelems; k>j; k--) // move bigger ones up a[k] = a[k-1]; a[j] = value; // insert it nelems++; // increment size // end insert() 5
Ejemplo: una lista ordenada de números public boolean delete(double value) int j = find(value); if(j==nelems) // can't find it return false; else // found it for(int k=j; k<nelems; k++) // move bigger ones down a[k] = a[k+1]; nelems--; // decrement size return true; // end delete() Ejemplo: una lista ordenada de números public void display() // displays array contents for(int j=0; j<nelems; j++) // for each element, System.out.print(a[j] + " "); // display it System.out.println(""); //--------------------------------------------------------- // end class OrdArray 6
Listas encadenadas A diferencia de los arreglos los elementos no están n almacenados en forma contigua. Cada elemento, además s de la data, contiene una referencia a la ubicación n del próximo elemento. Los tiempos para insert y remove son O(1), mientras que find y findkth requieren O(N). Operaciones en listas encadenadas A 1 A 2 A 3 A 4 A 5 Una lista encadenada A 1 A 2 A 3 A 4 A 5 Borrando un elemento en una lista encadenada A 1 A 2 A 3 A 4 A 5 Agregando un elemento en una lista encadenada X 7
Cabeza de lista A 1 A 2 A 3 A 4 header Una lista encadenada header Una lista vacía Nodos de la lista package DataStructures; class ListNode // Constructors ListNode( Object theelement ) this( theelement, null ); ListNode( Object theelement, ListNode n ) element = theelement; next = n; // Friendly data; accessible by other package routines Object element; ListNode next; 8
Iteradores public class LinkedListItr LinkedListItr( ListNode thenode ) current = thenode; public boolean ispastend( ) return current == null; public Object retrieve( ) return ispastend( )? null : current.element; public void advance( ) if(!ispastend( ) ) current = current.next; private ListNode current; // Current position La clase LinkedList public class LinkedList private ListNode header; public LinkedList( ) header = new ListNode( null ); public boolean isempty( ) return header.next == null; public void makeempty( ) header.next = null; public LinkedListItr zeroth( ) return new LinkedListItr( header ); 9
La clase LinkedList public LinkedListItr first( ) return new LinkedListItr( header.next ); public void insert( Object x, LinkedListItr p ) if( p!= null && p.current!= null ) p.current.next = new ListNode( x, p.current.next ); public LinkedListItr find( Object x ) ListNode itr = header.next; while( itr!= null &&!itr.element.equals( x ) ) itr = itr.next; return new LinkedListItr( itr ); La clase LinkedList public LinkedListItr findprevious( Object x ) ListNode itr = header; while(itr.next!= null &&!itr.next.element.equals(x)) itr = itr.next; return new LinkedListItr( itr ); public void remove( Object x ) LinkedListItr p = findprevious( x ); if( p.current.next!= null ) p.current.next = p.current.next.next; 10
La clase LinkedList public static void printlist( LinkedList thelist ) if( thelist.isempty( ) ) System.out.print( "Empty list" ); else LinkedListItr itr = thelist.first( ); for( ;!itr.ispastend( ); itr.advance( ) ) System.out.print( itr.retrieve( ) + " " ); System.out.println( ); Listas doblemente encadenadas A 1 A 2 A 3 A 4 A 5 Una lista doblemente encadenada A 1 A 2 A 3 A 4 A 5 Una lista circular doblemente encadenada 11
El ADT polinomio Se definirá un tipo de datos abstracto para representar polinomios de una variable de la forma: N i f ( x) = a i x i= 0 Se definen las operaciones para crear, imprimir, sumar y multiplicar polinomios. Se podrían considerar otras operaciones como diferenciación n y división Polinomios usando arreglos public class Polynomial public static final int MAX_DEGREE = 100; public static int max( int a, int b ) return a > b? a : b; public Polynomial( ) zeropolynomial( ); public void zeropolynomial( ) for( int i = 0; i <= MAX_DEGREE; i++ ) coeffarray[ i ] = 0; highpower = 0; 12
Polinomios usando arreglos public Polynomial add( Polynomial rhs ) Polynomial sum = new Polynomial( ); sum.highpower = max( highpower, rhs.highpower ); for( int i = sum.highpower; i >= 0; i-- ) sum.coeffarray[i] = coeffarray[i] + rhs.coeffarray[i]; return sum; public Polynomial multiply( Polynomial rhs ) throws Overflow Polynomial product = new Polynomial( ); product.highpower = highpower + rhs.highpower; if( product.highpower > MAX_DEGREE ) throw new Overflow( ); for( int i = 0; i <= highpower; i++ ) for( int j = 0; j <= rhs.highpower; j++ ) product.coeffarray[i+j]+=coeffarray[i]*rhs.coeffarray[j]; return product; Polinomios usando arreglos public void print( ) for( int i = highpower; i > 0; i-- ) System.out.print( coeffarray[ i ] + "x^" + i + " + " ); System.out.println( coeffarray[ 0 ] ); private int coeffarray[ ] = new int [ MAX_DEGREE + 1 ]; private int highpower = 0; 13
Polinomios usando listas 10 1000 5 14 1 0 P 1 1000 14 El polinomio 10x + 5x + 1 3 1990-2 1492 11 1 5 0 P 2 El polinomio 1990 1492 3x 2x + 11x + 5 Multilistas S 1 S 2 S 3 S 4 C 1 C 2 C 3 C 4 C 5 14
Listas encadenadas usando cursores Se usa un arreglo para almacenar los nodos. Deben simularse las características de las listas encadenadas: Cada nodo contiene un enlace al siguiente. En el caso de cursores los enlaces son índices del arreglo Se pueden obtener nuevos nodos cuando se necesitan y los nodos son automáticamente ticamente re-usados al disponer de ellos. Esta implementación n puede hacerse en lenguajes que no manejan memoria dinámica. Nodo para CursorList package DataStructures; class CursorNode // Constructors CursorNode( Object theelement ) this( theelement, 0 ); CursorNode( Object theelement, int n ) element = theelement; next = n; // Friendly data; accessible by other package routines Object element; int next; 15
Iterador para CursorList public class CursorListItr CursorListItr( int thenode ) current = thenode; public boolean ispastend( ) return current == 0; public Object retrieve( ) return ispastend( )? null : CursorList.cursorSpace[ current ].element; public void advance( ) if(!ispastend( ) ) current = CursorList.cursorSpace[ current ].next; int current; // Current position CusorList public class CursorList private static int alloc( ) int p = cursorspace[ 0 ].next; cursorspace[ 0 ].next = cursorspace[ p ].next; if( p == 0 ) throw new OutOfMemoryError( ); return p; private static void free( int p ) cursorspace[ p ].element = null; cursorspace[ p ].next = cursorspace[ 0 ].next; cursorspace[ 0 ].next = p; 16
CusorList public CursorList( ) header = alloc( ); cursorspace[ header ].next = 0; public boolean isempty( ) return cursorspace[ header ].next == 0; public void makeempty( ) while(!isempty( ) ) remove( first( ).retrieve( ) ); public CursorListItr zeroth( ) return new CursorListItr( header ); CusorList public CursorListItr first( ) return new CursorListItr( cursorspace[ header ].next ); public void insert( Object x, CursorListItr p ) if( p!= null && p.current!= 0 ) int pos = p.current; int tmp = alloc( ); cursorspace[ tmp ].element = x; cursorspace[ tmp ].next = cursorspace[ pos ].next; cursorspace[ pos ].next = tmp; 17
CusorList public CursorListItr find( Object x ) int itr = cursorspace[ header ].next; while( itr!= 0 &&!cursorspace[ itr].element.equals(x)) itr = cursorspace[ itr ].next; return new CursorListItr( itr ); public CursorListItr findprevious( Object x ) int itr = header; while( cursorspace[ itr ].next!= &&!cursorspace[cursorspace[itr].next].element.equals(x)) itr = cursorspace[ itr ].next; return new CursorListItr( itr ); CusorList public void remove( Object x ) CursorListItr p = findprevious( x ); int pos = p.current; if( cursorspace[ pos ].next!= 0 ) int tmp = cursorspace[ pos ].next; cursorspace[ pos ].next = cursorspace[ tmp ].next; free( tmp ); static public void printlist( CursorList thelist ) if( thelist.isempty( ) ) System.out.print( "Empty list" ); else CursorListItr itr = thelist.first( ); for( ;!itr.ispastend( ); itr.advance( ) ) System.out.print( itr.retrieve( ) + " " ); System.out.println( ); 18
CusorList private int header; static CursorNode[ ] cursorspace; private static final int SPACE_SIZE = 100; static cursorspace = new CursorNode[ SPACE_SIZE ]; for( int i = 0; i < SPACE_SIZE; i++ ) cursorspace[ i ] = new CursorNode( null, i + 1 ); cursorspace[ SPACE_SIZE - 1 ].next = 0; public static void main( String [ ] args ) CursorList thelist = new CursorList( ); CursorListItr theitr; int i; theitr = thelist.zeroth( ); printlist( thelist ); CusorList for( i = 0; i < 10; i++ ) thelist.insert( new MyInteger( i ), theitr ); printlist( thelist ); theitr.advance( ); for( i = 0; i < 10; i += 2 ) thelist.remove( new MyInteger( i ) ); for( i = 0; i < 10; i++ ) if(( i % 2 == 0 )!= (thelist.find( new MyInteger( i )).ispastend( ))) System.out.println( "Find fails!" ); System.out.println( "Finished deletions" ); printlist( thelist ); 19
El ADT Pila (Stack( Stack) Una pila (stack( stack) ) es una lista en la que todas las operaciones se efectúan en la posición n final de la misma Las operaciones fundamentales son: push(elem x): coloca el elemento x al final de la lista. pop(): remueve el elemento al final de la lista. elem top(): retorna el elemento final de la pila El ADT Pila (Stack( Stack) Las pilas también n se conocen como colas LIFO (Last In First Out) Debido a la simplicidad de las operaciones permitidas estas son muy rápidas. r A pesar de su simplicidad, las pilas son estructuras muy útiles. Todos los programas tienen al menos una pila para almacenar argumentos, variables locales y dirección n de retorno de llamadas a funciones. 20
Pilas usando listas encadenadas public class StackLi public StackLi( ) topofstack = null; public boolean isfull( ) return false; public boolean isempty( ) return topofstack == null; public void makeempty( ) topofstack = null; Pilas usando listas encadenadas public Object top( ) if( isempty( ) ) return null; return topofstack.element; public void pop( ) throws Underflow if( isempty( ) ) throw new Underflow( ); topofstack = topofstack.next; public Object topandpop( ) if( isempty( ) ) return null; Object topitem = topofstack.element; topofstack = topofstack.next; return topitem; 21
Pilas usando listas encadenadas public void push( Object x ) topofstack = new ListNode( x, topofstack ); private ListNode topofstack; public static void main( String [ ] args ) StackLi s = new StackLi( ); for( int i = 0; i < 10; i++ ) s.push( new MyInteger( i ) ); while(!s.isempty( ) ) System.out.println( s.topandpop( ) ); Pilas usando arreglos public class StackAr public StackAr( ) this( DEFAULT_CAPACITY ); public StackAr( int capacity ) thearray = new Object[ capacity ]; topofstack = -1; public boolean isempty( ) return topofstack == -1; public boolean isfull( ) return topofstack == thearray.length - 1; 22
Pilas usando arreglos public void makeempty( ) topofstack = -1; public Object top( ) if( isempty( ) ) return null; return thearray[ topofstack ]; public void pop( ) throws Underflow if( isempty( ) ) throw new Underflow( ); thearray[ topofstack-- ] = null; Pilas usando arreglos public void push( Object x ) throws Overflow if( isfull( ) ) throw new Overflow( ); thearray[ ++topofstack ] = x; public Object topandpop( ) if( isempty( ) ) return null; Object topitem = top( ); thearray[ topofstack-- ] = null; return topitem; 23
Pilas usando arreglos private Object [ ] thearray; private int topofstack; static final int DEFAULT_CAPACITY = 10; public static void main( String [ ] args ) StackAr s = new StackAr( 12 ); try for( int i = 0; i < 10; i++ ) s.push( new MyInteger( i ) ); catch( Overflow e ) System.out.println( "Unexpected overflow" ); while(!s.isempty( ) ) System.out.println( s.topandpop( ) ); Aplicaciones de pilas Balance de simbolos. Conversión n de expresiones de infix a postfix. Evaluación n de expresiones. Eliminación n de recursión 24
Balance de Parentesis class BracketChecker private String input; public BracketChecker(String in) input = in; public void check() int stacksize = input.length(); // input string // constructor // get max stack size StackX thestack = new StackX(stackSize); // make stack for(int j=0; j<input.length(); j++)// get chars in turn char ch = input.charat(j); switch(ch) case '': case '[': case '(': thestack.push(ch); break; // get char // opening symbols // push them case '': // closing symbols case ']': case ')': if(!thestack.isempty()) // if stack not empty, char chx = thestack.pop(); // pop and check if( (ch=='' && chx!='') (ch==']' && chx!='[') (ch==')' && chx!='(') ) System.out.println("Error: "+ch+" at "+j); else // prematurely empty System.out.println("Error: "+ch+" at "+j); break; default: // no action on other characters break; // end switch // end for if(!thestack.isempty() ) System.out.println("Error: missing right delimiter"); // end check() // end class BracketChecker 25
Infix to Postfix class InToPost // infix to postfix conversion private StackX thestack; private String input; private String output = ""; public InToPost(String in) // constructor input = in; int stacksize = input.length(); thestack = new StackX(stackSize); Infix to Postfix public String dotrans() // do translation to postfix for(int j=0; j<input.length(); j++) // for each char char ch = input.charat(j); // get it switch(ch) case '+': // it's + or - case '-': gotoper(ch, 1); // go pop operators break; // (precedence 1) case '*': // it's * or / case '/': gotoper(ch, 2); // go pop operators break; // (precedence 2) case '(': // it's a left paren thestack.push(ch); // push it break; 26
Infix to Postfix case ')': // it's a right paren gotparen(); // go pop operators break; default: // must be an operand output = output + ch; // write it to output break; // end switch // end for while(!thestack.isempty() ) // pop remaining opers output = output + thestack.pop(); // write to output return output; // return postfix // end dotrans() Infix to Postfix private void gotoper(char opthis, int prec1) // got operator from input while(!thestack.isempty() ) char optop = thestack.pop(); if( optop == '(' ) // if it's a '(' thestack.push(optop); // restore '(' break; else // it's an operator int prec2; // precedence of new op if(optop=='+' optop=='-') // find new op prec prec2 = 1; else prec2 = 2; 27
Infix to Postfix if(prec2 < prec1) // if prec of new op less // than prec of old thestack.push(optop); // save newly-popped op break; else // prec of new not less output = output + optop; // than prec of old // end else (it's an operator) // end while thestack.push(opthis); // push new operator // end gotoper() Infix to Postfix private void gotparen() // got right paren from input while(!thestack.isempty() ) char chx = thestack.pop(); if( chx == '(' ) // if popped '(' break; // we're done else // if popped operator output = output + chx; // output it // end while // end gotparen() // end class InToPost 28
Evaluación n de una expresión Postfix class ParsePost private StackX thestack; private String input; public ParsePost(String s) input = s; public int doparse() thestack = new StackX(20); // make new stack char ch; int j; int num1, num2, interans; for(j=0; j<input.length(); j++) // for each char, ch = input.charat(j); // read from input if(ch >= '0' && ch <= '9') // if it's a number thestack.push( (int)(ch-'0') ); // push it Evaluación n de una expresión Postfix else // it's an operator num2 = thestack.pop(); // pop operands num1 = thestack.pop(); switch(ch) // do arithmetic case '+': interans = num1 + num2; break; case '-': interans = num1 - num2; break; case '*': interans = num1 * num2; break; case '/': interans = num1 / num2; break; 29
Evaluación n de una expresión Postfix default: interans = 0; // end switch thestack.push(interans); // end else // end for interans = thestack.pop(); return interans; // end doparse() // end class ParsePost // push result // get answer El ADT Cola A semejanza de una pila, una cola es una lista en la que se restringen las operaciones permitidas: Solo se puede insertar en un extremo de la lista. Solo se permite extraer elementos en el otro extremo de la lista. Cualquier implementación n de lista es adecuada para colas. 30
Colas Usando Arreglos Es posible implementar colas usando arreglos de modo que las operaciones requieren un tiempo O(1) Además s del arreglo se tienen dos variables que contienen los índices al inicio y fin de la cola. También n se usa una tercera variable que contiene la longitud de la cola para diferenciar una cola vacía a de una que llena todo el arreglo. Colas Usando Arreglos public class QueueAr public QueueAr( ) this( DEFAULT_CAPACITY ); public QueueAr( int capacity ) thearray = new Object[ capacity ]; makeempty( ); public boolean isempty( ) return currentsize == 0; 31
Colas Usando Arreglos public boolean isfull( ) return currentsize == thearray.length; public void makeempty( ) currentsize = 0; front = 0; back = -1; public Object getfront( ) if( isempty( ) ) return null; return thearray[ front ]; Colas Usando Arreglos public Object dequeue( ) if( isempty( ) ) return null; currentsize--; Object frontitem = thearray[ front ]; thearray[ front ] = null; front = increment( front ); return frontitem; public void enqueue( Object x ) throws Overflow if( isfull( ) ) throw new Overflow( ); back = increment( back ); thearray[ back ] = x; currentsize++; 32
Colas Usando Arreglos private int increment( int x ) if( ++x == thearray.length ) x = 0; return x; private Object [ ] thearray; private int currentsize; private int front; private int back; static final int DEFAULT_CAPACITY = 10; Aplicaciones de Colas Colas de impresión. Simulación n de líneas l de espera. Colas de acceso a archivos en servidores 33