feature/sortingAlgorithm #8

Merged
lobo merged 5 commits from feature/sortingAlgorithm into main 2026-02-04 14:20:00 +01:00
7 changed files with 261 additions and 112 deletions
Showing only changes of commit 450ab0b837 - Show all commits

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { SortData } from '../sorting.models';
import {SortData, SortSnapshot} from '../sorting.models';
@Injectable({
providedIn: 'root'
@@ -8,90 +8,171 @@ export class SortingService {
constructor() { }
async bubbleSort(array: SortData[], animationSpeed: number): Promise<void> {
console.log(animationSpeed);
const n = array.length;
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++) {
console.log("1");
for (let j = 0; j < n - i - 1; j++) {
console.log("2");
array[j].state = 'comparing';
array[j + 1].state = 'comparing';
await this.delay(animationSpeed);
arr[j].state = 'comparing';
arr[j + 1].state = 'comparing';
snapshots.push(this.createSnapshot(arr)); // Snapshot Vergleich
if (array[j].value > array[j + 1].value) {
// Swap elements
const temp = array[j].value;
array[j].value = array[j + 1].value;
array[j + 1].value = temp;
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));
}
array[j].state = 'unsorted';
array[j + 1].state = 'unsorted';
arr[j].state = 'unsorted';
arr[j + 1].state = 'unsorted';
}
array[n - 1 - i].state = 'sorted'; // Mark the largest element as sorted
arr[n - 1 - i].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
}
array[0].state = 'sorted'; // Mark the last element as sorted (if n > 0)
arr[0].state = 'sorted';
snapshots.push(this.createSnapshot(arr));
return snapshots;
}
async selectionSort(array: SortData[], animationSpeed: number): Promise<void> {
const n = array.length;
for (let i = 0; i < n - 1; i++) {
let minIdx = i;
array[i].state = 'comparing';
for (let j = i + 1; j < n; j++) {
array[j].state = 'comparing';
await this.delay(animationSpeed);
// --- QUICK SORT ---
quickSort(array: SortData[]): SortSnapshot[] {
const snapshots: SortSnapshot[] = [];
const arr = array.map(item => ({ ...item }));
snapshots.push(this.createSnapshot(arr));
if (array[j].value < array[minIdx].value) {
minIdx = j;
}
if (j !== minIdx) { // Only reset if it wasn't the minimum
array[j].state = 'unsorted';
}
}
if (minIdx !== i) {
// Swap elements
const temp = array[i].value;
array[i].value = array[minIdx].value;
array[minIdx].value = temp;
}
array[i].state = 'sorted'; // Mark the current element as sorted
if (minIdx !== i) {
array[minIdx].state = 'unsorted';
}
}
array[n - 1].state = 'sorted'; // Mark the last element as sorted
this.quickSortHelper(arr, 0, arr.length - 1, snapshots);
arr.forEach(i => i.state = 'sorted');
snapshots.push(this.createSnapshot(arr));
return snapshots;
}
async insertionSort(array: SortData[], animationSpeed: number): Promise<void> {
const n = array.length;
for (let i = 1; i < n; i++) {
const key = array[i].value;
array[i].state = 'comparing';
let j = i - 1;
private quickSortHelper(arr: SortData[], low: number, high: number, snapshots: SortSnapshot[]) {
if (low < high) {
const pi = this.partition(arr, low, high, snapshots);
while (j >= 0 && array[j].value > key) {
array[j].state = 'comparing';
await this.delay(animationSpeed);
array[j + 1].value = array[j].value;
array[j + 1].state = 'unsorted'; // Reset after shifting
j = j - 1;
}
await this.delay(animationSpeed); // Delay after loop for final position
array[j + 1].value = key;
array[i].state = 'unsorted'; // Reset original 'key' position
array.forEach((item, idx) => { // Mark sorted up to i
if (idx <= i) {
item.state = 'sorted';
}
});
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));
}
array.forEach(item => item.state = 'sorted'); // Mark all as sorted at the end
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
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

@@ -7,20 +7,34 @@
<div class="controls-panel">
<mat-form-field appearance="fill">
<mat-label>{{ 'ALGORITHM.SORTING.ALGORITHM' | translate }}</mat-label>
<mat-select [(ngModel)]="selectedAlgorithm" [disabled]="isSorting">
<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>
<button mat-raised-button color="primary" (click)="startSorting()" [disabled]="isSorting">
<mat-form-field appearance="outline">
<mat-label>{{ 'ALGORITHM.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> {{ 'ALGORITHM.SORTING.START' | translate }}
</button>
<button mat-raised-button color="warn" (click)="resetSorting()" [disabled]="isSorting">
<button mat-raised-button color="warn" (click)="resetSorting()">
<mat-icon>refresh</mat-icon> {{ 'ALGORITHM.SORTING.RESET' | translate }}
</button>
<button mat-raised-button (click)="generateNewArray()" [disabled]="isSorting">
<button mat-raised-button (click)="generateNewArray()">
<mat-icon>add_box</mat-icon> {{ 'ALGORITHM.SORTING.GENERATE_NEW_ARRAY' | translate }}
</button>
</div>

View File

@@ -35,7 +35,7 @@
.bar {
flex-grow: 1;
background-color: #424242; /* Default unsorted color */
transition: height 0.1s ease-in-out, background-color 0.1s ease-in-out;
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 */
@@ -56,7 +56,7 @@
.info-panel {
margin-top: 10px;
font-size: 0.9em;
color: #555;
color: #FFFFFF;
}
}
}

View File

@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
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";
@@ -7,86 +7,134 @@ 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 } from './sorting.models';
import {SortData, SortSnapshot} from './sorting.models';
import { FormsModule } from '@angular/forms';
import {MatInput} from '@angular/material/input';
@Component({
selector: 'app-sorting',
standalone: true,
imports: [CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, TranslateModule, FormsModule],
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[] = [];
arraySize: number = 50;
unsortedArrayCopy: SortData[] = [];
arraySize: number = 100;
maxArrayValue: number = 100;
animationSpeed: number = 10; // Milliseconds per step
animationSpeed: number = 50; // Milliseconds per step
// Placeholder for available sorting algorithms
availableAlgorithms: { name: string; value: string }[] = [
{ name: 'Bubble Sort', value: 'bubbleSort' },
{ name: 'Selection Sort', value: 'selectionSort' },
{ name: 'Insertion Sort', value: 'insertionSort' },
{ name: 'Quick Sort', value: 'quickSort' },
{ name: 'Heap Sort', value: 'heapSort' },
];
selectedAlgorithm: string = this.availableAlgorithms[0].value;
isSorting: boolean = false;
executionTime: number = 0;
constructor(private sortingService: SortingService) { }
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.isSorting = false;
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: Math.floor(Math.random() * this.maxArrayValue) + 1,
value: randomValue,
state: 'unsorted'
});
this.unsortedArrayCopy.push({
value: randomValue,
state: 'unsorted'
});
}
}
async startSorting(): Promise<void> {
if (this.isSorting) {
return;
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';
}
console.log('Starting sorting...');
this.isSorting = true;
this.executionTime = 0;
}
// Reset states for visualization
this.sortArray.forEach(item => item.state = 'unsorted');
async startSorting(): Promise<void> {
this.resetSorting();
const startTime = performance.now();
console.log("Select algorithm ", this.selectedAlgorithm);
let snapshots: SortSnapshot[] = [];
switch (this.selectedAlgorithm) {
case 'bubbleSort':
await this.sortingService.bubbleSort(this.sortArray, this.animationSpeed);
snapshots = this.sortingService.bubbleSort(this.sortArray);
break;
case 'selectionSort':
await this.sortingService.selectionSort(this.sortArray, this.animationSpeed);
case 'quickSort':
snapshots = this.sortingService.quickSort(this.sortArray);
break;
case 'insertionSort':
await this.sortingService.insertionSort(this.sortArray, this.animationSpeed);
break;
default:
console.warn('Unknown sorting algorithm selected:', this.selectedAlgorithm);
case 'heapSort':
snapshots = this.sortingService.heapSort(this.sortArray);
break;
}
console.log("Done with sorting...");
const endTime = performance.now();
this.executionTime = Math.round(endTime - startTime);
this.isSorting = false;
this.sortArray.forEach(item => item.state = 'sorted'); // Mark all as sorted after completion
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.generateNewArray();
this.stopAnimations();
this.resetSortState();
}
}

View File

@@ -2,3 +2,7 @@ export interface SortData {
value: number;
state: 'sorted' | 'comparing' | 'unsorted';
}
export interface SortSnapshot {
array: SortData[];
}

View File

@@ -334,7 +334,8 @@
"START": "Sortierung starten",
"RESET": "Zurücksetzen",
"GENERATE_NEW_ARRAY": "Neues Array generieren",
"EXECUTION_TIME": "Ausführungszeit"
"EXECUTION_TIME": "Ausführungszeit",
"ARRAY_SIZE": "Anzahl der Balken"
}
}
}

View File

@@ -334,7 +334,8 @@
"START": "Start Sorting",
"RESET": "Reset",
"GENERATE_NEW_ARRAY": "Generate New Array",
"EXECUTION_TIME": "Execution Time"
"EXECUTION_TIME": "Execution Time",
"ARRAY_SIZE": "Number of Bars"
}
}
}