diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfd72cc --- /dev/null +++ b/.gitignore @@ -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 +*~ diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..4a4d4de --- /dev/null +++ b/content/_index.md @@ -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. diff --git a/content/docs/dsa/sorting/_index.md b/content/docs/dsa/sorting/_index.md new file mode 100644 index 0000000..a0ef34f --- /dev/null +++ b/content/docs/dsa/sorting/_index.md @@ -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. + +{{
}} diff --git a/content/docs/dsa/sorting/selection-sort.md b/content/docs/dsa/sorting/selection-sort.md new file mode 100644 index 0000000..8499b87 --- /dev/null +++ b/content/docs/dsa/sorting/selection-sort.md @@ -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. + + + +## 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(n2) 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 ; +import ; + +constexpr auto sort(std::vector &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 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 &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 &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] +```