diff --git a/src/app/pages/algorithms/sorting/service/sorting.service.ts b/src/app/pages/algorithms/sorting/service/sorting.service.ts index bbad7b1..d0972d2 100644 --- a/src/app/pages/algorithms/sorting/service/sorting.service.ts +++ b/src/app/pages/algorithms/sorting/service/sorting.service.ts @@ -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 { - 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 { - 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 { - 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 { - 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; } } diff --git a/src/app/pages/algorithms/sorting/sorting.component.html b/src/app/pages/algorithms/sorting/sorting.component.html index 8133128..e9dc6d0 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.html +++ b/src/app/pages/algorithms/sorting/sorting.component.html @@ -7,20 +7,34 @@
{{ 'ALGORITHM.SORTING.ALGORITHM' | translate }} - + @for (algo of availableAlgorithms; track algo.value) { {{ algo.name }} } -
+
+ - -
diff --git a/src/app/pages/algorithms/sorting/sorting.component.scss b/src/app/pages/algorithms/sorting/sorting.component.scss index 128fcaf..b911eac 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.scss +++ b/src/app/pages/algorithms/sorting/sorting.component.scss @@ -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; } } } diff --git a/src/app/pages/algorithms/sorting/sorting.component.ts b/src/app/pages/algorithms/sorting/sorting.component.ts index 43bd720..a247b31 100644 --- a/src/app/pages/algorithms/sorting/sorting.component.ts +++ b/src/app/pages/algorithms/sorting/sorting.component.ts @@ -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 { - 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 { + 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(); } } diff --git a/src/app/pages/algorithms/sorting/sorting.models.ts b/src/app/pages/algorithms/sorting/sorting.models.ts index 2741353..e4d8bce 100644 --- a/src/app/pages/algorithms/sorting/sorting.models.ts +++ b/src/app/pages/algorithms/sorting/sorting.models.ts @@ -2,3 +2,7 @@ export interface SortData { value: number; state: 'sorted' | 'comparing' | 'unsorted'; } + +export interface SortSnapshot { + array: SortData[]; +} diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 1d0e778..428a3e2 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -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" } } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 15c429a..daaa808 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -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" } } }