Zum Inhalt
wannacode

Zweidimensionale Arrays

Kurz gesagt: Ein zweidimensionales Array (Matrix) speichert Werte in Zeilen und Spalten: Jedes Element hat zwei Indizes, Zeile und Spalte, und verschachtelte Schleifen gehen durch alle.

Problem

Drei Wochen, drei Arrays

Ein Café notiert, wie viele Kaffees es an jedem Werktag verkauft hat. Für eine Woche ist das Array aus der letzten Lektion genau das Richtige. Für drei Wochen legst du drei Arrays an:

int[] week1 = { 42, 38, 51, 47, 60 };
int[] week2 = { 45, 40, 49, 52, 63 };
int[] week3 = { 39, 44, 50, 48, 58 };
int tuesday = week1[1] + week2[1] + week3[1];
Console.WriteLine("Dienstag: " + tuesday);
Ausgabe
Dienstag: 122

Jetzt fragt die Chefin, wie viel an allen Dienstagen des letzten Jahres verkauft wurde. Das sind 52 Wochen, also 52 Arrays und eine Summe mit 52 Summanden. Und eine Schleife kann nicht durch week1, week2, week3 gehen: Das sind verschiedene Namen, keine Indizes.

Die Daten sind eine Tabelle: Die Wochen sind Zeilen, die Tage sind Spalten. Du brauchst ein Array, das eine Tabelle ist.

Die Idee

Ein zweidimensionales Array: Zeile und Spalte

Ein Array, in dem jeder Wert eine Zeile und eine Spalte hat, heißt zweidimensionales Array. Oft nennt man es auch Matrix.

int[,] sales =
{
{ 42, 38, 51, 47, 60 },
{ 45, 40, 49, 52, 63 },
{ 39, 44, 50, 48, 58 }
};
Console.WriteLine(sales[1, 0]);
Console.WriteLine(sales.GetLength(0) + " Wochen, " + sales.GetLength(1) + " Tage");
Ausgabe
45
3 Wochen, 5 Tage
  • int[,] bedeutet eine Tabelle aus ganzen Zahlen. Das Komma in den Klammern sagt, dass es zwei Indizes gibt. Jedes innere { … } ist eine Zeile.
  • sales[1, 0] ist die Zeile mit Index 1 (die zweite Woche) und die Spalte mit Index 0 (Montag). Zuerst kommt die Zeile, dann die Spalte. Auch hier zählt man ab 0.
  • sales.GetLength(0) ist die Anzahl der Zeilen, sales.GetLength(1) die Anzahl der Spalten. Die Zahl in den Klammern sagt, nach welcher Dimension du fragst: 0 für die Zeilen, 1 für die Spalten.

So eine Tabelle heißt rechteckiges Array: Alle Zeilen haben dieselbe Länge.

Eine leere Tabelle erzeugst du mit ihren Maßen: new int[52, 5] sind 52 Zeilen mit je 5 Zahlen, alle 0.

Genauer, für Neugierige

C# hat noch eine zweite Art: das verzweigte Array (jagged), int[][]. Es ist ein Array von Arrays, und jede Zeile kann unterschiedlich lang sein. Die Indizes stehen in getrennten Klammern, shifts[1][0], und die Länge einer Zeile ist shifts[1].Length:

int[][] shifts = new int[3][];
shifts[0] = new int[] { 8, 8 };
shifts[1] = new int[] { 6, 6, 6 };
shifts[2] = new int[] { 12 };
Console.WriteLine(shifts[1].Length + " " + shifts[2].Length);
Ausgabe
3 1

Nimm int[,], wenn die Daten eine echte Tabelle sind (Wochen und Tage, Reihen und Plätze in einem Saal). Nimm int[][], wenn die Zeilen unterschiedlich lang sind.

sales.Length ist bei int[,] die Anzahl aller Zellen (3 · 5 = 15), nicht die Anzahl der Zeilen. sales.Rank ist die Anzahl der Dimensionen (2).

Sieh es dir an

Die ganze Tabelle auf dem Bildschirm

Um durch jede Zelle zu gehen, brauchst du zwei Schleifen, eine in der anderen. Verschachtelte Schleifen kennst du schon von den Sternchen: Die äußere wählt die Zeile, die innere geht durch die Spalten dieser Zeile.

for (int week = 0; week < sales.GetLength(0); week++)
{
for (int day = 0; day < sales.GetLength(1); day++)
{
Console.Write($"{sales[week, day],5}");
}
Console.WriteLine();
}
Ausgabe
42 38 51 47 60
45 40 49 52 63
39 44 50 48 58

Die äußere Schleife läuft 3-mal, einmal für jede Woche. In jeder ihrer Runden läuft die innere 5-mal. Insgesamt 15 Zellen, in der Reihenfolge, in der du eine Tabelle liest: von links nach rechts, von oben nach unten.

$"{x,5}" gibt die Zahl in einem Feld von 5 Zeichen aus, rechtsbündig. So stehen die Spalten genau untereinander. Nach jeder Zeile wechselt WriteLine() in eine neue Zeile.

Probier es aus

Welche Zelle?

Im Notenbuch ist jede Zeile ein Schüler und jede Spalte ein Fach.

Was gibt das Programm aus?

int[,] grades =
{
{ 5, 3, 4, 6 },
{ 3, 4, 6, 5 },
{ 6, 2, 5, 4 }
};
Console.WriteLine(grades[1, 2]);
Antwort

Die Idee

Summen pro Zeile und pro Spalte

Wie viele Kaffees wurden in jeder Woche verkauft? Das ist die Summe jeder Zeile. Die Summe beginnt für jede neue Zeile bei 0, deshalb steht total innerhalb der äußeren Schleife:

for (int week = 0; week < sales.GetLength(0); week++)
{
int total = 0;
for (int day = 0; day < sales.GetLength(1); day++)
{
total += sales[week, day];
}
Console.WriteLine("Woche " + (week + 1) + ": " + total);
}
Ausgabe
Woche 1: 238
Woche 2: 249
Woche 3: 239

int[,] sales =
{
{ 42, 38, 51, 47, 60 },
{ 45, 40, 49, 52, 63 },
{ 39, 44, 50, 48, 58 }
};
for (int week = 0; week < sales.GetLength(0); week++)
{
int total = 0;
for (int day = 0; day < sales.GetLength(1); day++)
{
total += sales[week, day];
}
Console.WriteLine("Woche " + (week + 1) + ": " + total);
}
Schritt für Schritt, in Worten
  1. Die Tabelle: 3 Wochen (Zeilen) mit je 5 Tagen (Spalten).
  2. week = 0: total beginnt wieder bei 0.
  3. Die innere Schleife geht durch die Tage: 42 + 38 + 51 + 47 + 60 = 238.
  4. Sie gibt „Woche 1: 238“ aus.
  5. week = 1: total beginnt wieder bei 0.
  6. Die innere Schleife geht durch die Tage: 45 + 40 + 49 + 52 + 63 = 249.
  7. Sie gibt „Woche 2: 249“ aus.
  8. week = 2: total beginnt wieder bei 0.
  9. Die innere Schleife geht durch die Tage: 39 + 44 + 50 + 48 + 58 = 239.
  10. Sie gibt „Woche 3: 239“ aus.

Wie viel wurde am Montag verkauft, am Dienstag und so weiter? Das ist die Summe jeder Spalte. Die Tabelle bleibt dieselbe, nur die Reihenfolge der Schleifen ändert sich: Außen steht der Tag, innen stehen die Wochen. Du schreibst es auf der nächsten Karte fertig.

Welche Schleife außen steht, entscheidet, was du addierst. Die Indizes in sales[week, day] wandern nicht: Zuerst kommt immer die Zeile.

Probier es aus

Summe pro Tag

Die äußere Schleife geht durch die Tage, die innere durch die Wochen. Was gehört in die Lücke?

string[] days = { "Mo", "Di", "Mi", "Do", "Fr" };
for (int day = 0; day < sales.GetLength(1); day++)
{
    int total = 0;
    for (int week = 0; week < sales.GetLength(0); week++)
    {
        total += sales[, day];
    }
    Console.WriteLine(days[day] + ": " + total);
}

Probier es aus

Finde den Fehler

Der Saal hat 3 Reihen mit je 5 Plätzen: 1 ist belegt, 0 ist frei. Es gibt 7 freie Plätze, aber das Programm sagt 5. In welcher Zeile steckt der Fehler?

Wähle die Zeile mit dem Fehler.

Die Idee

Reservierung: Menschen zählen ab 1

Ein Kinobesucher will Reihe 3, Platz 2. Für ihn ist die erste Reihe die Reihe 1. Für das Array ist sie die Reihe 0. Deshalb ziehst du von den Zahlen, die ein Mensch eingibt, 1 ab, bevor du sie als Indizes verwendest:

int row = int.Parse(Console.ReadLine());
int seat = int.Parse(Console.ReadLine());
if (hall[row - 1, seat - 1] == 1)
{
Console.WriteLine("Der Platz ist belegt.");
}
else
{
hall[row - 1, seat - 1] = 1;
Console.WriteLine("Gebucht: Reihe " + row + ", Platz " + seat);
}
Eingabe
3
2
Ausgabe
Gebucht: Reihe 3, Platz 2
Eingabe
1
2
Ausgabe
Der Platz ist belegt.

hall ist der Saal von der vorigen Karte. Eine Zelle änderst du wie eine gewöhnliche Variable: hall[row - 1, seat - 1] = 1;.

Genauer, für Neugierige
  • Wenn jemand Reihe 7 will, liegt Index 6 außerhalb der Tabelle, und das Programm bricht mit IndexOutOfRangeException ab, genau wie bei einem gewöhnlichen Array. Prüf die Zahlen, bevor du sie verwendest: row >= 1 && row <= hall.GetLength(0), und dasselbe für den Platz.
  • Hier steht jede Zahl in einer eigenen Zeile. Wie du eine ganze Zeile mit Zahlen auf einmal liest (0 1 1 0), siehst du in der nächsten Lektion über Text.

Übung

Rechnen mit einer Tabelle

Löse die Aufgaben in deinem Editor. Schreib die Tabellen direkt in den Code.

1. Diagonalen und Zeilen. Finde für die Tabelle 2 × 2 unten die Summe der Hauptdiagonale (von oben links nach unten rechts), die Summe der anderen Diagonale und die Summen der ersten und der zweiten Zeile. Auf der Hauptdiagonale sind der Index der Zeile und der Index der Spalte gleich. Auf der anderen Diagonale ist die Spalte n - 1 - row.

Die Tabelle
1 2
3 4
Ausgabe
Hauptdiagonale: 5
Nebendiagonale: 5
Erste Zeile: 3
Zweite Zeile: 7

2. Dreh die Tabelle um. Die Verkäufe sind nach Wochen (Zeilen) und Tagen (Spalten) notiert. Die Buchhaltung will sie nach Tagen und Wochen. Erzeuge eine neue Tabelle, in der die Zeilen zu Spalten werden. Das nennt man Transponieren. Die neue Tabelle hat die Maße Spalten × Zeilen, und die Zelle [r, c] wandert nach [c, r].

Die Tabelle
42 38 51
45 40 49
Ausgabe
42 45
38 40
51 49

Übung

Labyrinthe und ein Schatz

3. Wie viele Wege gibt es? Das Labyrinth ist eine Tabelle: 0 ist ein Durchgang, 1 ist eine Wand. Du startest in der oberen linken Ecke und kommst zur unteren rechten, wobei du dich nur nach rechts oder nach unten bewegst. Wie viele verschiedene Wege gibt es?

Das Labyrinth
0 0 0 0
0 1 0 0
0 0 0 1
1 0 0 0
Ausgabe
Wege: 3
Tipp

Leg eine zweite Tabelle ways mit derselben Größe an. Schreib hinein, auf wie viele Arten man jede Zelle erreicht. Den Start erreicht man auf 1 Art. Eine Wand auf 0 Arten. Jede andere Zelle erreicht man entweder von oben oder von links, deshalb ist die Anzahl die Summe aus der Zelle darüber und der Zelle links davon (falls es sie gibt). Füll die Tabelle Zeile für Zeile aus, von oben nach unten. Die Antwort steht in der letzten Zelle.

4. Der kürzeste Weg. Eine schwere Aufgabe. Jetzt darfst du dich in alle vier Richtungen bewegen: nach oben, nach unten, nach links und nach rechts. Mit wie vielen Schritten kommst du mindestens von der oberen linken zur unteren rechten Ecke? Gibt es keinen Weg, gib „Kein Weg“ aus.

Das Labyrinth
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
Ausgabe
Kürzester Weg: 16 Schritte
Tipp

Schick eine „Welle“ vom Start los. Leg eine Tabelle dist voller -1 an (diese Zelle haben wir noch nicht erreicht) und setz 0 an den Start. Dann wiederhole: Finde alle Zellen, deren dist gleich dem aktuellen Schritt ist, und schreib bei ihren freien Nachbarn, die wir noch nicht erreicht haben, Schritt + 1 hinein. Die Welle wächst in jeder Runde um einen Schritt. Hör auf, wenn sie das Ziel erreicht oder wenn sich nichts mehr geändert hat (dann gibt es keinen Weg). Die vier Richtungen sind praktisch als zwei Arrays: int[] dr = { -1, 1, 0, 0 }; und int[] dc = { 0, 0, -1, 1 };. Der Nachbar in Richtung k liegt in Zeile r + dr[k] und Spalte c + dc[k].

Weitere Aufgaben

  • Finde den Schatz. Ein Spiel auf einer Karte, die eine Tabelle ist: 0 ist ein Weg, 1 ist ein Hindernis, 2 ist der Schatz. Der Spieler startet in der oberen linken Ecke. Gib bei jedem Zug die Karte aus (P für den Spieler, # für ein Hindernis, . für einen Weg) und lies einen Befehl als Zahl ein: 8 nach oben, 2 nach unten, 4 nach links, 6 nach rechts (wie auf dem Ziffernblock). Führt der Zug aus der Karte hinaus oder in ein Hindernis, bleibt der Spieler stehen. Wenn er auf den Schatz tritt, gib „Du hast den Schatz in … Zügen gefunden!“ aus.
  • Die Woche mit den meisten Verkäufen. Finde für die Tabelle sales heraus, welche Woche die stärkste war und welcher Wochentag im Durchschnitt der stärkste ist.
  • Freie Plätze nebeneinander. Finde für den Saal hall eine Reihe, in der mindestens 3 freie Plätze nebeneinander liegen, und gib die Nummer der Reihe und des ersten Platzes aus.

Test

Teste dich

1. Was erzeugt new int[3, 5]?
2. Die Tabelle sales hat 3 Wochen (Zeilen) und 5 Tage (Spalten). Was liefert sales.GetLength(1)?
3. Welche ist die letzte Zelle einer Tabelle mit 3 Zeilen und 5 Spalten?
4. Du willst die Summe einer Spalte (ein Tag über alle Wochen). Welcher Index ändert sich in der inneren Schleife?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·