Merge pull request 'main' (#2) from main into MM_Branch

Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
2025-12-11 11:23:14 +00:00
20 changed files with 433 additions and 137 deletions
+40
View File
@@ -0,0 +1,40 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug test_stack",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/test_stack.exe",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
"miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe",
"preLaunchTask": "Build via Makefile"
},
{
"name": "Debug test_numbers",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/test_numbers.exe",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
"miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe",
"preLaunchTask": "Build via Makefile"
},
{
"name": "Debug test_bintree",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/test_bintree.exe",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
"miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe",
"preLaunchTask": "Build via Makefile"
}
]
}
+27
View File
@@ -0,0 +1,27 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build via Makefile",
"type": "shell",
"command": "mingw32-make",
"args": ["${input:target}"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$gcc"
}
],
"inputs": [
{
"id": "target",
"type": "pickString",
"description": "Welches Makefile-Target soll gebaut werden?",
"options": ["test_stack", "test_numbers", "test_bintree", "doble", "unitTests"]
}
]
}
+97 -60
View File
@@ -1,111 +1,148 @@
#include <stdlib.h>
#include <string.h>
#include "stack.h"
#include "bintree.h"
// internal helper to push node and all its left descendants onto iterator stack
static void bintree_pushLefts(StackNode **iterStackPtr, TreeNode *n)
/*
Schiebt einen Knoten und alle seine linken Nachfolger
(entlang der "linken Kante") auf den Iterator-Stack.
iterStackPtr: Zeiger auf den Top-Zeiger des Stacks (LIFO) für die Wiederholung
knoten: aktueller Startknoten, dessen linke Kette abgelegt wird
*/
static void bintree_pushLefts(StackNode **iterStackPtr, TreeNode *knoten)
{
while(n != NULL)
while (knoten != NULL)
{
*iterStackPtr = push(*iterStackPtr, n);
n = n->left;
*iterStackPtr = push(*iterStackPtr, knoten); // aktuellen Knoten oben auf den Stack legen
knoten = knoten->left; // zum linken Kind weiterlaufen
}
}
// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates
// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added).
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate)
/*
Fügt eine Kopie der Daten (Speicherbereich von 'data' mit Länge 'dataSize') in den Baum ein.
Die Ordnung wird über 'compareFct' festgelegt.
Duplikate:
- Wenn 'isDuplicate' != NULL übergeben wird: Duplikate werden NICHT eingefügt,
stattdessen wird *isDuplicate = 1 gesetzt (bei neuem Eintrag = 0).
- Wenn 'isDuplicate' == NULL: Duplikate SIND erlaubt; der Duplikat-Eintrag
wird in den rechten Teilbaum eingefügt.
Rückgabe:
- Zeiger auf die (ggf. unveränderte oder neue) Wurzel des Teilbaums.
- NULL bei Speicherfehlern beim Anlegen des ersten Knotens.
*/
TreeNode *addToTree(TreeNode *wurzel,
const void *daten,
size_t datenGroesse,
CompareFctType vergleichFkt,
int *istDuplikat)
{
if(isDuplicate != NULL)
*isDuplicate = 0;
// Standardmäßig annehmen: kein Duplikat (falls Ausgabefeld vorhanden)
if (istDuplikat != NULL)
*istDuplikat = 0;
if(root == NULL)
// Leerer Baum/Teilbaum: neuen Knoten erzeugen
if (wurzel == NULL)
{
TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode));
if(node == NULL)
return NULL;
node->data = malloc(dataSize);
if(node->data == NULL)
TreeNode *neuerKnoten = (TreeNode *)malloc(sizeof(TreeNode));
if (neuerKnoten == NULL)
return NULL; // Speicherfehler
neuerKnoten->data = malloc(datenGroesse);
if (neuerKnoten->data == NULL)
{
free(node);
return NULL;
free(neuerKnoten);
return NULL; // Speicherfehler für Datenbereich
}
memcpy(node->data, data, dataSize);
node->left = node->right = NULL;
return node;
memcpy(neuerKnoten->data, daten, datenGroesse); // tiefe Kopie der Nutzdaten
neuerKnoten->left = neuerKnoten->right = NULL; // Blatt
return neuerKnoten;
}
int cmp = compareFct(data, root->data);
if(cmp < 0)
// Vergleich der einzufügenden Daten mit dem aktuellen Knoten
int vergleich = vergleichFkt(daten, wurzel->data);
if (vergleich < 0)
{
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
// links einfügen
wurzel->left = addToTree(wurzel->left, daten, datenGroesse, vergleichFkt, istDuplikat);
}
else if(cmp > 0)
else if (vergleich > 0)
{
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
// rechts einfügen
wurzel->right = addToTree(wurzel->right, daten, datenGroesse, vergleichFkt, istDuplikat);
}
else // equal
else
{
if(isDuplicate != NULL)
// Gleichheit (potenzielles Duplikat)
if (istDuplikat != NULL)
{
*isDuplicate = 1;
// do not insert duplicate
*istDuplikat = 1; // Duplikat erkannt, NICHT einfügen
// keine Änderung am Baum
}
else
{
// duplicates allowed -> insert into right subtree
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
// Duplikate erlaubt -> konventionell in den rechten Teilbaum einfügen
wurzel->right = addToTree(wurzel->right, daten, datenGroesse, vergleichFkt, istDuplikat);
}
}
return root;
return wurzel;
}
// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction.
// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element,
// push the top node and push all its left nodes.
void *nextTreeData(TreeNode *root)
void *nextTreeData(TreeNode *wurzel)
{
static StackNode *iterStack = NULL;
static StackNode *iteratorStack = NULL; // interner Zustand über Aufrufe hinweg
// If a new tree root is provided -> reset iterator
if(root != NULL)
// Neuer Baum übergeben -> Iterator zurücksetzen/initialisieren
if (wurzel != NULL)
{
clearStack(iterStack);
iterStack = NULL;
bintree_pushLefts(&iterStack, root);
clearStack(iteratorStack); // ggf. alten Stack leeren (Speicher freigeben)
iteratorStack = NULL; // Top-Zeiger zurücksetzen
bintree_pushLefts(&iteratorStack, wurzel); // Wurzel und linke Kette ablegen
}
if(iterStack == NULL)
// Kein weiterer Eintrag?
if (iteratorStack == NULL)
return NULL;
TreeNode *node = (TreeNode *)top(iterStack);
iterStack = pop(iterStack);
// Nächsten Knoten holen (oberstes Stack-Element)
TreeNode *aktuellerKnoten = (TreeNode *)top(iteratorStack);
iteratorStack = pop(iteratorStack);
if(node->right != NULL)
bintree_pushLefts(&iterStack, node->right);
// Falls rechter Teilbaum existiert: dessen linke Kette ablegen
if (aktuellerKnoten->right != NULL)
bintree_pushLefts(&iteratorStack, aktuellerKnoten->right);
return node->data;
// Daten des aktuellen Knotens zurückgeben
return aktuellerKnoten->data;
}
// Releases all memory resources (including data copies).
void clearTree(TreeNode *root)
/*
Gibt den gesamten Baum frei (inkl. der tief kopierten Daten).
*/
void clearTree(TreeNode *wurzel)
{
if(root == NULL)
if (wurzel == NULL)
return;
clearTree(root->left);
clearTree(root->right);
clearTree(wurzel->left);
clearTree(wurzel->right);
free(root->data);
free(root);
free(wurzel->data);
free(wurzel);
}
// Returns the number of entries in the tree given by root.
unsigned int treeSize(const TreeNode *root)
/*
Liefert die Anzahl der Knoten/Einträge im Teilbaum 'wurzel'.
*/
unsigned int treeSize(const TreeNode *wurzel)
{
if(root == NULL)
if (wurzel == NULL)
return 0;
return 1 + treeSize(root->left) + treeSize(root->right);
return 1U + treeSize(wurzel->left) + treeSize(wurzel->right);
}
+64 -10
View File
@@ -1,27 +1,81 @@
#ifndef BINTREE_H
#define BINTREE_H
#include <stdlib.h>
/*
Typdefinition für die Vergleichsfunktion.
Die Funktion muss zwei Datenzeiger vergleichen und zurückgeben:
- < 0, wenn arg1 kleiner als arg2 ist
- 0, wenn arg1 gleich arg2 ist
- > 0, wenn arg1 größer als arg2 ist
*/
typedef int (*CompareFctType)(const void *arg1, const void *arg2);
/*
Struktur für einen Binärbaum-Knoten.
Enthält:
- data: Zeiger auf die gespeicherten Daten (beliebiger Typ, dynamisch allokiert)
- left: Zeiger auf linken Teilbaum
- right: Zeiger auf rechten Teilbaum
*/
typedef struct node
{
void *data;
struct node *left;
struct node *right;
void *data; // Zeiger auf die Nutzdaten
struct node *left; // Zeiger auf linken Kindknoten
struct node *right; // Zeiger auf rechten Kindknoten
} TreeNode;
// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates
// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added).
/*
Fügt eine Kopie der Daten in den Binärbaum ein.
Parameter:
root : Wurzel des (Teil-)Baums
data : Zeiger auf die einzufügenden Daten
dataSize : Größe der Daten in Bytes
compareFct : Vergleichsfunktion für die Ordnung
isDuplicate : Optionaler Zeiger:
- Wenn NULL: Duplikate sind erlaubt
- Wenn != NULL: Duplikate werden ignoriert und *isDuplicate wird gesetzt:
- 0: neuer Eintrag eingefügt
- 1: Duplikat erkannt, nicht eingefügt
Rückgabe:
Zeiger auf die (ggf. neue) Wurzel des Baums oder NULL bei Speicherfehler.
*/
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate);
// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction.
// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element,
// push the top node and push all its left nodes.
/*
Iteriert über den Baum in Inorder-Reihenfolge.
Verhalten:
- Erster Aufruf mit root != NULL: Iterator initialisieren
- Folgeaufrufe mit root == NULL: nächstes Element zurückgeben
Rückgabe:
Zeiger auf die Daten des nächsten Knotens oder NULL, wenn Ende erreicht.
Hinweis:
Intern wird ein Stack verwendet. Nicht threadsicher.
*/
void *nextTreeData(TreeNode *root);
// Releases all memory resources (including data copies).
/*
Gibt den gesamten Baum frei (inklusive der gespeicherten Daten).
Nach dem Aufruf sind alle Zeiger ungültig.
*/
void clearTree(TreeNode *root);
// Returns the number of entries in the tree given by root.
/*
Liefert die Anzahl der Knoten im Baum.
Rückgabe:
Anzahl der Knoten (0 bei leerem Baum).
*/
unsigned int treeSize(const TreeNode *root);
#endif
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -1,10 +1,10 @@
Player1;19887
Player1;19843
Lena;19811
Lena;19702
Player1;19578
player_name;9981
Lena;9980
Lena;9978
Lena;9978
Lena;9976
Lena;9975
Lena;9971
Lena;9965
BIN
View File
Binary file not shown.
+126 -38
View File
@@ -1,3 +1,4 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
@@ -5,70 +6,135 @@
#include "numbers.h"
#include "bintree.h"
// helper comparator for unsigned int for bintree
/**
* @brief Vergleichsfunktion für unsigned int-Werte zur Verwendung im Binärbaum.
*
* Diese Funktion wird von der Binärbaum-Implementierung genutzt, um die
* Ordnung der Knoten zu bestimmen. Sie vergleicht die dereferenzierten
* unsigned int-Werte a und b.
*
* @param a Pointer auf einen unsigned int-Wert (linker Operand)
* @param b Pointer auf einen unsigned int-Wert (rechter Operand)
* @return -1, falls *a < *b; 1, falls *a > *b; 0, falls *a == *b
*/
static int compareUInt(const void *a, const void *b)
{
unsigned int va = *(const unsigned int *)a;
unsigned int vb = *(const unsigned int *)b;
if(va < vb) return -1;
if(va > vb) return 1;
if (va < vb) return -1;
if (va > vb) return 1;
return 0;
}
// comparator for qsort (unsigned int)
/**
* @brief Vergleichsfunktion für qsort() zur Sortierung von unsigned int-Arrays.
*
* @param a Pointer auf einen Arrayeintrag
* @param b Pointer auf einen Arrayeintrag
* @return -1, 0, 1 analog zu compareUInt()
*/
static int qsort_uint_cmp(const void *a, const void *b)
{
unsigned int va = *(const unsigned int *)a;
unsigned int vb = *(const unsigned int *)b;
if(va < vb) return -1;
if(va > vb) return 1;
if (va < vb) return -1;
if (va > vb) return 1;
return 0;
}
// Returns len random numbers between 1 and 2x len in random order which are all different, except for two entries.
// Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while
// creating random numbers.
/**
* @brief Erzeugt ein Array aus len Zufallszahlen im Bereich [1 .. 2*len],
* das genau einen duplizierten Wert enthält (d. h. len-1 eindeutige + 1 Duplikat),
* und mischt die Reihenfolge zufällig.
*
* Funktionsweise:
* - Es werden zunächst len-1 eindeutige Zufallszahlen erzeugt. Die Eindeutigkeit wird
* mithilfe eines Binärsuchbaums (BST) geprüft: addToTree() fügt die Zahl ein
* und signalisiert per isDup, ob sie bereits vorhanden war.
* - Anschließend wird eine der bereits erzeugten Zahlen zufällig ausgewählt und
* noch einmal an das Ende des Arrays geschrieben, um das geforderte Duplikat sicherzustellen.
* - Zum Schluss wird das gesamte Array mittels Fisher–Yates-Algorithmus gemischt.
*
* Fehlerbehandlung:
* - Bei len < 2 wird NULL zurückgegeben, da das Problem ein Duplikat erfordert.
* - Bei Speicher- oder Baum-Insertionsfehlern wird aufgeräumt und NULL zurückgegeben.
* Wichtig: Der Baumzeiger root wird erst nach erfolgreichem Insert aktualisiert,
* um im Fehlerfall kein bereits aufgebautes Teilbaum-Objekt zu verlieren.
*
* Randbedingungen / Annahmen:
* - addToTree(root, &val, sizeof(val), compareUInt, &isDup) setzt isDup:
* isDup == 1 bedeutet „Duplikat gefunden, Baum unverändert“,
* isDup == 0 bedeutet „neuer Wert eingefügt (oder Fehler)“.
* - Bei Speicherfehler gibt addToTree NULL zurück und isDup bleibt 0.
* - clearTree(root) darf mit NULL-Argument aufgerufen werden (No-Op).
*
* Komplexität:
* - Durchschnittlich O(len * log(len)) für die len-1 Einfügungen in den BST.
* - Shuffle in O(len).
*
* @param len Anzahl der zu erzeugenden Werte (muss >= 2 sein)
* @return Pointer auf ein Array mit len Einträgen bei Erfolg; NULL bei Fehlern
*/
unsigned int *createNumbers(unsigned int len)
{
if(len < 2)
if (len < 2)
return NULL;
unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len);
if(numbers == NULL)
if (numbers == NULL)
return NULL;
// seed once
srand((unsigned int)time(NULL));
// Zufallszahlengenerator nur einmal pro Prozess initialisieren.
// Hintergrund: Wird createNumbers mehrfach schnell hintereinander gerufen,
// kann time(NULL) identische Seeds liefern und damit identische Zahlenfolgen erzeugen.
static int seeded = 0;
if (!seeded) {
srand((unsigned int)time(NULL));
seeded = 1;
}
TreeNode *root = NULL;
unsigned int range = 2 * len;
// create len-1 unique numbers
for(unsigned int i = 0; i < len - 1; i++)
// Schritt 1: len-1 eindeutige Zufallszahlen erzeugen
for (unsigned int i = 0; i < len - 1; i++)
{
unsigned int val;
int isDup = 0;
// try until a unique number is inserted
do
int isDup;
// Wiederholen, bis eine wirklich neue Zahl eingefügt wurde
for (;;)
{
val = (unsigned int)(rand() % range) + 1; // [1..2*len]
root = addToTree(root, &val, sizeof(val), compareUInt, &isDup);
// if addToTree returned NULL due to allocation failure, cleanup and return NULL
if(root == NULL && isDup == 0)
{
isDup = 0; // vor jedem Insert zurücksetzen, um „alte“ Werte zu vermeiden
val = (unsigned int)(rand() % range) + 1; // Wertebereich [1 .. 2*len]
// addToTree kann bei Erfolg einen (ggf. neuen) Wurzelzeiger liefern.
// Zur Vermeidung eines Speicherlecks bei Fehlern zunächst in temp speichern.
TreeNode *newRoot = addToTree(root, &val, sizeof(val), compareUInt, &isDup);
if (newRoot == NULL && isDup == 0) {
// Vermutlich Speicher-/Insertionsfehler: aufräumen und abbrechen
free(numbers);
clearTree(root);
clearTree(root); // root zeigt noch auf den gültigen Teilbaum
return NULL;
}
} while(isDup);
numbers[i] = val;
if (!isDup) {
// Einfügen war erfolgreich und der Wert ist eindeutig.
root = newRoot;
numbers[i] = val;
break;
}
// Andernfalls Duplikat: Neue Zufallszahl versuchen.
}
}
// duplicate one existing random entry
unsigned int idx = (unsigned int)(rand() % (len - 1));
// Schritt 2: Eine der bestehenden Zahlen zufällig duplizieren
unsigned int idx = (unsigned int)(rand() % (len - 1)); // Index im Bereich [0 .. len-2]
numbers[len - 1] = numbers[idx];
// shuffle array (Fisher-Yates)
for(unsigned int i = len - 1; i > 0; i--)
// Schritt 3: Fisher–Yates-Shuffle über das gesamte Array
for (unsigned int i = len - 1; i > 0; i--)
{
unsigned int j = (unsigned int)(rand() % (i + 1));
unsigned int tmp = numbers[i];
@@ -76,31 +142,53 @@ unsigned int *createNumbers(unsigned int len)
numbers[j] = tmp;
}
// free tree resources
// Aufräumen: Baum freigeben
clearTree(root);
return numbers;
}
// Returns only the only number in numbers which is present twice. Returns zero on errors.
/**
* @brief Findet den einzigen duplizierten Wert in einem Array aus len unsigned int.
*
* Funktionsweise:
* - Es wird eine Kopie des Eingabearrays erstellt, um die Reihenfolge des
* Originalarrays nicht zu verändern.
* - Die Kopie wird mittels qsort() aufsteigend sortiert.
* - Beim Durchlauf werden benachbarte Elemente verglichen. Da genau ein Wert
* doppelt vorkommt, finden wir ihn als erstes Paar gleicher Nachbarn.
*
* Fehlerbehandlung:
* - Bei ungültigen Parametern (numbers == NULL oder len < 2) wird 0 geliefert.
* - Bei Speicherfehlern beim Kopieren ebenfalls 0.
*
* Komplexität:
* - Sortieren in O(len * log(len)), anschließender Linearpass O(len).
*
* @param numbers Pointer auf das Eingabearray
* @param len Länge des Arrays (muss >= 2 sein)
* @return Der doppelte Wert; 0 bei Fehlern oder falls kein Duplikat gefunden wurde
* (gemäß Aufgabenstellung sollte aber genau ein Duplikat existieren).
*/
unsigned int getDuplicate(const unsigned int numbers[], unsigned int len)
{
if(numbers == NULL || len < 2)
if (numbers == NULL || len < 2)
return 0;
// make a copy so original array order is not modified by caller expectation
// Kopie erstellen, damit das Original unangetastet bleibt
unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len);
if(copy == NULL)
if (copy == NULL)
return 0;
memcpy(copy, numbers, sizeof(unsigned int) * len);
// Sortieren der Kopie
qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp);
// Linearer Scan: erstes Paar identischer Nachbarn ist das Duplikat
unsigned int result = 0;
for(unsigned int i = 0; i + 1 < len; i++)
for (unsigned int i = 0; i + 1 < len; i++)
{
if(copy[i] == copy[i+1])
{
if (copy[i] == copy[i + 1]) {
result = copy[i];
break;
}
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -1,15 +1,15 @@
#include <stdlib.h>
#include "stack.h"
// Pushes data as pointer onto the stack.
// Push Daten auf den Stack legen.
StackNode *push(StackNode *stack, void *data)
{
StackNode *node = (StackNode *)malloc(sizeof(StackNode));
StackNode *node = malloc(sizeof(StackNode));
if(node == NULL)
return stack; // allocation failed -> return unchanged stack
node->data = data;
node->next = stack;
node->data = data; // Set the data for the new node
node->next = stack; // New node points to the previous top of the stack
return node;
}
BIN
View File
Binary file not shown.
+69 -19
View File
@@ -1,41 +1,91 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "numbers.h"
/**
* @brief Selbsttest für createNumbers() und getDuplicate().
*
* Erzeugt ein Array aus len Zufallszahlen mit genau einem duplizierten Wert,
* validiert die Eigenschaften per Zähl-Array (Wertebereich, Häufigkeiten)
* und prüft anschließend, ob getDuplicate() dasselbe Duplikat ermittelt.
*
* Rückgabecodes:
* 0: OK
* 1: createNumbers() fehlgeschlagen
* 2: Speicherfehler für counts
* 3: Wert außerhalb des Bereichs [1..2*len]
* 4: Ein Wert erscheint öfter als zweimal
* 5: Nicht genau ein Duplikat gefunden
* 6: getDuplicate() liefert anderes Ergebnis als Zählung
*/
int main(void)
{
unsigned int len = 100;
unsigned int len = 100; // Anzahl zu erzeugender Zahlen
unsigned int *nums = createNumbers(len);
if(nums == NULL) { fprintf(stderr, "createNumbers returned NULL\n"); return 1; }
// count occurrences
unsigned int maxVal = 2 * len;
unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int));
if(counts == NULL) { free(nums); return 2; }
for(unsigned int i = 0; i < len; i++)
{
if(nums[i] > maxVal) { fprintf(stderr, "value out of expected range\n"); free(nums); free(counts); return 3; }
counts[nums[i]]++;
if (nums == NULL) {
fprintf(stderr, "createNumbers returned NULL\n");
return 1; // Erzeugung fehlgeschlagen
}
unsigned int maxVal = 2 * len; // Erlaubter Bereich: [1 .. 2*len]
// Zähl-Array für Häufigkeiten pro Wert (Index 0 bleibt ungenutzt)
unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int));
if (counts == NULL) {
free(nums);
return 2; // Speicherfehler bei counts
}
// Häufigkeiten bestimmen und gleichzeitig Bereich prüfen
for (unsigned int i = 0; i < len; i++) {
unsigned int v = nums[i];
if (v == 0 || v > maxVal) { // sollte nicht passieren, wenn createNumbers korrekt ist
fprintf(stderr, "value out of expected range\n");
free(nums);
free(counts);
return 3;
}
counts[v]++; // Auftreten des Werts v zählen
}
// Exakt ein Wert muss doppelt vorkommen; keiner darf >2-mal vorkommen
int duplicatesFound = 0;
unsigned int duplicateValue = 0;
for(unsigned int v = 1; v <= maxVal; v++)
{
if(counts[v] == 2) { duplicatesFound++; duplicateValue = v; }
else if(counts[v] > 2) { fprintf(stderr, "value %u appears more than twice\n", v); free(nums); free(counts); return 4; }
for (unsigned int v = 1; v <= maxVal; v++) {
if (counts[v] == 2) {
duplicatesFound++;
duplicateValue = v; // den doppelten Wert merken
} else if (counts[v] > 2) {
fprintf(stderr, "value %u appears more than twice\n", v);
free(nums);
free(counts);
return 4; // Vertragsbruch: zu häufiges Auftreten
}
// counts[v] == 0 oder 1 sind unkritisch
}
if(duplicatesFound != 1) { fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound); free(nums); free(counts); return 5; }
if (duplicatesFound != 1) {
fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound);
free(nums);
free(counts);
return 5; // zu wenige/zu viele Duplikate
}
// Ergebnis von getDuplicate() mit der Zählung abgleichen
unsigned int found = getDuplicate(nums, len);
if(found != duplicateValue) { fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue); free(nums); free(counts); return 6; }
if (found != duplicateValue) {
fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue);
free(nums);
free(counts);
return 6; // Abweichung zwischen Methoden
}
// Ressourcen freigeben und Erfolg melden
free(nums);
free(counts);
printf("test_numbers: OK (duplicate = %u)\n", duplicateValue);
return 0;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.