mutecipher

An algorithm is a finite sequence of steps or instructions for solving a computational problem or achieving a specific task.

Sorting

A sorting algorithm is a method for arranging data in a particular order, such as alphabetical, numerical, or ascending/descending order.

Bubble Sort

The Bubble Sort algorithm is a simple sorting technique that repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order. This process is repeated until no more swaps are needed, indicating that the list is sorted. It gets its name from the way smaller elements "bubble up" to their correct position during each iteration, creating a visual bubble effect. Despite its simplicity, Bubble Sort has a time complexity of O(n2), making it inefficient for large datasets and less preferred over other sorting algorithms like QuickSort or MergeSort.

def bubble_sort(items)
  num = items.length
  for i in 0..num-1
    for j in 0..num-i-1
      if items[j] >= items[j + 1]
        temp = items[j]
        items[j] = items[j + 1]
        items[j + 1] = temp
      end
    end
  end
  return items
end

Insertion Sort

The Insertion Sort algorithm is an efficient in-place sorting technique that builds a final sorted array (or list) one item at a time. It works by iterating through the list, inserting each new element into its correct position in the already sorted sublist. This process continues until all elements are sorted. Its average and worst-case time complexity is O(n2), making it best suited for small to moderately-sized input data sets. Despite this limitation, Insertion Sort remains popular due to its simplicity and low memory requirements.

def insertion_sort(items)
  for i in 1..items.length
    j = 1
    while j > 0
      if items[j - 1] > items[j]
        temp = items[j]
        items[j] = items[j - 1]
        items[j - 1] = temp
      else
        break
      end
      j -= 1
    end
  end
  return items
end

Merge Sort

The Merge Sort algorithm is an efficient, divide-and-conquer sorting technique that recursively divides an unsorted list into smaller sublists until each sublist contains only one element, which are already sorted. It then merges these sublists back together in a sorted manner using the merge operation. Merge Sort has a worst-case time complexity of O(n log n), making it highly scalable and suitable for large input data sets. Its space complexity is also O(n), as it requires additional memory to store the intermediate arrays during the merging process.

def merge(items, left, middle, right)
  left_count = middle - left + 1
  left = Array.new(left_count)
  right_count = right - middle
  right = Array.new(right_count)

  for i in 0..left_count
    left[i] = items[left + i]
  end

  for i in 0..right_count
    right[i] = items[middle + 1 + i]
  end

  i = 0
  j = 0

  for k in left..right
    if i >= left_count
      items[k] = right[j]
      j += 1
    elsif j >= right_count
      items[k] = left[i]
      i += 1
    elsif left[i] > right[i]
      items[k] = right[j]
      j += 1
    else
      items[k] = left[i]
      i += 1
    end
  end
end

def merge_sort(items, left, right)
  if left < right
    middle = left + (right - left) / 2
    merge_sort(items, left, middle)
    merge_sort(items, middle + 1, right)
    merge(items, left, middle, right)
  end
end

Quick Sort

The Quick Sort algorithm is an efficient, divide-and-conquer sorting technique that selects a pivot element and partitions the input array around it into two subarrays: elements less than the pivot and elements greater than the pivot. This process is recursively applied to each subarray until all elements are sorted. The average time complexity of Quick Sort is O(n log n), making it a popular choice for large data sets. Its space complexity, however, can be O(log n) in the best case or O(n) in the worst case, depending on how the partitioning process is done and the amount of recursion depth.

def quick_sort(items, low, high)
  if low < high
    p = partition(items, low, high)
    quick_sort(items, low, partition - 1)
    quick_sort(items, partition + 1, high)
  end
end

def partition(items, low, high)
  i = low
  j = high + 1
  pivot = items[low]

  while true
    begin
      i += 1
      break if i == high
    end

    while items[i] < pivot
      begin
        j -= 1
        break if j == low
      end
    end

    while items[j] > pivot
      break if i >= j
      temp = items[i]
      items[i] = items[j]
      items[j] = temp
    end
  end

  temp = items[low]
  items[low] = items[j]
  items[j] = temp

  return j
end

Searching

A searching algorithm is a method for locating specific data or information within a larger collection, such as a database or array, based on given criteria or conditions.

The Binary Search algorithm is an efficient searching technique that works by repeatedly dividing an sorted array into halves. Given a target value, it compares the middle element with the target and eliminates half of the remaining elements based on this comparison. This process continues until the target is found or the remaining elements are empty. The time complexity of Binary Search is O(log n), making it highly efficient for large datasets that are already sorted. Its space complexity is O(1) as only a few variables are required to store indices and the comparison result.

def binary_search(items, key)
  low = 0
  high = items.length + 1

  while (low <= high)
    mid = low + (high - low) / 2

    if items[mid] == key
      return mid
    elsif items[mid] < key
      low = mid + 1
    else
      high = mid - 1
    end
  end

  return nil
end

Shuffling

A shuffling algorithm is a method for randomly rearranging elements in a data structure, often used to simulate chance events or generate permutations for various applications.

Array Shuffle

The Array Shuffle algorithm, also known as Fisher-Yates or Knuth shuffle, is a simple in-place algorithm used to randomly shuffle an array by repeatedly swapping elements with other elements in the array based on random indices. Each swap operation is performed using a random index from the remaining elements. The time complexity of Array Shuffle is O(n), and its space complexity is O(1) as it operates directly on the input array without creating any additional data structures. It is widely used when generating random permutations or when dealing with large datasets where memory usage is a concern.

def shuffle(items)
  items.each_with_index |_, i|
    j = rand(items.length)
    temp = items[i]
    items[i] = items[j]
    items[j] = temp
  end
end