Lezione 10: Array Multidimensionali e Ordinamento

Soluzione di Esercizi

Esercizio 9.1 Scrivere una funzione double *duparrayD(double A[], int n) che ritorna un duplicato (tramite array allocato dinamicamente) dell'array A.

#include <stdlib.h>

/* Ritorna una copia (allocata dinamicamente) dell'array A 
 * di dimensione n */
double *duparrayD(double A[], int n) {
    double *dup = malloc(n*sizeof(double));
    for (int i = 0 ; i < n ; i++)
        dup[i] = A[i];
    return dup;
}

Esercizio 9.2 Scrivere una funzione char *concat(char *s1, char *s2) che ritorna una nuova stringa che contiene la concatenazione delle stringhe s1 e s2. Ad esempio, se s1 e' "Prima parte" e s2 e' "Seconda parte" allora la funzione ritorna la stringa "Prima parteSeconda parte".

#include <stdlib.h>
#include <string.h>

/* Ritorna, in una stringa allocata dinamicamente, la concatenazione 
 * delle stringhe s1 e s2. */
char *concat(char *s1, char *s2) {
    int n1 = strlen(s1), n2 = strlen(s2);
    char *str = malloc((n1 + n2 + 1)*sizeof(char));
    for (int i = 0 ; i < n1 ; i++)
        str[i] = s1[i];
    for (int i = 0 ; i < n2 ; i++)
        str[n1 + i] = s2[i];
    str[n1 + n2] = '\0';
    return str;
}

Array Multidimensionali

Il C supporta l'uso di array multidimensionali allocati staticamente con la sintassi

<tipo> <nome>[<dimensione_1>][<dimensione_2>];

Ad esempio, int M[20][30] dichiara un array di dimensioni 20 per 30. Gli array multidimensionali posso essere inizializzati con una sintassi simile agli array standard, cioè

<tipo> <nome>[d_1][d_2] = {{v_11, v_12}, {v_21, v_22}};

Notate che la prima dimensione e' opzionale.

Rappresentazione in memoria

Gli array multidimensionale sono rappresentati in memoria come un gruppo di celle contigue disposte in ordine row major. Ad esempio per il vettore int A[3][5], la rappresentazione in memoria e'

       ___riga 0____  ___riga 1____  ___riga 2____
      /             \/             \/             \
. . . ---------------------------------------------- . . .
      |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
. . . ---------------------------------------------- . . .
      |              |              |
     A[0]           A[1]           A[2]

Allocazione Dinamica

In C, non e' possibile allocare dinamicamente un tipo equivalente ad un array multidimensionale. Si possono allocare tipi che hanno un sottoinsieme delle proprietà degli array multidimensionali, ma non ne possiedono tutte le proprietà. Questo rende

Array e Puntatori

Data la rappresentazione in memoria precedente, si può interpretare un array multidimensionale come un array i cui elementi sono array. Ad esempio le righe di un array bidimensionale <tipo> A[d1][d2] sono array unidimensionali di cui si può ottenerne l'indirizzo usando <tipo>* ptr = A[i]. Gli elementi puntati dalle righe della matrice e dal puntatore sono equivalenti (cioè puntano agli stessi elementi): ptr[j] ≡ A[i][j].

Si può' ottenere un puntatore al primo elemento di un array multidimensionale dichiarandone appropriatamente il tipo come <tipo> (*ptr) [d2], che indica un puntatore ptr ad un array unidimensionale di dimensione d2, cioè al tipo delle righe dell'array multidimensionale.

Esempi

Funzione void magicsquare(int n, int Q[n][n]) che riempie la matrice Q con un quadrato magico di ordine n (solo se n è dispari). La semplice procedura per la costruzione di quadrati magici di ordine dispari è qui.

                               quadrato chiuso a toro
                                                  -----           -----
      0   1   . . .  n/2  . . .  n-1             |     |         |     |
     -------- . . . ----- . . . ----             | k+1 |         |  X  |
  0 |   |   |       | 1 |       |   |            |     |         |     |
    |-------- . . . ----- . . . ----|       -----|-----     -----|-----
  1 |                               |      |     |         |     |
    .                               .      |  k  |         |  k  |
    .                               .      |     |         |     |
    .                               .       -----          |-----|
    |                               |                      |     |
    |                               |                      | k+1 |
n-1 |                               |                      |     |
     -------- . . . ----- . . . ----                        -----
void magicsquare(int n, int Q[n][n]) {
    if (n < 1 || (n % 2) != 1) return;
    for (int r = 0 ; r < n ; r++)
        for (int c = 0 ; c < n ; c++)
            Q[r][c] = 0;
    int r = 0, c = n/2;
    for (int k = 1 ; k <= n*n ; k++) {
        Q[r][c] = k;
        r = (r + n - 1) % n;
        c = (c + 1) % n;
        if (Q[r][c] != 0) {
            r = (r + 2) % n;
            c = (c + n - 1) % n;
        }
    }
}

Funzione float *maxrows(int nr, int nc, float M[nr][nc]) che ritorna un array, allocato dinamicamente, che contiene i valori massimi delle righe della matrice M.

#include <stdlib.h>

float *maxrows(int nr, int nc, float M[nr][nc]) {
    float *rows = malloc(nr*sizeof(float));
    for (int i = 0 ; i < nr ; i++) {
        rows[i] = M[i][0];
        for (int j = 1 ; j < nc ; j++)
            if (M[i][j] > rows[i])
                rows[i] = M[i][j];
    }
    return rows;
}

Algoritmi di Ordinamento

A seguito sono due algoritmi di ordinamento semplice. L'operazione primitiva di questi algoritmi e la scambia del valore di due variabili che si puo' implementare con

// scambia i valori di *a e *b
void swapint(int *a, int *b) {    
    int c = *a;
    *a = *b;
    *b = c;
}

Nel bubble-sort ogni coppia di elementi adiacenti della lista viene comparata e se gli elementi sono nell'ordine sbagliato vengono invertiti. L'algoritmo scorre tutta la lista finché non vengono più eseguiti scambi, situazione che indica che la lista è ordinata.

Nel selection-sort l'array e' suddiviso in due parti: la parte gia' ordinata, che occupa le prime posizioni dell'array, e la parte da ordinare, che costituisce la parte restante dell'array. L'algoritmo costruisce la sequenza ordinata un elemento alla volta, che viene determinato trovando il numero minore nella sequenza non ancora ordinata.

// ordina in senso crescente l'array V di dimensione n
void selectionsort(int V[], int n) {    
    for (int i = 0 ; i < n ; i++) {
        int indmin = i;
        for (int j = i + 1 ; j < n ; j++)
            if (V[j] < V[indmin])
                indmin = j;
        swapint(&V[i], &V[indmin]);          
    }
}

// ordina in senso crescente l'array V di dimensione n
void bubblesort(int V[], int n) {    
    int scambi;
    do {
        scambi = 0;
        for (int i = 0 ; i < n - 1 ; i++)
            if (V[i] > V[i + 1]) {
                swapint(&V[i], &V[i+1]);
                scambi++;
            }
    } while (scambi > 0);
}

Il seguente programma puo' essere usato per testare le funzioni di ordinamento.

int main() {
    int n;
    printf("Quanti interi: ");
    scanf("%d", &n);
    int V[n];
    printf("Inserire %d interi: ", n);
    for (int i = 0 ; i < n ; i++)
        scanf("%d", &V[i]);
    selectionsort(V, n);
    // o bubblesort(V, n);
    for (int i = 0 ; i < n ; i++)
        printf("%d ", V[i]);
    printf("\n");
}

Esercizi

Esercizio 10.1 Scrivere una funzione int ismagicsquare(int n, int Q[n][n]) che verifica se Q contiene un quadrato magico di ordine n (ovvero, contiene tutti gli interi da 1 a n2, e tutte le righe, colonne e diagonali hanno la stessa somma).

Esercizio 10.2 Scrivere una funzione void printmatrix(int nr, int nc, float M[nr][nc]) che stampa la matrice M ben formattata. Ad esempio se la matrice è nr = 2, nc = 3 e M = {{1.06, 220.77, 12.3}, {0.01, 1.3, 345.0}}, allora la funzione la stampa così

1.06  220.77    12.30
0.01    1.30   345.00

Esercizio 20 Scrivere una funzione void strsort(char *s) che ordina i caratteri della stringa s. Ad esempio, se s = "stringa", allora la funzione la ordina così "aginsrt".

Esercizio 21 Scrivere una funzione void sortstrings(char *sA[], int n) che ordina le stringhe dell'array sA (tramite la funzione strcmp()).