Lezione in Aula del 23 Novembre 2001 & lezioni in laboratorio nella settimana successiva

Ancora esercizi con gli array


COSA E' STATO FATTO A LEZIONE

Questo documento contiene un RIASSUNTO degli argomenti presentati a lezione. Per eventuali chiarimenti rivolgersi ai docenti.

Ancora esercizi sugli array (di interi)

Realizzare una classe MyArrayOfIntUtil che fornisca alcuni metodi static sugli array di int.

Quiesta classe fornisce un insieme di metodi statici: non e' pensata per essere istanziata.

Riportiamo nel seguito lo "scheletro" della classe (sara' vostra cura riempirlo).

class MyArrayOfIntUtil
{
   /*
      In questo modo (cioe' dichiarando il costruttore come private}
      si impedisce di 
      costruire oggetti appatenenti a questa classe
   */
   private MyArrayOfIntUtil(){}

   /*
      AI: a != null
      AF: restituisce il numero di volte che "e occorre in a"
   */
   public static int howMany(int[] a, int e)
   {
     // NOTA: questo non e' il vero corpo del metodo,
     //       e' un corpo "fittizio" il cui unico
     //       scopo e' quello di permettere di compilare
     //       la classe, e provare ad eseguire,
     //       di in volta in volta,
     //       i metodi che si scrivono
     return 0;
   }

   /*
      AI: a != null
      AF: restituisce un nuovo array ottenuto da a eliminando 
          la prima occorrenza di e
   */
   public static int[] deleteFirst(int[] a, int e)
   {
     // NOTA: questo non e' il vero corpo del metodo,
     //       e' un corpo "fittizio" il cui unico
     //       scopo e' quello di permettere di compilare
     //       la classe, e provare ad eseguire,
     //       di in volta in volta,
     //       i metodi che si scrivono
     int[] c = new int[1];
     c[0] = 0;
     return c;
    }
}   

Scriviamo il corpo del primo metodo:

   /*
      AI: a != null
      AF: restituisce il numero di volte che "e occorre in a"
   */
   public static int howMany(int[] a, int e)
   { 
      if (a==null) throw new IllegalArgumentException();

      int counter = 0;

      /*
         0 <= i <= a.length 
         AND counter = numero di occorrenze di e in a[0..i-1]
      */
      for (int i=0; i < a.length; i++)
          if (a[i]==e) counter++;

      return counter;
    }

L'esempio precedente e' relativamente semplice: per scrivere il corpo del metodo non e' necessario scrivere altri metodi.

Consideriamo invece il metodo

   /*
      AI: a != null
      AF: restituisce un nuovo array, ottenuto da a eliminando 
          la prima occorrenza di e
   */
   public static int[] deleteFirst(int[] a, int e)
Da un'analisi del problema ci si rende conto che la scrittura di tale metodo richiede di effettuare operazioni complesse come ad esempio:
"individuare il minimo indice i di a tale che a[i]==x",
"copiare a[0],...,a[i-1] in b[0],...,b[i-1]",
"copiare a[i+1],...,a[a.lenght-1] in b[i],...,b[b.lenght-1]"
(dove b.lenght==a.lenght-1).

In tal caso e' bene scrivere metodi che si occupino di fare queste cose. Tali metodi dovranno essere "il piu' generici possibile" (in modo da poter essere riutilizzati anche in altre circostanze).

A tale scopo, a lezione abbiamo considerato i seguenti metodi:

   /*
      AI: a != null
      AF: restituisce il minimo i tale che a[i]==x,
          -1 altrimenti (se a[i]!=x per tutti gli i)
   */
   public static int indexOfFirst(int[] a, int x)

   /*
      AI: a,b != null 
          AND n >= 0 
          AND firstA+n <= a.length 
          AND firstB+n <= b.length 
      AF: copia a[firstA], a[firstA+1],..., a[firstB+n-1]
          in
          b[firstB], b[firstB+1],..., b[firstB+n-1]
   */
   public static void copy(int[] a, int firstA, int n, int[] b, int firstB)
Supponendo di avere a disposizione tali metodi, possiamo scrivere il metodo deleteFirst:
   /*
      AI: a != null
      AF: restituisce un nuovo array, ottenuto da a eliminando 
          la prima occorrenza di e
   */
   public static int[] deleteFirst(int[] a, int e)
   { 
      if (a==null) 
          throw new IllegalArgumentException();
      
      int i = indexOfFirst(a,e);
      int[] c;

      if (i ==-1)
         {
          c = new int[a.length];
          copy(a, 0, a.lenght, c, 0);
         }
      else
         {
          c = new int[a.length-1;]
          copy(a, 0, i, c, 0);
          copy(a, i+1, a.length-(i+1), c, i);
         }
      
      return c;
     }
Ora possiamo completare il tutto scrivendo il metodo indexOfFirst e il metodo copy:
   /*
      AI: a != null
      AF: restituisce il minimo i tale che a[i]==x,
          -1 altrimenti (se a[i]!=x per tutti gli i)
   */
   public static int indexOfFirst(int[] a, int x)
   {
      if (a==null) throw new IllegalArgumentException();

      /*
         0 <= i <= a.length 
         AND per ogni j in {0,..,i-1}. a[j]!=x
      */
      for (int i=0; i < a.length; i++)
          if (a[i]==x) return i;

      return -1;
    }
     

   /*
      AI: a,b != null 
          AND n >= 0 
          AND firstA+n <= a.length 
          AND firstB+n <= b.length 
      AF: copia a[firstA], a[firstA+1],..., a[firstB+n-1]
          in
          b[firstB], b[firstB+1],..., b[firstB+n-1]
   */
   public static void copy(int[] a, int firstA, int n, int[] b, int firstB)
   {
      if ((a==null) 
          || (b==null) 
          || (firstA+n > a.length) 
          || (firstB+n > b.length))
         throw new IllegalArgumentException();

      /*
         0 <= i <= n 
         AND per ogni j in {0,..,i-1}. 
             a[firstA+j] e' stato copiato in b[firstB+i]
      */
      for (int i=0; i < n; i++)
         b[firstB+i] = a[firstA+i];
   }       

Ricerca binaria su un vettore ordinato

   /*
      AI: a != null
      AF: restituisce un indice i tale che a[i]==x,
          -1 altrimenti (se a[i]!=x per tutti gli i)
   */
   public static int binarySearch(int[] a, int x)
   {
    int lo=0;
    int hi=a.length-1;

    /*
      IC: se esiste j tale che a[j]==x, allora lo <= j <= hi
    */
    while (lo <= hi)
    {
       int i = (hi+lo)/2;
       if (a[i] == x) return i;
       else 
       if (a[i] < x) lo=i+1;
       else 
          hi=i-1;
     }
     
     return -1;
    }

ESERCIZI DA SVOLGERE (assegnati al termine della Lezione in Aula)

Per eventuali chiarimenti rivolgersi ai docenti.

  1. Completare la classe MyArrayOfIntUtil, scrivendo il body dei metodi di seguito elencati.
       /*
          AI: a != null
          AF: restituisce un nuovo array ottenuto, da a eliminando
              tutte le occorrenze di e
       */
       public static int[] deleteAll(int[] a, int e)
       
       /*
          AI: a != null
          AF: restituisce un nuovo'array ottenuto, da a eliminando 
              le prime n occorrenze di e
       */
       public static int[] deleteFirst(int[] a, int e, int n)
    
       
       /*
          AI: a != null
          AF: restituisce un nuovo'array ottenuto, da a eliminando 
              l'ultima occorrenza di e
       */
       public static int[] deleteLast(int[] a, int e)
    
       /*
          AI: a != null
          AF: restituisce un nuovo'array ottenuto, da a eliminando 
              le ultime n occorrenze di e
       */
       public static int[] deleteLast(int[] a, int e, int n)
    
       /*
          AI: a != null
          AF: restituisce un nuovo array, ottenuto da a eliminando 
              tutte le duplicazioni di elementi,
              preservando la prima occorrenza di ciascun elemento
              (esempio:
               semplify({4,5,3,4,1}) restituisce {4,5,3,1})
       */
       public static int[] simplify(int[] a, int e, int n)
    
       /*
          AI: a,b != null
          AF: restituisce un nuovo array, ottenuto da a eliminando 
              tutte le occorrenze degli elementi di b
              (se un elemento occorre piu' volte in a,
               si devono eliminare tutte le sue occorrenze)
              (esempio:
              add({4,5,3,4,1},{3,7,4,7,1,1}) restituisce {5})
       */
       public static int[] deleteAll(int[] a, int b[])
    
       /*
          AI: a,b != null
          AF: restituisce un nuovo array, ottenuto da a aggiungendo in coda
              gli elementi di b
              che non accorrono in a 
              (se un elemento occorre piu' volte in b,
               lo si deve aggiungere una volta sola)
              (esempio:
              add({4,5,3,4,1},{5,3,7,8,7,1}) restituisce {4,5,3,1,7,8})
       */
       int[] add(int[] a, int[] b)