feature/sortingAlgorithm #8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,7 @@ export interface SortData {
|
||||
value: number;
|
||||
state: 'sorted' | 'comparing' | 'unsorted';
|
||||
}
|
||||
|
||||
export interface SortSnapshot {
|
||||
array: SortData[];
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user