add selection sort

This commit is contained in:
Aditya 2024-05-28 10:03:56 +05:30
parent 810d524abc
commit 8b6289d4f6
Signed by: aditya
SSH key fingerprint: SHA256:jL1IvWsjjlPtw6HvDIHfXfhO9IkIokNEyIfuFhSdoyU
4 changed files with 241 additions and 0 deletions

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# ---> Hugo
# Generated files by hugo
/public/
/resources/_gen/
/assets/jsconfig.json
hugo_stats.json
# Executable may be added to repository
hugo.exe
hugo.darwin
hugo.linux
# Temporary lock file while building
/.hugo_build.lock
# Ignore swap files
*.swp
*~

9
content/_index.md Normal file
View file

@ -0,0 +1,9 @@
---
title: "Data Strcutures and Algorithms"
date: 2024-05-28T08:15:27+05:30
type: docs
---
# Data Structures and Algorithms
On this website, I document my explorations into the realm of Data Structures and Algorithms to serve as a personal resource for future reflection.

View file

@ -0,0 +1,68 @@
---
title: "Sorting"
date: 2024-05-28T07:49:20+05:30
bookCollapseSection: true
---
# Sorting
This section contains entries related to sorting algorithms.
Sorting in data structures and algorithms involves rearranging a collection of items, such as
numbers or strings, into a sequence or list where the elements are organized according to a
particular criterion (e.g., ascending numerical value). Various algorithms have been developed for
this purpose, each with its own advantages and trade-offs in terms of time complexity, space
efficiency, stability, adaptability, and ease of implementation. Some widely known sorting
algorithms include:
1. **Bubble Sort** - A simple comparison-based algorithm that repeatedly steps through the list,
compares adjacent elements, and swaps them if they are in the wrong order. It has a time complexity
of O(n^2) in its average and worst cases.
2. **Selection Sort** - This algorithm divides the input list into two parts: a sorted sublist of
items that are built up from left to right at the front (or beginning) of the list, and an unsorted
sublist. On each iteration, it selects the smallest or largest element (depending on sorting order)
from the unsorted sublist and moves that element to the end of the sorted sublist. Its time
complexity is O(n^2).
3. **Insertion Sort** - Builds the final sorted array one item at a time. It has an average and
worst-case performance of O(n^2), but it's efficient for small datasets or nearly sorted arrays,
with best-case time complexity being O(n).
4. **Merge Sort** - A divide-and-conquer algorithm that divides the input array into two halves,
recursively sorts them, and then merges the two sorted halves together. It has a time complexity of
O(n log n) in all cases.
5. **Quick Sort** - Also utilizes a divide-and-conquer strategy by selecting a 'pivot' element from
the array and partitioning the other elements into two sub-arrays, according to whether they are
less than or greater than the pivot. The time complexity in average cases is O(n log n), but it can
degrade to O(n^2) if the smallest or largest element is always chosen as the pivot.
6. **Heap Sort** - Builds a heap data structure from the input data and then repeatedly extracts
the maximum (or minimum) element from the heap, swapping it with the last item in the unsorted
portion of the array, and re-heaping the reduced array. The time complexity is O(n log n).
7. **Radix Sort** - This non-comparative integer sorting algorithm sorts data with integer keys by
grouping keys by the individual digits which share the same significant position and value (radix).
It has a linear time complexity of O(nk) where k is the number of passes of the sorting algorithm
and n is the number of elements.
8. **Counting Sort** - Suitable for sorting integer arrays when the range of potential items in the
input (i.e., their possible key values) is relatively small compared to the number of items. Its
time complexity varies based on the specific implementation but generally has a linear time
complexity, O(n+k), where k is the range of the input.
9. **Bucket Sort** - Works by distributing elements into several 'buckets' and then sorting these
buckets individually using a different sorting algorithm or by recursively applying bucket sort to
each bucket. Its time complexity can be as good as O(n+k) where n is the number of elements and k
is the number of buckets, but it often performs better than O(n log n).
10. **Timsort** - A hybrid sorting algorithm derived from merge sort and insertion sort designed to
perform well on many kinds of real-world data. It's used as the default sort in Python and Java.
Timsort has a time complexity of O(n log n) for average and worst cases.
The choice of a particular sorting algorithm depends on several factors, including but not limited
to: the size and nature of the dataset, the computational resources available (such as CPU and
memory), the desired level of stability in the output, whether parallel computing is involved, etc.
{{<section summary >}}

View file

@ -0,0 +1,146 @@
---
title: "Selection Sort"
date: 2024-05-28T07:49:30+05:30
---
# Selection Sort
Selection Sort is a simple comparison-based sorting algorithm. It operates by repeatedly finding
the minimum element (considering ascending order) from the unsorted part of an array and putting it
at the beginning. The process involves dividing the input list into two parts: a sorted sublist
which is built up from left to right, and an unsorted sublist where elements are not yet in their
final position.
<!--more-->
## Algorithm
Here's how Selection Sort works step by step:
1. Start with the first element of the array as the minimum.
2. Compare this "current" minimum with the rest of the array to find a new minimum.
3. Once you have found the true minimum, swap it with the value at the current position.
4. Move one position ahead in the array and consider this element as part of the unsorted segment
while treating the remaining elements as the sorted sublist.
5. Repeat steps 2-4 until the entire list is sorted.
The algorithm's time complexity is O(n<sup>2</sup>) for all cases (worst, average, and best), making it
inefficient on large lists compared to more advanced algorithms like quicksort, mergesort or
heapsort. However, Selection Sort has its advantages such as simplicity and performing only a
minimal number of swaps, which can be beneficial when the cost of swap is high.
## Code
Here is an implementation in C++.
```cpp
import <print>;
import <vector>;
constexpr auto sort(std::vector<ssize_t> &vec) -> void {
for (auto it1{vec.begin()}; it1 < vec.end(); ++it1) {
auto it{it1};
for (auto it2{it1 + 1}; it2 < vec.end(); ++it2) {
if (*it2 < *it) {
it = it2;
}
}
auto temp{*it1};
*it1 = *it;
*it = temp;
}
}
int main() {
std::vector<ssize_t> v{4, 7, 3, 7, 2, 7, 4, 64, 32, 65,
32, 4, 5, 5, 6456, 4, 5, 53, 5};
sort(v);
for (const auto &a : v) {
std::print("{} ", a);
}
return 0;
}
```
## Explanation
### Function Signature
```cpp
constexpr auto sort(std::vector<ssize_t> &vec) -> void
```
- `constexpr`: This keyword indicates that the function can be evaluated at compile time if provided with constant expressions. This is useful for performance optimization in some cases.
- `auto sort`: Uses the auto keyword to automatically deduce the return type. In this case, the return type is explicitly specified as `void`.
- `(std::vector<ssize_t> &vec)`: The function takes a reference to a `std::vector` of `ssize_t` integers as its parameter. Using a reference avoids copying the vector and allows the function to modify the original vector.
- `-> void`: This specifies the return type of the function, which is `void` (meaning it doesn't return a value).
### Function Body
```cpp
{
for (auto it1{vec.begin()}; it1 < vec.end(); ++it1) {
auto it{it1};
for (auto it2{it1 + 1}; it2 < vec.end(); ++it2) {
if (*it2 < *it) {
it = it2;
}
}
auto temp{*it1};
*it1 = *it;
*it = temp;
}
}
```
1. **Outer Loop (Selection Sort)**:
```cpp
for (auto it1{vec.begin()}; it1 < vec.end(); ++it1)
```
- This loop iterates over each element of the vector from the beginning to the end.
- `it1` is an iterator starting from the beginning of the vector and moving towards the end.
2. **Inner Loop (Finding Minimum)**
```cpp
auto it{it1};
for (auto it2{it1 + 1}; it2 < vec.end(); ++it2) {
if (*it2 < *it) {
it = it2;
}
}
```
- The inner loop starts from the next element after `it1` and iterates to the end of the vector.
- `it` initially points to `it1`, which is the current element in the outer loop.
- `it2` iterates over the remaining elements to find the smallest element.
- If `*it2` (the value pointed to by `it2`) is smaller than `*it` (the value pointed to by `it`), `it` is updated to point to `it2`.
3. **Swapping Elements**:
```cpp
auto temp{*it1};
*it1 = *it;
*it = temp;
```
- After finding the smallest element in the unsorted portion of the vector, the smallest element (pointed to by `it`) is swapped with the element at the position `it1`.
- `temp` temporarily holds the value of `*it1`.
- `*it1` is set to `*it` (the smallest element found).
- `*it` is then set to `temp` to complete the swap.
## Output
```console
./main
2 3 4 4 4 4 5 5 5 5 7 7 7 32 32 53 64 65 6456 [ble: EOF]
```