Merge pull request 'feature/sortingAlgorithm' (#8) from feature/sortingAlgorithm into main
All checks were successful
Build & Push Frontend A / docker (push) Successful in 46s

Reviewed-on: #8
This commit was merged in pull request #8.
This commit is contained in:
2026-02-04 14:19:59 +01:00
17 changed files with 738 additions and 104 deletions

79
GEMINI.md Normal file
View File

@@ -0,0 +1,79 @@
# Gemini CLI Context for playground-frontend
## Project Overview
This is the frontend of the Playground project, built with Angular 21 and Angular Material. It includes features like a light/dark theme toggle and multi-language support via ngx-translate. The application is a static Single Page Application (SPA) served by NGINX.
**Key Technologies:**
* **Frontend Framework:** Angular 21
* **UI Components & Theming:** Angular Material
* **Internationalization:** ngx-translate
* **Server:** NGINX (for serving the SPA)
* **Containerization:** Docker
* **CI/CD:** GitHub Actions
## Building and Running
### Local Development
1. **Install dependencies:**
```bash
npm install
```
2. **Start development server:**
```bash
ng serve --open
```
The app will run at `http://localhost:4200`.
### Building for Production
To build the project for production, which creates the optimized static files:
```bash
ng build
```
### Running Tests
To run the unit tests:
```bash
ng test
```
### Linting
To lint the codebase:
```bash
ng lint
```
### Docker
To build and run the application using Docker locally:
1. **Build Docker image:**
```bash
docker build -t playground-frontend:local .
```
2. **Run Docker container:**
```bash
docker run -p 8080:80 playground-frontend:local
```
Then open `http://localhost:8080` in your browser.
## Development Conventions
* **Language:** TypeScript
* **Framework:** Angular
* **Styling:** SCSS (based on `styles.scss` and component-specific `.scss` files).
* **Linting:** ESLint is configured (see `eslint.config.js` and `package.json` scripts).
* **Internationalization:** Uses `ngx-translate` with `en.json` and `de.json` asset files.
## Project Structure (Key Areas)
* `src/app/`: Contains the main application logic, components, services, and routing.
* `src/app/pages/`: Specific pages of the application (e.g., about, algorithms, imprint, projects).
* `src/assets/`: Static assets including images, internationalization files (`i18n`), and logos.
* `Dockerfile`: Defines the Docker image for the application.
* `nginx.conf`: NGINX configuration for serving the SPA.
* `.gitea/workflows/`: Contains CI/CD workflows (e.g., `build-Frontend-a.yml`).

6
package-lock.json generated
View File

@@ -2187,9 +2187,9 @@
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -8,6 +8,7 @@ export const routes: Routes = [
{ path: RouterConstants.PROJECTS.PATH, component: RouterConstants.PROJECTS.COMPONENT},
{ path: RouterConstants.ALGORITHMS.PATH, component: RouterConstants.ALGORITHMS.COMPONENT},
{ path: RouterConstants.PATHFINDING.PATH, component: RouterConstants.PATHFINDING.COMPONENT},
{ path: RouterConstants.SORTING.PATH, component: RouterConstants.SORTING.COMPONENT},
{ path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT},
];

View File

@@ -3,6 +3,7 @@ import {ProjectsComponent} from '../pages/projects/projects.component';
import {ImprintComponent} from '../pages/imprint/imprint.component';
import {AlgorithmsComponent} from '../pages/algorithms/algorithms.component';
import {PathfindingComponent} from '../pages/algorithms/pathfinding/pathfinding.component';
import {SortingComponent} from '../pages/algorithms/sorting/sorting.component';
export class RouterConstants {
@@ -30,6 +31,12 @@ export class RouterConstants {
COMPONENT: PathfindingComponent
};
static readonly SORTING = {
PATH: 'algorithms/sorting',
LINK: '/algorithms/sorting',
COMPONENT: SortingComponent
};
static readonly IMPRINT = {
PATH: 'imprint',
LINK: '/imprint',

View File

@@ -3,4 +3,7 @@
static readonly GIT_HUB = 'https://github.com/LoboTheDark';
static readonly DIJKSTRA_WIKI = 'https://de.wikipedia.org/wiki/Dijkstra-Algorithmus'
static readonly ASTAR_WIKI = 'https://de.wikipedia.org/wiki/A*-Algorithmus'
static readonly BUBBLE_SORT_WIKI = 'https://de.wikipedia.org/wiki/Bubblesort'
static readonly QUICK_SORT_WIKI = 'https://de.wikipedia.org/wiki/Quicksort'
static readonly HEAP_SORT_WIKI = 'https://de.wikipedia.org/wiki/Heapsort'
}

View File

@@ -1,6 +1,8 @@
<div class="container">
<h1>{{ 'PATHFINDING.TITLE' | translate }}</h1>
<mat-card class="container">
<mat-card-header>
<mat-card-title>{{ 'PATHFINDING.TITLE' | translate }}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="algo-info">
<h3>{{ 'PATHFINDING.EXPLANATION.TITLE' | translate }}</h3>
@@ -42,7 +44,7 @@
<div class="controls">
<div class="grid-size">
<mat-form-field appearance="outline" class="grid-field">
<mat-label>{{ 'ALGORITHM.PATHFINDING.GRID_HEIGHT' | translate }}</mat-label>
<mat-label>{{ 'PATHFINDING.GRID_HEIGHT' | translate }}</mat-label>
<input
matInput
type="number"
@@ -55,7 +57,7 @@
</mat-form-field>
<mat-form-field appearance="outline" class="grid-field">
<mat-label>{{ 'ALGORITHM.PATHFINDING.GRID_WIDTH' | translate }}</mat-label>
<mat-label>{{ 'PATHFINDING.GRID_WIDTH' | translate }}</mat-label>
<input
matInput
type="number"
@@ -84,4 +86,4 @@
</div>
<canvas #gridCanvas></canvas>
</div>
</mat-card-content>

View File

@@ -2,25 +2,6 @@
padding: 2rem;
}
.algo-info {
margin: 0 0 1rem 0;
padding: 0.75rem 1rem;
border: 1px solid #ddd;
border-radius: 8px;
h3 {
margin: 0 0 0.5rem 0;
}
p {
margin: 0.5rem 0;
}
a {
margin-left: 0.25rem;
}
}
.controls-container {
display: flex;
flex-direction: column;

View File

@@ -12,6 +12,7 @@ import {TranslateModule, TranslateService} from '@ngx-translate/core';
import {DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS, MAX_GRID_PX, MAX_GRID_SIZE, MIN_GRID_SIZE, Node} from './pathfinding.models';
import {PathfindingService} from './service/pathfinding.service';
import {UrlConstants} from '../../../constants/UrlConstants';
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
enum NodeType {
Start = 'start',
@@ -32,7 +33,11 @@ interface GridPos { row: number; col: number }
MatButtonToggleModule,
MatFormFieldModule,
MatInputModule,
TranslateModule
TranslateModule,
MatCard,
MatCardHeader,
MatCardTitle,
MatCardContent
],
templateUrl: './pathfinding.component.html',
styleUrls: ['./pathfinding.component.scss']

View File

@@ -14,6 +14,12 @@ export class AlgorithmsService {
title: 'ALGORITHM.PATHFINDING.TITLE',
description: 'ALGORITHM.PATHFINDING.DESCRIPTION',
routerLink: RouterConstants.PATHFINDING.LINK
},
{
id: 'sorting',
title: 'ALGORITHM.SORTING.TITLE',
description: 'ALGORITHM.SORTING.DESCRIPTION',
routerLink: RouterConstants.SORTING.LINK
}
];

View File

@@ -0,0 +1,178 @@
import { Injectable } from '@angular/core';
import {SortData, SortSnapshot} from '../sorting.models';
@Injectable({
providedIn: 'root'
})
export class SortingService {
constructor() { }
private createSnapshot(array: SortData[]): SortSnapshot {
return {
array: array.map(item => ({ ...item }))
};
}
bubbleSort(array: SortData[]): SortSnapshot[] {
const snapshots: SortSnapshot[] = [];
const arr = array.map(item => ({ ...item }));
const n = arr.length;
snapshots.push(this.createSnapshot(arr));
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
arr[j].state = 'comparing';
arr[j + 1].state = 'comparing';
snapshots.push(this.createSnapshot(arr)); // Snapshot Vergleich
if (arr[j].value > arr[j + 1].value) {
const temp = arr[j].value;
arr[j].value = arr[j + 1].value;
arr[j + 1].value = temp;
snapshots.push(this.createSnapshot(arr));
}
arr[j].state = 'unsorted';
arr[j + 1].state = 'unsorted';
}
arr[n - 1 - i].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
}
arr[0].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
return snapshots;
}
// --- QUICK SORT ---
quickSort(array: SortData[]): SortSnapshot[] {
const snapshots: SortSnapshot[] = [];
const arr = array.map(item => ({ ...item }));
snapshots.push(this.createSnapshot(arr));
this.quickSortHelper(arr, 0, arr.length - 1, snapshots);
arr.forEach(i => i.state = 'sorted');
snapshots.push(this.createSnapshot(arr));
return snapshots;
}
private quickSortHelper(arr: SortData[], low: number, high: number, snapshots: SortSnapshot[]) {
if (low < high) {
const pi = this.partition(arr, low, high, snapshots);
this.quickSortHelper(arr, low, pi - 1, snapshots);
this.quickSortHelper(arr, pi + 1, high, snapshots);
} else if (low >= 0 && high >= 0 && low === high) {
arr[low].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
}
}
private partition(arr: SortData[], low: number, high: number, snapshots: SortSnapshot[]): number {
const pivot = arr[high];
arr[high].state = 'comparing'; // Pivot visualisieren
snapshots.push(this.createSnapshot(arr));
let i = (low - 1);
for (let j = low; j <= high - 1; j++) {
arr[j].state = 'comparing';
snapshots.push(this.createSnapshot(arr));
if (arr[j].value < pivot.value) {
i++;
this.swap(arr, i, j);
snapshots.push(this.createSnapshot(arr));
}
arr[j].state = 'unsorted';
}
this.swap(arr, i + 1, high);
arr[high].state = 'unsorted';
arr[i + 1].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
return i + 1;
}
// --- HEAP SORT ---
heapSort(array: SortData[]): SortSnapshot[] {
const snapshots: SortSnapshot[] = [];
const arr = array.map(item => ({ ...item }));
const n = arr.length;
snapshots.push(this.createSnapshot(arr));
for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
this.heapify(arr, n, i, snapshots);
}
for (let i = n - 1; i > 0; i--) {
arr[0].state = 'comparing';
arr[i].state = 'comparing';
snapshots.push(this.createSnapshot(arr));
this.swap(arr, 0, i);
arr[0].state = 'unsorted';
arr[i].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
this.heapify(arr, i, 0, snapshots);
}
arr[0].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
return snapshots;
}
private heapify(arr: SortData[], n: number, i: number, snapshots: SortSnapshot[]) {
let largest = i;
const l = 2 * i + 1;
const r = 2 * i + 2;
if (l < n) {
arr[l].state = 'comparing';
arr[largest].state = 'comparing';
snapshots.push(this.createSnapshot(arr));
if (arr[l].value > arr[largest].value) {
largest = l;
}
arr[l].state = 'unsorted';
arr[largest].state = 'unsorted';
}
// Vergleich Rechts
if (r < n) {
arr[r].state = 'comparing';
arr[largest].state = 'comparing';
snapshots.push(this.createSnapshot(arr));
if (arr[r].value > arr[largest].value) {
largest = r;
}
arr[r].state = 'unsorted';
arr[largest].state = 'unsorted';
}
if (largest !== i) {
this.swap(arr, i, largest);
snapshots.push(this.createSnapshot(arr));
this.heapify(arr, n, largest, snapshots);
}
}
private swap(arr: SortData[], i: number, j: number) {
const temp = arr[i].value;
arr[i].value = arr[j].value;
arr[j].value = temp;
}
}

View File

@@ -0,0 +1,88 @@
<div class="sorting-container">
<mat-card class="sorting-card">
<mat-card-header>
<mat-card-title>{{ 'SORTING.TITLE' | translate }}</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="algo-info">
<h3>{{ 'SORTING.EXPLANATION.TITLE' | translate }}</h3>
<p>
<strong>Bubble Sort</strong> {{ 'SORTING.EXPLANATION.BUBBLE_SORT_EXPLANATION' | translate }}
<a href="{{UrlConstants.BUBBLE_SORT_WIKI}}" target="_blank" rel="noopener noreferrer">Wikipedia</a>
</p>
<p>
<strong>Quick Sort</strong> {{ 'SORTING.EXPLANATION.QUICK_SORT_EXPLANATION' | translate}}
<a href="{{UrlConstants.QUICK_SORT_WIKI}}" target="_blank" rel="noopener noreferrer">Wikipedia</a>
</p>
<p>
<strong>Heap Sort</strong> {{ 'SORTING.EXPLANATION.HEAP_SORT_EXPLANATION' | translate}}
<a href="{{UrlConstants.HEAP_SORT_WIKI}}" target="_blank" rel="noopener noreferrer">Wikipedia</a>
</p>
<p>
<strong>{{ 'SORTING.EXPLANATION.NOTE' | translate}}</strong> {{ 'SORTING.EXPLANATION.DISCLAIMER' | translate}}
</p>
<ul>
<li>{{ 'SORTING.EXPLANATION.DISCLAIMER_1' | translate}}</li>
<li>{{ 'SORTING.EXPLANATION.DISCLAIMER_2' | translate}}</li>
<li>{{ 'SORTING.EXPLANATION.DISCLAIMER_3' | translate}}</li>
</ul>
<p>
{{ 'SORTING.EXPLANATION.DISCLAIMER_4' | translate}}
</p>
</div>
<div class="controls-panel">
<mat-form-field appearance="fill">
<mat-label>{{ 'SORTING.ALGORITHM' | translate }}</mat-label>
<mat-select [(ngModel)]="selectedAlgorithm">
@for (algo of availableAlgorithms; track algo.value) {
<mat-option [value]="algo.value">{{ algo.name }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>{{ 'SORTING.ARRAY_SIZE' | translate }}</mat-label>
<input
matInput
type="number"
[min]="MIN_ARRAY_SIZE"
[max]="MAX_ARRAY_SIZE"
[(ngModel)]="arraySize"
(blur)="newArraySizeSet()"
(keyup.enter)="newArraySizeSet()"
/>
</mat-form-field>
</div>
<div class="controls-panel">
<button mat-raised-button color="primary" (click)="startSorting()">
<mat-icon>play_arrow</mat-icon> {{ 'SORTING.START' | translate }}
</button>
<button mat-raised-button color="warn" (click)="resetSorting()">
<mat-icon>refresh</mat-icon> {{ 'SORTING.RESET' | translate }}
</button>
<button mat-raised-button (click)="generateNewArray()">
<mat-icon>add_box</mat-icon> {{ 'SORTING.GENERATE_NEW_ARRAY' | translate }}
</button>
</div>
<div class="info-panel">
<p>{{ 'SORTING.EXECUTION_TIME' | translate }}: {{ executionTime }} ms</p>
</div>
<div class="visualization-area">
@for (item of sortArray; track $index) {
<div
class="bar"
[style.height.px]="item.value * 3"
[class.unsorted]="item.state === 'unsorted'"
[class.comparing]="item.state === 'comparing'"
[class.sorted]="item.state === 'sorted'"
></div>
}
</div>
</mat-card-content>
</mat-card>
</div>

View File

@@ -0,0 +1,62 @@
.sorting-container {
display: flex;
justify-content: center;
align-items: flex-start;
padding: 20px;
height: 100%;
box-sizing: border-box;
.sorting-card {
width: 100%;
max-width: 1200px;
padding: 20px;
.controls-panel {
display: flex;
gap: 10px;
margin-bottom: 20px;
align-items: center;
flex-wrap: wrap;
mat-form-field {
width: 200px;
}
}
.visualization-area {
display: flex;
align-items: flex-end;
height: 300px; /* Max height for bars */
border-bottom: 1px solid #ccc;
margin-bottom: 20px;
gap: 1px;
background-color: #f0f0f0;
.bar {
flex-grow: 1;
background-color: #424242; /* Default unsorted color */
transition: height 0.05s ease-in-out, background-color 0.05s ease-in-out;
width: 10px; /* Default width, flex-grow will adjust */
min-width: 1px; /* Ensure bars are always visible */
&.unsorted {
background-color: #424242;
}
&.comparing {
background-color: #ffeb3b; /* Yellow for comparing */
}
&.sorted {
background-color: #4caf50; /* Green for sorted */
}
}
}
.info-panel {
margin-top: 10px;
font-size: 0.9em;
color: #FFFFFF;
}
}
}

View File

@@ -0,0 +1,143 @@
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
import { CommonModule } from '@angular/common';
import {MatCardModule} from "@angular/material/card";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatSelectModule} from "@angular/material/select";
import {MatButtonModule} from "@angular/material/button";
import {MatIconModule} from "@angular/material/icon";
import {TranslateModule} from "@ngx-translate/core";
import { SortingService } from './service/sorting.service';
import {SortData, SortSnapshot} from './sorting.models';
import { FormsModule } from '@angular/forms';
import {MatInput} from '@angular/material/input';
import {UrlConstants} from '../../../constants/UrlConstants';
@Component({
selector: 'app-sorting',
standalone: true,
imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule, MatInput],
templateUrl: './sorting.component.html',
styleUrls: ['./sorting.component.scss']
})
export class SortingComponent implements OnInit {
readonly MAX_ARRAY_SIZE: number = 200;
readonly MIN_ARRAY_SIZE: number = 20;
private timeoutIds: any[] = [];
sortArray: SortData[] = [];
unsortedArrayCopy: SortData[] = [];
arraySize: number = 100;
maxArrayValue: number = 100;
animationSpeed: number = 50; // Milliseconds per step
// Placeholder for available sorting algorithms
availableAlgorithms: { name: string; value: string }[] = [
{ name: 'Bubble Sort', value: 'bubbleSort' },
{ name: 'Quick Sort', value: 'quickSort' },
{ name: 'Heap Sort', value: 'heapSort' },
];
selectedAlgorithm: string = this.availableAlgorithms[0].value;
executionTime: number = 0;
constructor(private readonly sortingService: SortingService, private readonly cdr: ChangeDetectorRef) { }
ngOnInit(): void {
this.generateNewArray();
}
newArraySizeSet()
{
if (this.arraySize == this.sortArray.length)
{
return;
}
this.generateNewArray();
}
generateNewArray(): void {
this.resetSorting();
this.executionTime = 0;
this.unsortedArrayCopy = [];
this.sortArray = [];
for (let i = 0; i < this.arraySize; i++) {
const randomValue = Math.floor(Math.random() * this.maxArrayValue) + 1;
this.sortArray.push({
value: randomValue,
state: 'unsorted'
});
this.unsortedArrayCopy.push({
value: randomValue,
state: 'unsorted'
});
}
}
private resetSortState() {
for (let i = 0; i < this.sortArray.length; i++) {
let element = this.sortArray[i];
let unsortedElement = this.unsortedArrayCopy[i];
element.value = unsortedElement.value;
element.state = 'unsorted';
}
}
async startSorting(): Promise<void> {
this.resetSorting();
const startTime = performance.now();
let snapshots: SortSnapshot[] = [];
switch (this.selectedAlgorithm) {
case 'bubbleSort':
snapshots = this.sortingService.bubbleSort(this.sortArray);
break;
case 'quickSort':
snapshots = this.sortingService.quickSort(this.sortArray);
break;
case 'heapSort':
snapshots = this.sortingService.heapSort(this.sortArray);
break;
}
const endTime = performance.now();
this.executionTime = parseFloat((endTime - startTime).toFixed(4));
console.log(snapshots.length);
this.animateSorting(snapshots);
}
private animateSorting(snapshots: SortSnapshot[]): void {
snapshots.forEach((snapshot, index) => {
const id = setTimeout(() => {
for (let i = 0; i < this.sortArray.length; i++) {
if (snapshot.array[i]) {
this.sortArray[i].value = snapshot.array[i].value;
this.sortArray[i].state = snapshot.array[i].state;
}
}
this.cdr.detectChanges();
if (index === snapshots.length - 1) {
this.sortArray.forEach(item => item.state = 'sorted');
this.cdr.detectChanges();
}
}, index * this.animationSpeed);
this.timeoutIds.push(id);
});
}
private stopAnimations(): void {
this.timeoutIds.forEach(id => clearTimeout(id));
this.timeoutIds = [];
}
resetSorting(): void {
this.stopAnimations();
this.resetSortState();
}
protected readonly UrlConstants = UrlConstants;
}

View File

@@ -0,0 +1,8 @@
export interface SortData {
value: number;
state: 'sorted' | 'comparing' | 'unsorted';
}
export interface SortSnapshot {
array: SortData[];
}

View File

@@ -317,15 +317,41 @@
},
"ALERT": {
"START_END_NODES": "Bitte wählen Sie einen Start- und Endknoten aus, bevor Sie den Algorithmus starten."
},
"GRID_HEIGHT": "Höhe",
"GRID_WIDTH": "Beite"
},
"SORTING": {
"TITLE": "Sortieralgorithmen",
"ALGORITHM": "Algorithmen",
"START": "Sortierung starten",
"RESET": "Zurücksetzen",
"GENERATE_NEW_ARRAY": "Neues Array generieren",
"EXECUTION_TIME": "Ausführungszeit",
"ARRAY_SIZE": "Anzahl der Balken",
"EXPLANATION": {
"TITLE": "Algorithmen",
"BUBBLE_SORT_EXPLANATION":"vergleicht wiederholt benachbarte Elemente und tauscht sie, wenn sie in der falschen Reihenfolge stehen. Das größte Element \"blubbert\" dabei wie eine Luftblase ans Ende der Liste. Vorteil: Extrem einfach zu verstehen und zu implementieren; erkennt bereits sortierte Listen sehr schnell. Nachteil: Sehr ineffizient bei großen Listen (Laufzeit O(n²)). In der Praxis kaum genutzt.",
"QUICK_SORT_EXPLANATION": "folgt dem \"Teile und Herrsche\"-Prinzip. Ein \"Pivot\"-Element wird gewählt, und das Array wird in zwei Hälften geteilt: Elemente kleiner als das Pivot und Elemente größer als das Pivot. Vorteil: Im Durchschnitt einer der schnellsten Sortieralgorithmen (O(n log n)); benötigt keinen zusätzlichen Speicher (In-Place). Nachteil: Im schlechtesten Fall (Worst Case) langsam (O(n²)), wenn das Pivot ungünstig gewählt wird. Ist nicht stabil (ändert Reihenfolge gleicher Elemente).",
"HEAP_SORT_EXPLANATION": "organisiert die Daten zunächst in einer speziellen Baumstruktur (Binary Heap). Das größte Element (die Wurzel) wird entnommen und ans Ende sortiert, dann wird der Baum repariert. Vorteil: Garantiert eine schnelle Laufzeit von O(n log n), selbst im schlechtesten Fall. Benötigt fast keinen zusätzlichen Speicher. Nachteil: In der Praxis oft etwas langsamer als Quick Sort, da die Sprünge im Speicher (Heap-Struktur) den CPU-Cache schlechter nutzen.",
"NOTE": "HINWEIS",
"DISCLAIMER": "Die Wahl des \"besten\" Sortieralgorithmus hängt stark von den Daten und den Rahmenbedingungen ab. In der Informatik betrachtet man oft drei Szenarien:",
"DISCLAIMER_1": "Best Case: Die Daten sind schon fast sortiert (hier glänzt z.B. Bubble Sort).",
"DISCLAIMER_2": "Average Case: Der statistische Normalfall.",
"DISCLAIMER_3": "Worst Case: Die Daten sind maximal ungünstig angeordnet (hier bricht Quick Sort ohne Optimierung ein, während Heap Sort stabil bleibt).",
"DISCLAIMER_4": "Zusätzlich gibt es fast immer einen Time-Space Trade-off (Zeit-Speicher-Kompromiss): Algorithmen, die extrem schnell sind (wie Merge Sort), benötigen oft viel zusätzlichen Arbeitsspeicher. Algorithmen, die direkt im vorhandenen Speicher arbeiten (wie Heap Sort), sparen Platz, sind aber manchmal komplexer oder minimal langsamer. Es gibt also keine \"One-Size-Fits-All\"-Lösung."
}
},
"ALGORITHM": {
"TITLE": "Algorithmen",
"PATHFINDING": {
"TITLE": "Wegfindung",
"DESCRIPTION": "Vergleich von Dijkstra vs. A*.",
"GRID_HEIGHT": "Höhe",
"GRID_WIDTH": "Beite"
"DESCRIPTION": "Vergleich von Dijkstra vs. A*."
},
"SORTING": {
"TITLE": "Sortierung",
"DESCRIPTION": "Visualisierung verschiedener Sortieralgorithmen."
}
}
}

View File

@@ -317,15 +317,40 @@
},
"ALERT": {
"START_END_NODES": "Please select a start and end node before running the algorithm."
},
"GRID_HEIGHT": "Height",
"GRID_WIDTH": "Width"
},
"SORTING": {
"TITLE": "Sorting Algorithms",
"ALGORITHM": "Algorithm",
"START": "Start Sorting",
"RESET": "Reset",
"GENERATE_NEW_ARRAY": "Generate New Array",
"EXECUTION_TIME": "Execution Time",
"ARRAY_SIZE": "Number of Bars",
"EXPLANATION": {
"TITLE": "Algorithms",
"BUBBLE_SORT_EXPLANATION": "repeatedly compares adjacent elements and swaps them if they are in the wrong order. The largest element \"bubbles\" to the end of the list like an air bubble. Advantage: Extremely simple to understand and implement; detects already sorted lists very quickly. Disadvantage: Very inefficient for large lists (runtime O(n²)). Rarely used in practice.",
"QUICK_SORT_EXPLANATION": "follows the \"divide and conquer\" principle. A \"pivot\" element is selected, and the array is divided into two halves: elements smaller than the pivot and elements larger than the pivot. Advantage: On average one of the fastest sorting algorithms (O(n log n)); requires no additional memory (in-place). Disadvantage: Slow in the worst case (O(n²)) if the pivot is chosen poorly. Is not stable (changes order of equal elements).",
"HEAP_SORT_EXPLANATION": "organizes the data initially into a special tree structure (Binary Heap). The largest element (the root) is extracted and sorted to the end, then the tree is repaired. Advantage: Guarantees a fast runtime of O(n log n), even in the worst case. Requires almost no additional memory. Disadvantage: Often slightly slower than Quick Sort in practice because the jumps in memory (heap structure) utilize the CPU cache less effectively.",
"NOTE": "NOTE",
"DISCLAIMER": "The choice of the \"best\" sorting algorithm depends heavily on the data and the constraints. In computer science, three scenarios are often considered:",
"DISCLAIMER_1": "Best Case: The data is already nearly sorted (Bubble Sort shines here, for example).",
"DISCLAIMER_2": "Average Case: The statistical norm.",
"DISCLAIMER_3": "Worst Case: The data is arranged in the most unfavorable way possible (Quick Sort performs poorly here without optimization, while Heap Sort remains stable).",
"DISCLAIMER_4": "Additionally, there is almost always a Time-Space Trade-off: Algorithms that are extremely fast (like Merge Sort) often require a lot of additional working memory. Algorithms that work directly in existing memory (like Heap Sort) save space but are sometimes more complex or slightly slower. Thus, there is no \"one-size-fits-all\" solution."
}
},
"ALGORITHM": {
"TITLE": "Algorithms",
"PATHFINDING": {
"TITLE": "Pathfinding",
"DESCRIPTION": "Comparing of Dijkstra vs. A*.",
"GRID_HEIGHT": "Height",
"GRID_WIDTH": "Width"
"DESCRIPTION": "Comparing of Dijkstra vs. A*."
},
"SORTING": {
"TITLE": "Sorting",
"DESCRIPTION": "Visualizing various sorting algorithms."
}
}
}

View File

@@ -211,3 +211,23 @@ a {
height: 18px;
width: 18px;
}
// algos
.algo-info {
margin: 0 0 1rem 0;
padding: 0.75rem 1rem;
border: 1px solid #ddd;
border-radius: 8px;
h3 {
margin: 0 0 0.5rem 0;
}
p {
margin: 0.5rem 0;
}
a {
margin-left: 0.25rem;
}
}