Manual Básico Programación

38
1 ÍNDICE DISEÑO DE ESTRUCTURAS DE DATOS Y ALGORITMOS

description

manual programacion c#

Transcript of Manual Básico Programación

Page 1: Manual Básico Programación

1

ÍNDICE

DISEÑO DE ESTRUCTURAS DE DATOS Y ALGORITMOS

Page 2: Manual Básico Programación

2

1. Unidad didáctica 1 – Estructuras estáticas y dinámicas......................................4 1.1. Variables.............................................................................................................. 4 1.2. Declaración.......................................................................................................... 4 1.3. Tipos de datos..................................................................................................... 4 1.4. Identificadores ..................................................................................................... 4 1.5. Variables locales.................................................................................................. 5 1.6. Variables globales................................................................................................ 5 1.7. Constantes............................................................................................................ 6 1.8. Palabras clave ...................................................................................................... 7 1.9. Operadores aritméticos........................................................................................ 7 1.10. Operadores relacionales....................................................................................... 8 1.11. Operadores lógicos .............................................................................................. 8 1.12. Operadores de asignación.................................................................................... 8 1.13. Sentencias simples............................................................................................... 8 1.14. Sentencias compuestas ........................................................................................ 8 1.15. Sentencias alternativas o condicionales............................................................... 9

1.15.1. Sentencia alternativa simple/doble if…else ................................................. 9 1.15.2. Operador (?).................................................................................................. 9 1.15.3. Sentencia alternativa múltiple switch.........................................................10

1.16. Sentencias de iteración ...................................................................................... 11 1.16.1. Sentencia while........................................................................................... 11 1.16.2. Sentencia do-while ..................................................................................... 12 1.16.3 Sentencia for ................................................................................................ 12

1.17 Arrays ................................................................................................................. 13 1.17.1 Declaración de Arrays Unidimensionales ................................................. 14 1.17.2 Uso de Arrays Unidimensionales .............................................................. 15 1.17.3 Funciones más comunes con arrays ........................................................... 16 1.17.4 Foreach para el manejo de arrays .............................................................. 16 1.17.5 Ejemplos de declaración y uso de arrays ................................................... 17

1.18 Arrays Multidimensionales ................................................................................ 18 1.18.1 Declaración de Matrices ............................................................................. 19 1.18.2 Uso de Matrices......................................................................................... 20 1.18.3 Obteniendo Dimensiones de una Matriz .................................................... 20 1.18.4 Ejemplos de uso de Matrices ...................................................................... 20 1.18.5 Foreach en matrices.................................................................................... 20

1.19 Enumeraciones.................................................................................................... 21 2. Unidad didáctica 2 – Estructuras estáticas y dinámicas....................................23

2.1 Introducción......................................................................................................... 23 2.2 Algoritmos de Búsqueda Simple......................................................................... 23

2.2.1 Búsqueda Lineal ......................................................................................... 23 2.3 Algoritmos de Ordenamiento .............................................................................. 26

2.3.1 Ordenamiento por Selección ...................................................................... 26 2.3.2 Ordenamiento por Selección (versión iterativa)......................................... 28

2.4 Complejidad de Algoritmos ................................................................................ 31 2.4.1 Ordenamiento y Búsqueda Eficientes ........................................................ 31

2.5 Algoritmos Eficientes de Búsqueda .................................................................... 33 2.5.1 Búsqueda Binaria........................................................................................ 33 2.5.2 Algoritmo de Ordenamiento: Quicksort..................................................... 35

2.6 Pilas y colas (Stack & Queue)............................................................................. 36

Page 3: Manual Básico Programación

3

Page 4: Manual Básico Programación

4

1. Unidad didáctica 1 – Estructuras estáticas y dinámicas 1.1. Variables Una variable es una posición de memoria con nombre (identificador) que se usa para mantener valores que pueden ser modificados durante el programa. 1.2. Declaración Las variables deben estar previamente declaradas antes de poder hacer referencia a las mismas. La declaración de las variables será de la forma:

tipo nombre_variable;

Donde tipo será un tipo de datos válido y nombre_variable será el identificador de la misma. La posición dónde se declaran las variables vendrán dadas por las necesidades de ámbito de las mismas; siendo locales, las que se declaran dentro de funciones o métodos y globales, las que se declaran fuera de toda función o método. La asignación de valores a las variables podrá realizarse de las siguientes formas:

Forma 1: int edad; edad = 12;

Forma 2: int edad = 12;

1.3. Tipos de datos Los tipos de datos primitivos de variables pueden ser: int, long, float, double, decimal, string, char y bool. 1.4. Identificadores Son nombre_variable que se utilizan para representar variables, constantes, tipos y funciones de un programa. Se construyen con secuencias de una o más letras, dígitos o símbolos de subrayado. Los identificadores según el estándar ANSI deben:

- Comenzar por una letra o símbolo de subrayado.

Page 5: Manual Básico Programación

5

- No deben exceder de 32 caracteres. - Los compiladores suelen ser sensibles a mayúsculas y minúsculas. - Los identificadores no deben tener la misma secuencia de caracteres que una palabra clave o una función predefinida. - Los identificadores deben ser significativos para la aplicación que se está programando.

1.5. Variables locales Las variables que se declaran dentro de un bloque (entendemos por bloque una sentencia compuesta delimitada por {}), se denominan variables locales. Estas variables sólo son conocidas dentro del bloque donde han sido declaradas, por lo que sólo las pueden referenciar sentencias de su mismo bloque. Las variables locales sólo existen durante la ejecución del bloque de código en el que son declaradas, después se destruyen, por lo que se pierde el contenido de las mismas; no retienen valores. En el siguiente ejemplo, la variable dato de la funcion_1 no tiene nada que ver con la variable dato de la funcion_2

static void funcion_1() { int dato; dato = 2; }

static void function_2() { int dato; dato = 111; }

También se pueden declarar variables locales en cualquier bloque de código del programa. Por ejemplo dentro de un bloque if else, for, foreach, etc. 1.6. Variables globales Se declaran fuera de cualquier bloque, normalmente, al principio del programa. Las variables globales son conocidas en todo los bloques del programa y se pueden acceder desde cualquier bloque del mismo. Ocupan memoria durante toda la ejecución del programa.

Page 6: Manual Básico Programación

6

Si en un bloque se declara una variable local con el mismo nombre que una variable global, cuando se use el identificador dentro del bloque se hará referencia a la variable local. Serán variables distintas almacenadas en distintas posiciones de memoria. Sólo se deben declarar las variables globales necesarias, ya que en programas grandes puede provocar efectos laterales como modificaciones no deseadas. En el siguiente ejemplo, en la funcion_1 la variable temporal tendrá el valor de la variable global edad (12) mientras que en la funcion_2 la variable temporal, tendrá el valor de la variable local edad(200)

static int edad; static void Main(string[] args) { edad = 12; funcion_1(); } static void funcion_1() { int temporal = edad; function_2(); } static void function_2() { int edad = 200; int temporal = edad; }

1.7. Constantes Una constante es una variable cuyo valor no puede alterarse por el programa; como variables, tendrá un identificador o nombre que se asocia al valor de la constante. Se pueden definir constantes de cualquiera de los tipos de datos. Una constante no podrá aparecer a la izquierda del signo de asignación igual (=), salvo en su inicialización. Para declarar e inicializar constantes se hará con la palabra CONST seguido del tipo de dato y el nombre identificativos de la variable, preferiblemente con todas las letras en mayúsculas. Por ejemplo: const double PI = 3.14;

Page 7: Manual Básico Programación

7

1.8. Palabras clave Son identificadores predefinidos que tienen un significado especial para el compilador. Sólo se pueden usar de la forma que se han definido. Por ejemplo: const readonly new void this string struct int interface delegate null base parcial abstract sealed enum class override virtual using try catch finally throw for do while foreach public private protected internal static goto …

1.9. Operadores aritméticos Los operadores aritméticos son:

Operador Acción - Resta + Suma * Multiplicación / División % Resto de división -- Decremento ++ Incremento

Cuando el operador de división (/) se aplica a dos operandos de tipo int, el resto se desprecia (los decimales no se guardan) y se denomina división entera. En caso contrario es una división real. El operador del resto de la división (%) no se puede aplicar a los tipos de coma flotante (sólo para divisiones enteras). Los operadores de incremento y decremento (++ y --) se pueden usar antes de las variables o después, con significados diferentes: ++variable variable++ --variable variable-- Si el operador (++ ó --) precede a la variable (se pone antes), se realiza primero el incremento o el decremento y después usa el valor de la variable. Si el operador (++ ó --) sigue a la variable (se pone después), se usa primero el valor de la variable y después realiza el incremento o el decremento.

Page 8: Manual Básico Programación

8

1.10. Operadores relacionales

Operador Acción > Mayor que < Menor que >= Mayor o igual que <= Menor o igual que == Igual a != Distinto de

1.11. Operadores lógicos

Operador Acción && AND || OR ! NOT

1.12. Operadores de asignación Por lo general, el operador de asignación es el signo igual (=) y se usa de la siguiente forma:

<variable> = <expresión>; 1.13. Sentencias simples Una sentencia simple es una expresión válida en C# acabada en punto y coma (;)

Por ejemplo: int dato; dato = 2;

1.14. Sentencias compuestas Las sentencias compuestas son grupos de sentencias que se tratan como si fueran un única sentencia. Las sentencias compuestas comienzan con una llave de apertura { y se terminan con la llave de cierre o complementaria }. Generalmente las sentencias compuestas se usan para indicar el conjunto de operaciones a realizar cuando se cumplen ciertas condiciones (estructuras if-else), o bien el conjunto de operaciones que se quieren repetir hasta que se cumpla una condición (estructuras for, while,…)

Page 9: Manual Básico Programación

9

1.15. Sentencias alternativas o condicionales Se utilizan en puntos del programa donde sea necesario comprobar una condición. Se ejecutarán unas sentencias u otras dependiendo de si la condición es cierta o falsa. 1.15.1. Sentencia alternativa simple/doble if…else La sentencia alternativa simple if tiene la siguiente forma:

if (expresión) {

sentencias-1; ...

} sentencias-n;

Si la expresión es cierta (true) se ejecutan las sentencias-1; si es falsa (false), no se ejecutan. En ambos casos, el programa continuará por el punto sentencias-n. La sentencia alternativa doble if...else tiene la siguiente forma: if (expresión)

{ sentencias-1; ...

} else {

sentencias-2; ...

} sentencias-n;

Si la expresión es cierta (true) se ejecutan las sentencias-1; si es falsa (false), se ejecutan las sentencias-2. En ambos casos, el programa continuará por el punto sentencias-n. Nunca se ejecutarán ambos grupos de sentencias: sentencias-1 y sentencias-2. 1.15.2. Operador (?) El operador ? es equivalente a una sentencia if...else. Las siguientes estructuras son equivalentes:

// primera forma.. variable = expresión-1 ? expresión-2 : expresión-3;

Page 10: Manual Básico Programación

10

// segunda forma.. if (expresión-1)

variable = expresión-2; else

variable = expresión-3; 1.15.3. Sentencia alternativa múltiple switch Se compone de las palabras clave switch, case, default y break. La sentencia switch tiene la siguiente forma:

switch (expresión) {

case constante-1: sentencias-1; ...; break;

case constante-2:

sentencias-2; ...; break;

default:

sentencias-n; ...; break;

} Se compara la expresión con cada una de las constantes-i; cuando coincide la expresión con la constante, se ejecutarán las sentencias-i asociadas a esa constante. La sentencia break no es obligatoria, pero si no se pone, el programa después de ejecutar sentencias-i continuará con los siguientes case, comprobando la expresión con las siguientes constantes. Las constantes pueden ser enteras o de caracteres. La sentencia default es opcional; sus sentencias-n asociadas se ejecutarán cuando la expresión no ha coincidido con ninguna de las constantes-i. switch sólo puede comprobar la igualdad y además sólo trabaja con variables enteras o de caracteres. Dentro de un case no se pueden poner llaves. Ejemplo:

static void Main(string[] args) {

Console.Write("Introduzca un número: ");

Page 11: Manual Básico Programación

11

int num = int.Parse(Console.ReadLine()); switch (num) {

case 1: case 2: case 3: Console.Write("el número es un 1 ó un 2 ó un 3)"); break;

case 4: case 5:

Console.Write("el número es un 4 ó un 5"); break;

default: Console.Write("número no encontrado"); break;

}

Console.Read(); }

1.16. Sentencias de iteración Son las sentencias que nos permiten repetir un conjunto de sentencias mientras se cumpla una determinada condición. 1.16.1. Sentencia while La sentencia while tiene la siguiente forma:

while (condición) {

sentencias; ...;

} sentencias-siguientes;

Si la condición es cierta se ejecutan las sentencias; al llegar a la llave de cierre } se vuelve a evaluar la condición; si vuelve a ser cierta, se ejecutan de nuevo las sentencias. Y así sucesivamente. Cuando en algún momento la condición sea falsa (false), el programa continuará por el punto de sentencias-siguientes, ya fuera del while. Hay que asegurarse que la condición esté inicializada, es decir, que todas las variables que intervengan en la misma tengan algún valor. Además, también hay que asegurarse de que en algún momento no se cumpla la condición para que el programa salga del bucle. Ejemplo:

string resp = "S";

Page 12: Manual Básico Programación

12

while (resp == "S") {

Console.Write("¿Desea continuar [S/N]? "); resp = Console.ReadLine();

} 1.16.2. Sentencia do-while La sentencia do-while tiene la siguiente forma:

do {

sentencias; ...;

} while (condición); sentencias-siguientes;

En la sentencia do-while primero se ejecutan las sentencias; después, se comprueba la condición; si es cierta (true), se vuelve a ejecutar las sentencias y se vuelve a evaluar la condición. Y así sucesivamente. Cuando en algún momento la condición sea falsa (false), el programa continuará por el punto sentencias-siguientes ya fuera de la estructura do- while. Con do-while, al contrario que con while, se ejecutan las sentencias y después se evalúa la condición, por lo que las sentencias se ejecutarán al menos una vez. Ejemplo:

string resp; do {

Console.Write("¿Desea continuar [S/N]? "); resp = Console.ReadLine();

} while (resp == "S");

1.16.3 Sentencia for La sentencia for tiene la siguiente forma:

for(expresión-1; expresión-2; expresión-3) {

sentencias; ...;

}

Page 13: Manual Básico Programación

13

El uso habitual es: - expresión-1: se usa para inicializar las variables de control del bucle, es decir las que intervienen en la condición del bucle. - expresión-2: se usa para la condición de permanencia del bucle; si esta expresión es cierta se repite de nuevo el bucle. - expresión-3: se usa para modificar las variables de control del bucle, para que no sea un bucle infinito. Por lo que la sentencia for se podría escribir de la siguiente forma:

for(inicialización; condición; progresión) {

sentencias; ...;

} Siendo el orden de ejecución el siguiente: 1. Se ejecuta la inicialización. 2. Se comprueba la condición. 3. Si es cierta, se ejecutan las sentencias. 4. Se ejecuta la progresión y se vuelve al paso 2. Y así sucesivamente hasta que la condición sea falsa (false). Ejemplo:

for (int i = 1; i <= 10; i++) {

Console.WriteLine(i); } Console.Read();

Si queremos que un bucle finalice en algún momento antes que la condición del mismo deje de cumplirse, usaremos la sentencia break. Esta sentencia hace que la siguiente instrucción salte fuera del bucle. 1.17 Arrays Los arrays son estructuras de datos complejas (en el sentido de que no son atómicas) que agrupan datos de un mismo tipo en particular, llamado el tipo base del array. El tipo base de un array puede ser cualquiera de los tipos básicos de C#, o incluso algunos tipos complejos como las clases. Un array es también ejemplo de un modelo. Un array puede considerarse como ejemplo de una variable compuesta capaz de almacenar uno o más datos al mismo tiempo. La sintaxis del lenguaje permite referirse a cada uno de los elementos que constituyen

Page 14: Manual Básico Programación

14

el array empleando índices. Esto es posible pues los elementos del array están numerados en forma jerárquica y consecutiva, empezando en 0 en cada dimensión. El siguiente gráfico ilustra un ejemplo de un array llamado numeros, cuya posición 0 almacena el valor 10, la posición 1 el valor de 21, etc. Este array en total almacena n+1 elementos. El valor de n, depende de la memoria que pueda tener el computador y el tipo de elementos que se almacenen en el array.

Los arrays, al igual que el resto de las variables se identifican con un nombre. Al emplear ese nombre, se hace referencia a la estructura de datos como un todo, es decir, con todos sus elementos. El lenguaje interpreta dicho nombre como un puntero. Cuando se utiliza el nombre del array en forma indexada, es decir, combinado con índices, se hace referencia a un elemento particular, del tipo base, dentro de la estructura compleja. Importante: El lenguaje C# no controla la validez de los índices que se emplean para referenciar un array. Esto quiere decir que es posible cometer errores graves y difíciles de detectar en este sentido. Más adelante se presenta un ejemplo en este sentido. 1.17.1 Declaración de Arrays Unidimensionales Los arrays, al igual que las demás variables deben declararse antes de poder utilizarlas, y cumplen con las mismas reglas de alcance y vida. Los arrays de una sola dimensión reciben también el nombre de vectores. La sintaxis de la declaración de un array unidimensional es la siguiente: <tipo-base>[] <identificador>; Observaciones: • El <tipo-base> puede ser cualquiera de los tipos básicos del lenguaje, o incluso algunos complejos como estructuras. • El <identificador> es el nombre que distinguirá el array. • Los corchetes [ ] son obligatorios y denotan que el identificador descrito, del tipo-base indicado, es un array (lista de elementos del tipo base). • En esta declaración NO se define el tamaño que tendrá el array (aunque se puede determinar las dimensiones, lo que se verá más adelante). El tamaño del array se determina en una segunda declaración, que puede ir en la siguiente línea, como se muestra a continuación.

<identificador> = new <tipo-base> [<NumElementos>] En esta declaración, se dimensiona el array con una cantidad determinada de elementos, todos correspondientes a tipo-base. Es posible hacer la declaración del array y su dimensionamiento en una misma sentencia:

Page 15: Manual Básico Programación

15

<tipo-base>[ ] <identificador> = new <tipo-base>[<NumElementos>] Adicionalmente es factible declarar, dimensionar, e inicializar un array con todos sus elementos, en una sola declaración: <tipo-base>[ ] <identificador> = {valor1, valor2, ..., valorN}; Esta última declaración implícitamente dimensiona el array para almacenar los N elementos descritos, por lo que no se requiere dimensionarlo aparte. Es decir: • Con los valores indicados entre llaves {} se inicializarán los elementos del array. • Los valores deben ser del <tipo-base> del array También es factible declarar, dimensionar, e inicializar un array con todos sus elementos, en una sola declaración, pero sólo indicando un subconjunto de los valores que el array puede guardar:

<tipo-base>[] <identificador> = new <tipo-base>[N] {valor1, ..., valorM }; ... donde M<N, y N debe ser una expresión constante, como por ejemplo 10. Es factible hacer una declaración donde M>N, en cuyo caso, el real dimensionamiento del array se regirá por M. Algunos ejemplos: // Array para 10 enteros int [] numeros; numeros = new int[10]; // Array para 10 enteros int [] numeros = new int[10]; // Array para 10 enteros int [] numeros = { 1, 1, 1, 2, 3, 5, 2, 5, 3, 4 }; 1.17.2 Uso de Arrays Unidimensionales Los elementos de un array son variables del tipo base del vector, por lo que se utilizan de la misma manera en expresiones y demás instrucciones, como la asignación. Por ejemplo, para asignar un valor a un elemento de un array basta con escribir: <array>[indice] = <expresion>; donde <array> es el nombre de la variable e indice hace referencia a la posición del elemento al que se le quiere asignar el <expresion>. La referencia de valores en un array, se indexa desde el 0 al N-1. Importante: Puesto que los arrays son estructuras complejas (es decir, no básicas), no es posible asignar un array a otro mediante una simple asignación (=). Para hacer esto es necesario escribir un ciclo y asignar elemento a elemento. Como se mencionó anteriormente, el lenguaje C# no controla la validez de los índices que se emplean para referenciar un array. Esto quiere decir que es posible cometer errores muy difíciles de detectar en este sentido. Es necesario prestar especial interés a los valores que toman los índices para evitar estos problemas.

Page 16: Manual Básico Programación

16

Por ejemplo, la siguiente porción de código compila sin problemas (es decir, sin errores sintácticos), pero probablemente produzca un error en tiempo de ejecución al referenciarse posiciones inexistentes del array. // Las posiciones con índices del 10 al 19 son inválidas.

int[] array = { 1,1,1,1,1,1,1,1,1,1 }; // 10 elementos int i;

for (i=0; i<20; i++) array[i] = 0; // Error para i >= 10 También es común cometer estos errores olvidando que las posiciones de los arrays están numeradas a partir del índice cero. Es decir, en un array de tamaño N las posiciones están numeradas de 0 a N-1. 1.17.3 Funciones más comunes con arrays En C#, los arrays se representan con un tipo específico, y por ello cuentan con sus propios atributos y métodos específicos. Por ejemplo, para obtener el largo (la cantidad de elementos dimensionados) de un array, o para ordenarlo. El Largo de un Array En el caso de los arrays unidimensionales, el tamaño o cantidad de elementos se obtiene con la propiedad Length. int [] numeros = { 1,2,3,4,5,6 }; Console.WriteLine(“Largo: {0}”, numeros.Length); Ordenamiento de un Array En el caso de los arrays que sean de uno de los tipos predefinidos (int, float, char, etc.), es factible ordenarlos en forma creciente, aprovechando el método básico Sort() de la clase Array: int [] numeros = { 4,5,2,3,1,6 }; Array.Sort(numeros); // 1,2,3,4,5,6 Revertir el Orden de un Array En el mismo caso en que se pueda ordenar un array, se puede reordenar exactamente al revés de cómo está, aprovechando el método básico Reverse() de la clase Array: int [] numeros = { 1,2,3,4,5,6 }; Array.Reverse(numeros); // 6,5,4,3,2,1 1.17.4 Foreach para el manejo de arrays

Existe esta sentencia de control de flujo, especialmente diseñada para este tipo de estructuras, donde se manejan listas de elementos, todos del mismo tipo. Foreach

Page 17: Manual Básico Programación

17

depende de la definición previa de un array de elementos del mismo tipo, los cuales puede recorrer individualmente sin conocer el tamaño explícitamente (como se requiere en otras instrucciones, por ejemplo en el for). La sintaxis de uso es: foreach ( <tipo> <variable> in <array> ) { <instrucciones> } Donde: • <tipo> es el tipo básico de los elementos contenidos en el array. • <array> es el array de elementos a revisar. • <variable> es un identificador de una variable local del foreach() que se usará para ver un elemento del array en cada iteración. Ejemplo: using System; class MainClass {

public static void Main() {

int impares = 0, pares = 0; int[] arr = new int [] {0,1,2,5,7,8,11};

foreach (int i in arr) {

if (i%2 == 0) pares++;

else impares++; }

Console.WriteLine("Hay {0} impares y {1} pares.", impares, pares); Console.ReadLine();

} }

1.17.5 Ejemplos de declaración y uso de arrays En todos los casos, A es el nombre de la variable declarada: 1. Declaración de un array de 50 enteros: int[] A = new int[50] ; 2. Declaración de un array de 100 caracteres: char[] A = new char[100]; 3. Declaración e inicialización de un array de 10 enteros: int[] A = { 2, 5, 8, 100, 1, 2, 100, 5, 5, 5 } 4. Inicialización parcial: El resto se inicializa en cero: int[] A = new int[100] { 25, 5, 100, 25, 5 }

Page 18: Manual Básico Programación

18

5. Declaración e inicialización de un array de 10 caracteres: char[] A = { 'a', 'z', 'E', 'e', 65, '\65', '@', 'U ', '*', '\n' } 6. Asignando un valor a la sexta posición de un array de enteros: A[5] = 200; 7. Imprimiendo un array de 100 enteros mediante un ciclo for: int i; for (i=0; i<100; i++)

Console.Write("{0} ", A[i]); 8. Imprimiendo un array de 100 enteros mediante un ciclo foreach: foreach (int i in A)

Console.Write("{0} ", i); 9. Leyendo del usuario el contenido de un array de 20 enteros, mediante un ciclo for: int i; for (i=0; i<20; i++) A[i] = int.Parse(Console.ReadLine()); 10. Una función que recibe un array de enteros como argumento y calcula el promedio: int promedio(int[] A) {

int prom = 0; foreach( int i in A )

prom = prom + i; return(prom/A.Length);

} 11. Llamando una función que recibe un array de enteros como parámetro: int prom; int[] A; ... prom = promedio(A); 1.18 Arrays Multidimensionales Los arrays que se estudiaron anteriormente son estructuras de datos vectoriales de una sola dimensión. En C# también es posible manejar arrays de más de una dimensión. Particularmente en este lenguaje, existen dos maneras de declarar arrays multidimensionales, por un lado aquellos en que todas las dimensiones son fijas, también conocidos como matrices, y en segundo lugar, los que tienen filas (o columnas) de largo distinto. En forma rigurosa, estos últimos arrays no son más que arrays en los que cada elemento es a la vez otro array, que puede ser de diferente dimensión que su vecino. Los arrays de dos dimensiones reciben el nombre de matrices. Gran parte del desarrollo de esta sección se limita a las matrices, es decir, a arrays de dos dimensiones, de filas del mismo tamaño. Sin embargo, tanto la sintaxis para la

Page 19: Manual Básico Programación

19

declaración como la forma de utilizar estos arrays puede generalizarse sin problema a dimensiones mayores (3D, 4D, etc). Para referirse a cada uno de los elementos que constituyen el array es necesario emplear una serie de índices. En el caso de las matrices, dos. Esto es posible pues los elementos de la matriz, al igual que en el caso de los vectores unidimensionales, están numerados en forma jerárquica consecutiva, empezando en 0,0. Cada par de índices referencia tanto una fila, como una columna de la matriz, identificando de manera única cada elemento de la estructura. 1.18.1 Declaración de Matrices Como se mencionó anteriormente, las matrices no son más que arrays en los que cada elemento es a su vez otro array. La sintaxis de la declaración de una matriz es la siguiente: <tipo-base>[,] <identificador>; Observaciones: • El <tipo-base> puede ser cualquiera de los tipos básicos del lenguaje, o incluso algunos complejos como estructuras. Cada elemento de la matriz será del tipo definido aquí. • El <identificador> es el nombre que distinguirá la matriz. • Los corchetes [] son obligatorios. • El separador de dimensiones es la coma ‘,’. El dimensionamiento de estas matrices se puede hacer en una declaración separada, de la siguiente manera: <identificador> = <tipo-base>[NumElem1, NumElem2]; • El término <NumElem1> determina el tamaño de la primera dimensión de la matriz, es decir, la cantidad de “filas” que tendrá. Los elementos de la segunda dimensión (columnas) están numerados en forma consecutiva, empezando en 0. El término <NumElem2> determina el tamaño de la segunda dimensión de la matriz, es decir, la cantidad de elementos del tipo base que contendrá. Dichos elementos estarán numerados en forma consecutiva, empezando en 0. Al igual que en el caso de arrays unidimensionales, es posible declarar una matriz y al mismo tiempo inicializar sus elementos con valores del tipo base. La sintaxis para hacer esto es la siguiente: <tipo-base>[M,N] identif = { { valor1-1, valor1-2, ..., valor1-N }, { valor2-1, valor2-2, ..., valor2-N },

..., { valorM-1, valorM-2, ..., valorM-N }

}; • Con los valores indicados entre llaves {} se inicializarán los MxN elementos de la matriz. • Los valores deben ser del <tipo-base> de la matriz.

Page 20: Manual Básico Programación

20

1.18.2 Uso de Matrices Los elementos de una matriz se pueden entender como un “casillero” o una “celda”, determinada por fila y columna. Para asignar un valor a un elemento de una matriz basta con escribir:

matriz[indice1,indice2] = valor; en donde matriz es el nombre de la variable y las expresiones indice1 e indice2 hacen referencia a la posición del elemento al que se le quiere asignar el valor. El nombre de una matriz también puede emplearse sin índice, bajo ciertas circunstancias, por ejemplo, para pasar la matriz completa como argumento a una función. 1.18.3 Obteniendo Dimensiones de una Matriz Al igual que en las matrices unidimensionales, el largo de la fila o columna de una matriz se puede obtener, en este caso con el método GetLength(dimension), donde dimension se refiere a filas, columnas, etc. En el caso de una matriz bidimensional, el largo de las filas se ve con GetLength(0), y de las columnas, con GetLength(1). 1.18.4 Ejemplos de uso de Matrices En todos los casos, matriz es el nombre de la variable declarada: 1. Declaración de una matriz de 50 filas de 20 enteros: int[,] matriz = new int[50,20]; 2. Declaración e inicialización de una matriz: int[,] matriz = { { 2, 5, 8 }, { 9, 1, 2 } }; // 2 filas, 3 col. 3. Asignando un valor a la primera posición de la segunda fila de una matriz de enteros: matriz[1,0] = 50; 4. Imprimiendo una matriz de 100x50 enteros mediante un ciclo for: int i, j; for (i=0; i<100; i++) {

for (j=0; j<50; j++) Console.Write("{0} ", matriz[i,j]); Console.WriteLi ne(“”);

} 5. Imprimiendo una matriz de NxN enteros mediante un ciclo for: int i, j; for (i=0; i<matriz.GetLength(0); i++) {

for (j=0; j< matriz.GetLength(1); j++) Console.Write("{0} ", matriz[i,j]); Console.WriteLi ne(“”);

} 1.18.5 Foreach en matrices En forma similar al caso de los arrays unidimensionales, es posible utilizar foreach() para recorrer los elementos de las celdas de una matriz. Sin embargo, foreach() hace un

Page 21: Manual Básico Programación

21

recorrido exhaustivo sin explicitar cuando se cambia de fila o de columna. Es decir, en este caso sirve para recorrer todo el contenido de una matriz, sin tener conocimiento explícito de las respectivas posiciones de las celdas. 1.19 Enumeraciones

Existe una versión simplificada de arrays, que se declaran con datos incluidos. Estos se utilizan esencialmente para listar opciones fijas para un programa. Por definición, todos los elementos de las enumeraciones son de tipo entero. En otras palabras, al declarar una enumeración, se está definiendo un conjunto de valores aceptados, dándole nombres más entendibles, y como consecuencia, el compilador dará aviso cuando se intente usar un valor no definido. La sintaxis para la declaración de una enumeración es la siguiente: enum <identificador> { <nombre1> = <valorEntero1>, <nombre2> = <valorEntero2>, ... <nombreN> = <valorEnteroN> } Por ejemplo, una útil enumeración se puede definir al utilizar una clase con un atributo que sólo puede tomar los valores Femenino o Masculino. Ejemplo: public enum Sexo { Femenino = 1, Masculino = 2 } class Persona {

string nombre; int edad; Sexo sexo;

Public void Mostrar() {

Console.WriteLine(“Nombre: {0}”, nombre); Console.WriteLine(“Edad: {0}”, edad); if ( sexo == Sexo.Masculino )

Console.WriteLine(“Sexo: Masculino”); else Console.WriteLine(“Sexo: Femenino”); } } Entre los beneficios de utilizar enumeraciones se cuentan: - Se hace más fácil de mantener el código, al permitir asegurar que las variables sólo reciben valores dentro de un rango definido, sin posibilidad de valores inválidos.

Page 22: Manual Básico Programación

22

- Las enumeraciones hacen al código más legible y entendible, permitiendo referenciar valores enteros con nombres más descriptivos, en lugar de números “oscuros y mágicos”.

Page 23: Manual Básico Programación

23

2. Unidad didáctica 2 – Estructuras estáticas y dinámicas 2.1 Introducción Los computadores se emplean frecuentemente para almacenar y recuperar grandes volúmenes de datos. Con su velocidad y facilidad de acceso, los computadores aventajan a otros medios de almacenamiento como el papel y las microfichas. Es importante estudiar la forma en que los computadores pueden almacenar los datos, de modo que su recuperación (búsqueda) sea rápida. Para lograr esto, y puesto que usualmente los usuarios requieren que los datos recuperados cuenten con algún orden particular, también es importante estudiar algoritmos para ordenar los datos almacenados. En todos los ejemplos se supondrá que existe un vector que contiene N datos. Para el caso de la búsqueda, el problema consiste en averiguar si un determinado dato está o no en el vector, y si es así, queremos saber su posición. Para el caso del ordenamiento, el problema consiste en ordenar el vector en forma ascendente (de menor a mayor). Si se quisiera trabajar con ordenamiento descendente los cambios serían mínimos. En este capítulo se presentan varios algoritmos de búsqueda y ordenamiento que difieren entre sí en cuanto a su complejidad y eficiencia. 2.2 Algoritmos de Búsqueda Simple 2.2.1 Búsqueda Lineal Algoritmo : Recorrer el vector de inicio a fin, comparando el dato buscado con cada elemento del arreglo. Implementación: Mediante una función que implemente el algoritmo descrito. La función retornará la posición en que se encontró el dato, o bien la cantidad de elementos en el vector en el caso en que no se encuentre (esto sería un subíndice inválido). /************************************************** ****************** * Funcion que realiza una busqueda lineal de un valor entero en un vector * de enteros. La funcion recibe como parametros el vector, la cantidad de * elementos que hay en el vector (N) y el valor entero que se busca. * Si se encuentra el valor entonces se devuelve la posicion de este, sino * se devuelve la cantidad de elementos del vector. *************************************************** *****************/ int BuscarLinealmente(int Numeros[], int N, int NumeroBuscado)

Page 24: Manual Básico Programación

24

{ /* Inicializar el indice para recorrer el vector de numeros */ int i = 0;

/* Recorrer el vector mientras no se llegue a su fin y no se encuentre el numero buscado */ while ((i < N) && (NumeroBuscado != Numeros[i])) i++; /* Retornar la posicion del numero o la cantidad de elementos */ return(i);

} 2.1.1 Ejemplo: Búsqueda lineal en vector de enteros /************************************************** ****************** * Ejemplo: Busqueda lineal en un vector de enteros. *************************************************** *****************/ using System; using System.IO; public class CSort {

public int[] mLeerVector(string archivo) {

StreamReader sr; int [] numeros; Console.WriteLine("Leyendo datos de entrada"); // Abrir el archivo try {

sr = new StreamReader(archivo); numeros = new int[Int32.Parse(sr.ReadLine())]; string str; for(int i = 0; (str = sr.ReadLine()) != null; i++) numeros[i] = Int32.Parse(str); sr.Close(); return(numeros);

} catch (Exception e) {

Console.WriteLine("Problema con apertura de I/O: "); Console.WriteLine(e.Message); Environment.Exit(1);

}

Page 25: Manual Básico Programación

25

return null; }

public void mMostrarVector(int[] vector) {

Console.WriteLine("Los contenidos del vector son..."); foreach(int i in vector)

Console.WriteLine("{0}",i); }

public int mBusquedaLineal(int[] Numeros, int NumeroBuscado) {

for(int i = 0; i < Numeros.Length; i++) if(Numeros[i] == NumeroBuscado) return i;

return -1;

} } public class CMain {

public static void Main(string [] args) {

CSort cs = new CSort(); Console.Write("Ingrese Nombre archivo: "); string archivo = Console.ReadLine(); int[] vector = cs.mLeerVector(archivo); cs.mMostrarVector(vector); Console.Write("Ingrese el número que desea buscar: "); int search = Int32.Parse(Console.ReadLine()); int pos = cs.mBusquedaLineal(vector,search); if(pos == -1)

Console.WriteLine("No se encontró el número {0}",search); else

Console.WriteLine("El numero {0} se encuentra en la posición {1}", search,pos);

} }

Page 26: Manual Básico Programación

26

Array de entrada: 10, 3, -5, 20, 0, 11, 4, 2, 9, -1, 90 Ejemplo de ejecución El vector de 10 enteros es: 3 -5 20 0 11 4 2 9 -1 90 Ingrese un número entero: 5 El 5 no esta en el vector. Leyendo datos de entrada El vector de 10 enteros es: 3 -5 20 0 11 4 2 9 -1 90 Ingrese un número entero: 11 El 11 esta en la posición 4. 2.3 Algoritmos de Ordenamiento 2.3.1 Ordenamiento por Selección Algoritmo recursivo escogiendo el mayor: Si el vector tiene un único elemento no se hace nada. En caso contrario, se elige el mayor de los elementos y se intercambia por el último (posición N-1). De esta forma, el último elemento estará en su posición correcta. Luego se ordena el subvector que tiene N-1 datos. /************************************************** ****************** * Programa con función recursiva que ordena vector de enteros por selección. * Si el vector tiene un solo elemento no se hace nada. De lo contrario, * se elige el mayor de los elementos y se intercambia por el ultimo del * vector (posicion N - 1). Luego se ordena el vector con N - 1 datos. *************************************************** *****************/ using System; using System.IO; public class CSelSort {

public int [] mLeerVector(string archivo) {

StreamReader sr; int [] numeros; Console.WriteLine("Leyendo datos de entrada"); try {

sr = new StreamReader(archivo); numeros = new int[Int32.Parse(sr.ReadLine())]; string str;

Page 27: Manual Básico Programación

27

for(int i = 0; (str = sr.ReadLine()) != null; i++) numeros[i] = Int32.Parse(str);

sr.Close(); return(numeros);

} catch (Exception e) {

Console.WriteLine("Problema con apertura de I/O: "); Console.WriteLine(e.Message); Environment.Exit(1);

}

return null; }

public void mMostrarVector(int [] vector) {

Console.WriteLine("Los contenidos del vector son..."); foreach(int i in vector) Console.WriteLine("{0}",i);

}

public void mOrdenarPorSeleccionRecursivo(int [] Numeros, int N) {

int i, PosMayor, aux; if (N > 1) {

/* Busca el mayor elemento */ PosMayor = 0; /* Supone que el mayor es el primero */ for (i=1; i<N; i++) {

if (Numeros[i] > Numeros[PosMayor]) PosMayor = i;

} /* Se lleva a cabo el intercambio */ aux = Numeros[N - 1]; Numeros[N - 1] = Numeros[PosMayor]; Numeros[PosMayor] = aux; /* Llamado recursivo con el subvector de N-1 elementos */ mOrdenarPorSeleccionRecursivo(Numeros, N - 1);

} }

}

Page 28: Manual Básico Programación

28

public class CMain {

public static void Main(string [] args) {

CSelSort ss = new CSelSort(); Console.Write("Ingrese Nombre archivo: "); string archivo = Console.ReadLine(); int [] vector = ss.mLeerVector(archivo); Console.WriteLine("Antes de ordenar..."); ss.mMostrarVector(vector); ss.mOrdenarPorSeleccionRecursivo(vector,vector.Length-1); Console.WriteLine("Despues de ordenar..."); ss.mMostrarVector(vector);

} } Array de entrada: 10, 3, -5, 20, 0, 11, 4, 2, 9, -1, 90 Ejemplo de ejecución C:\>ordsel entrada.txt Leyendo datos de entrada Antes de Ordenar El vector de 10 enteros es: 3 -5 20 0 11 4 2 9 -1 90 Despues de Ordenar El vector de 10 enteros es: -5 -1 0 2 3 4 9 11 20 90 2.3.2 Ordenamiento por Selección (versión iterativa) Algoritmo iterativo escogiendo el mayor: Se itera tantas veces como elementos hay en el vector. En cada iteración se elige el mayor de los elementos y se intercambia por el último. Se decrementa N para que en la siguiente iteración se trate el siguiente. El orden en que se van ubicando los elementos es el mismo que en el caso recursivo.

Page 29: Manual Básico Programación

29

using System; using System.IO; public class CSelSort {

public int [] mLeerVector(string archivo) {

StreamReader sr; int [] numeros; Console.WriteLine("Leyendo datos de entrada"); Try {

sr = new StreamReader(archivo); numeros = new int[Int32.Parse(sr.ReadLine())]; string str; for(int i = 0; (str = sr.ReadLine()) != null; i++) numeros[i] = Int32.Parse(str); sr.Close(); return(numeros);

} catch (Exception e) {

Console.WriteLine("Problema con apertura de I/O: "); Console.WriteLine(e.Message); Environment.Exit(1);

} return null;

}

public void mMostrarVector(int [] vector) {

Console.WriteLine("Los contenidos del vector son..."); foreach(int i in vector)

Console.WriteLine("{0}",i); }

public void mOrdenarPorSeleccionIterativo(int [] Numeros, int N) {

int i, PosMayor, aux; /* Itera tantas veces como elementos hay en el vector. */ /* En cada iteracion ubica el elemento en la posicion N */ while (N > 1) { /* Busca el mayor */

PosMayor = 0; /* Supone que el mayor es el primero */ for (i = 1; i < N; i++)

Page 30: Manual Básico Programación

30

if (Numeros[i] > Numeros[PosMayor]) PosMayor = i;

/* Lleva a cabo el intercambio */ aux = Numeros[N - 1]; Numeros[N - 1] = Numeros[PosMayor]; Numeros[PosMayor] = aux; N--; /* Decrementar cantidad de elementos a ordenar */

} }

} public class CMain {

public static void Main(string [] args) {

CSelSort ss = new CSelSort(); Console.Write("Ingrese Nombre archivo: "); string archivo = Console.ReadLine(); int [] vector = ss.mLeerVector(archivo); Console.WriteLine("Antes de ordenar..."); ss.mMostrarVector(vector); ss.mOrdenarPorSeleccionIterativo(vector,vector.Length-1); Console.WriteLine("Despues de ordenar..."); ss.mMostrarVector(vector);

} } Array de entrada 10, 3, -5, 20, 0, 11, 4, 2, 9, -1, 90 Ejemplo de ejecución Leyendo datos de entrada Antes de Ordenar El vector de 10 enteros es: 3 -5 20 0 11 4 2 9 -1 90 Después de Ordenar El vector de 10 enteros es: -5 -1 0 2 3 4 9 11 20 90

Page 31: Manual Básico Programación

31

2.4 Complejidad de Algoritmos El estudio de la complejidad de los algoritmos intenta obtener medidas que indiquen cómo será el comportamiento de los algoritmos ante variaciones en las características de los datos de entrada (por ejemplo, la cantidad de datos). El orden de ejecución de un algoritmo, denotado por O(expresión) indica cómo será el comportamiento del tiempo de ejecución del algoritmo ante variaciones en los parámetros de la expresión. Por ejemplo, cuando se dice que un algoritmo tiene orden lineal respecto al número de elementos de entrada, n, y denotado por O(n), se quiere expresar que el tiempo de ejecución del algoritmo se incrementará linealmente conforme se incremente el número de elementos de entrada. Puede deducirse entonces que los algoritmos más deseables son aquellos que cuenten con órdenes de ejecución logarítmicos, y los menos deseables son aquellos con órdenes de ejecución exponenciales. 2.4.1 Ordenamiento y Búsqueda Eficientes Cuando la cantidad de datos que se están manipulando es extremadamente grande, incluso un computador puede tener problemas para llevar a cabo búsquedas u ordenamientos en un tiempo razonable. El algoritmo de búsqueda lineal, como es de esperar, es de orden O(n), donde n es el número de elementos. El algoritmo de ordenamiento por selección es de

orden O(n2), donde n es el número de elementos. A continuación se estudiarán algoritmos que tienen complejidad O(log n).

Page 32: Manual Básico Programación

32

4.2 Comparación de algunas funciones de tiempo

n

log n

n log n

n2

n2 log n

1 0,00

0,0

1

0,0

2

0,69

1,4

4

2,8

5

1,61

8,0

25

40,2

10

2,30

23,0

100

230,3

20

3,00

59,9

400

1198,3

50

3,91

195,6

2500

9780,1

100

4,61

460,5

10000

46051,7

200

5,30

1059,7

40000

211932,7

500

6,21

3107,3

250000

1553652,0

1000

6,91

6907,8

1000000

6907755,3

2000

7,60

15201,8

4000000

30403609,8

5000

8,52

42586,0

25000000

212929829,8

10000

9,21

92103,4

100000000

921034037,2

20000

9,90

198069,8

400000000

3961395021,0

50000

10,82

540988,9

2500000000

27049445711,0

100000

11,51

1151292,5

10000000000

115129254649,7

200000

12,21

2441214,5

40000000000

488242905821,2

500000

13,12

6561181,7

250000000000

3280590844351,1

1000000

13,82

13815510,6

1000000000000

13815510557964,3

2000000

14,51

29017315,5

4000000000000

58034630954096,9

5000000

15,42

77124742,4

25000000000000

385623711759959,0

Page 33: Manual Básico Programación

33

2.5 Algoritmos Eficientes de Búsqueda 2.5.1 Búsqueda Binaria La búsqueda binaria puede aplicarse únicamente a un conjunto ordenado de datos. Por esto, supondremos que el vector que contiene los datos se encuentra ordenado ascendentemente (es decir, de menor a mayor). 2.5.1.1 Algoritmo Se analiza la posición del medio. Si ahí se encuentra el dato buscado, terminamos. Si el dato buscado es menor, se busca en el subvector menor, y si es mayor, se busca en el subvector mayor (aprovechando el hecho de que el vector está ordenado). Cada vez que se hace uno de estos análisis, se divide el vector en dos (de ahí su nombre, búsqueda binaria), y se descarta una de las mitades. Intuitivamente puede verse que la cantidad de trabajo necesaria para encontrar un elemento (o saber que no está), es mucho menor que para el caso de la búsqueda lineal.

/************************************************** *********** * Método que realiza una búsqueda binaria de un elemento en un vector * de enteros. La función recibe como parámetros el vector, el índice de * inicio y de fin del vector, y el valor entero que se desea buscar. * Si el índice de inicio es mayor que el de fin se retorna que no se * encontró. En otro caso se divide el vector a la mitad, si el numero * buscado es menor que el que esta en la mitad se busca entonces en la * mitad inferior, si el numero buscado es mayor que el que esta en la * mitad se busca entonces en la mitad superior y en otro caso, el numero * en la mitad es el numero buscado.

*************************************************** *****************/ public int BusquedaBinaria(int[] Numeros, int Inici o, int Fin, int NumeroBuscado) {

if (Inicio > Fin) return(-1); // -1 representa: “NO ENCONTRADO”

else {

int pos = (Inicio + Fin) / 2; if (NumeroBuscado < Numeros[pos])

return(BusquedaBinaria(Numeros, Inicio, pos-1, NumeroBuscado));

else if (NumeroBuscado > Numeros[pos]) return(BusquedaBinaria(Numeros, pos + 1, Fin, NumeroBuscado));

else return(pos); }

}

Page 34: Manual Básico Programación

34

5.1.3 Versión Iterativa También es posible implementar el algoritmo de búsqueda binaria iterativamente, reemplazando la recursividad por un ciclo: /************************************************** ******************* * Método que realiza una búsqueda binaria de un valor entero en vector * de enteros. Se reciben como parámetros el vector, el índice de * inicio y de fin del vector, y el valor entero que se desea buscar. * Mientras el índice de inicio sea menor o igual que el de fin se divide * vector a la mitad, si el numero buscado es menor que el que esta en la * mitad se cambia el índice de fin para la posición anterior a la mitad y * se vuelve a iterar, si el numero buscado es mayor que el que esta en la * mitad se cambia índice de inicio para la posición posterior a la mitad * y se vuelve a iterar, y sino, el numero en la mitad es el buscado. * Si el índice de inicio se hace mayor que el de fin entonces se retorna * como No Encontrado. *************************************************** ******************/ int BusquedaBinariaIterativa(int[] Numeros, int Inicio, int Fin, int NumeroBuscado) {

while (Inicio <= Fin) {

int pos = (Inicio + Fin) / 2; if (NumeroBuscado < Numeros[pos])

Fin = pos - 1;

else if (NumeroBuscado > Numeros[pos]) Inicio = pos + 1;

else return pos;

} return -1; // -1 indica “no encontrado” }

Page 35: Manual Básico Programación

35

2.5.2 Algoritmo de Ordenamiento: Quicksort El algoritmo Quicksort fue desarrollado en 1962 por C.A.R. Hoare, antes de que se implementaran los primeros lenguajes con capacidad para ejecutar funciones recursivas.

El algoritmo, aplicado a un subvector vec[a ... b] es:

1. Elegir un elemento x cualquiera del vector, entre a y b. El elemento x se llama

pivote. Usualmente se elige el elemento que está en el medio.

2. Particionar el vector en dos subvectores: vec[a ... p] y vec[p+1 ... b], intercambiando elementos de modo que todos los elementos que son menores que x estén a la izquierda y todos los mayores que x estén a la derecha.

3. Aplicar Quicksort al subvector vec[a ... p]

4. Aplicar Quicksort al subvector vec[p+1 ... b]

La función que implementa el algoritmo Quicksort podría implementarse de la siguiente manera:

/************************************************** ****************** * Método recursivo que ordena un vector de enteros mediante Quicksort. * Si el vector no tiene más de dos elementos no se hace nada. De lo * contrario, se particiona el vector y se aplica el método a los dos * subvectores que resultan de la partición. * Se reciben como parámetros el vector y el índice inicial y final.

*************************************************** *****************/ void QuickSort(int[] Numeros, int Inicio, int Fin) {

int p;

if (Inicio < Fin) {

p = ParticionarVector(Numeros, Inicio, Fin); QuickSort(Numeros, Inicio, p); QuickSort(Numeros, p + 1, Fin);

} }

Page 36: Manual Básico Programación

36

La función que lleva a cabo la selección del pivote y el particionamiento del vector sería:

/************************************************** *************

* Función para particionar un vector en dos subvectores, intercambiando * elementos de modo que todos los menores que el pivote estén a la * izquierda y los mayores a la derecha. * Se reciben como parámetros el vector y el índice inicial y final. * Se retorna el índice para particionar el vector (posición del pivote).

*************************************************** ************/ int ParticionarVector(int[] Numeros, int Inicio, in t Fin) {

int pivote = Numeros[(Inicio + Fin) / 2]; int izq = Inicio - 1; int der = Fin + 1; int aux;

while (izq < der) {

// Busca el 1er. elemento de la derecha que haya que intercambiar do {

izq++; } while (Numeros[izq] < pivote);

// Busca el 1er. elemento de la izq. que haya que intercambiar do {

der--; } while (Numeros[der] > pivote);

if (izq < der) {

// Lleva a cabo el intercambio aux = Numeros[izq]; Numeros[izq] = Numeros[der]; Numeros[der] = aux;

} } return(der);

} 2.6 Pilas y colas (Stack & Queue) Las pilas (Stack) y las colas (Queue) son dos colecciones muy similares entre si ya que solo varia la forma en que guardan y extraen los elementos que contienen. En ciertas cosas estas dos colecciones se parece a un ArrayList, como por ejemplo que soporta el redimensionamiento automático y que los elementos son almacenados como objetos (System.Object). Pero también tienen algunas diferencias, como por ejemplo que no se puede cambiar su capacidad y no se puede acceder a sus elementos a través de índices. En algunas ocasiones, es importante tener un control sobre el orden en que los elementos son ingresados y obtenidos de la colección. Por esta razón existen las

Page 37: Manual Básico Programación

37

colecciones Stack y Queue. Como se menciono anteriormente, en estas colecciones NO es posible acceder aleatoriamente mediante índices a sus elementos, sino que es necesario utilizar un método encargado de extraer un elemento a la vez. Pero ¿cual elemento? Precisamente en la respuesta a esa pregunta radica la diferencia entre estas dos colecciones. La pila (Stack), es una colección en la que todo nuevo elemento se ingresa al final de la misma, y únicamente es posible extraer el último elemento de la colección. Por este comportamiento, el Stack es conocido como una colección LIFO (Last Input Fisrt Output) ya que siempre el último elemento ingresado a la colección, será el primero en salir. Quizás la mejor manera de recordar el comportamiento de un Stack, es asociándolo con una “pila” de platos en donde cada plato esta encima del otro y en caso de querer ingresar un plato a la pila, lo que se debe hacer es ponerlo encima del último plato. Luego cuando se quiere sacar un plato de la pila, solo podemos coger el último plato. La cola (Queue), tiene el comportamiento contrario a la pila. Todo nuevo elemento se agrega al principio de la colección y solo se puede extraer el último elemento. Por esta razón, la cola se conoce como una colección FIFO (Fisrt Input First Output) ya que el primer elemento que ingresa a la cola es el primer elemento que sale. Para recordar este comportamiento se puede asociar la Queue con la fila que se debe hacer en un banco para realizar una consignación. En ese caso, el cajero atiende en el orden en que llegan las personas a la cola. Las colecciones Stack y Queue se encuentran en el espacio de nombres System.Collections como todas las colecciones no genéricas. Para implementar cada una de ellas se debe utilizar la clase Stack y Queue respectivamente y utilizar sus métodos que ofrecen la posibilidad de agregar elementos a la colección y extraer elementos según el comportamiento de la colección que se este utilizando.

Como se ve en la figura anterior, para agregar elementos a una pila se debe utilizar el método Push que recibe como parámetro un Object. Mientras que en la cola se debe utilizar el método Enqueue (encolar). Ambos métodos, incrementan automáticamente la capacidad de la colección. Para obtener un elemento de la colección, contamos con dos opciones diferentes: 1. Obtener el elemento indicado según el comportamiento de la colección sin quitarlo de la colección. Esto se logra mediante el método Peek de cada colección. 2. Obtener un elemento de la colección, quitándolo de la misma. Esto se logra mediante el método Pop de la pila (Stack) o el método Dequeue de la cola (Queue).

Page 38: Manual Básico Programación

38

Ejemplo del Stack:

Resultado:

Ejemplo del Queue:

Este par de colecciones deben ser usadas cuando nos interesa tener control sobre el orden en que los elementos son obtenidos de las mismas. La pila se debe usar cuando queremos obtener los elementos en el orden inverso al cual fueron ingresados. Mientras que la cola debe ser utilizada cuando queremos obtener los elementos en el mismo orden que fueron ingresados.