diff --git a/DIRECTORY.md b/DIRECTORY.md index 69a9bf8..91687ec 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1,12 +1,22 @@ +## Backtracking + * [Generate Paranthesis](https://github.com/TheAlgorithms/Ruby/blob/master/backtracking/generate_paranthesis.rb) + ## Bit Manipulation + * [Binary And Operator](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/binary_and_operator.rb) + * [Binary Count Setbits](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/binary_count_setbits.rb) + * [Binary Count Trailing Zeroes](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/binary_count_trailing_zeroes.rb) + * [Binary Or Operator](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/binary_or_operator.rb) + * [Binary Xor Operator](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/binary_xor_operator.rb) * [Power Of Two](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/power_of_two.rb) + * [Single Bit Binary Operations](https://github.com/TheAlgorithms/Ruby/blob/master/bit_manipulation/single_bit_binary_operations.rb) ## Ciphers * [Merkle Hellman Cryptosystem](https://github.com/TheAlgorithms/Ruby/blob/master/ciphers/merkle_hellman_cryptosystem.rb) ## Conversions * [Temperature Conversions](https://github.com/TheAlgorithms/Ruby/blob/master/conversions/temperature_conversions.rb) + * [Weight Conversions](https://github.com/TheAlgorithms/Ruby/blob/master/conversions/weight_conversions.rb) ## Data Structures * Arrays @@ -15,15 +25,22 @@ * [Find The Highest Altitude](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/find_the_highest_altitude.rb) * [Fizz Buzz](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/fizz_buzz.rb) * [Get Products Of All Other Elements](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/get_products_of_all_other_elements.rb) + * [Good Pairs](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/good_pairs.rb) + * [Intersection](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/intersection.rb) + * [Next Greater Element](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/next_greater_element.rb) * [Remove Elements](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/remove_elements.rb) * [Richest Customer Wealth](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/richest_customer_wealth.rb) + * [Shortest Word Distance](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/shortest_word_distance.rb) * [Shuffle Array](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/shuffle_array.rb) * [Single Number](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/single_number.rb) * [Sort Squares Of An Array](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/sort_squares_of_an_array.rb) + * [Sorted Arrays Intersection](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/sorted_arrays_intersection.rb) * Strings * [Anagram Checker](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/strings/anagram_checker.rb) * [Jewels And Stones](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/strings/jewels_and_stones.rb) + * [Palindrome](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/strings/palindrome.rb) * [Remove Vowels](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/strings/remove_vowels.rb) + * [Sudoku](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/sudoku.rb) * [Two Sum](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/two_sum.rb) * [Two Sum Ii](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/arrays/two_sum_ii.rb) * Binary Trees @@ -32,15 +49,21 @@ * [Postorder Traversal](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/binary_trees/postorder_traversal.rb) * [Preorder Traversal](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/binary_trees/preorder_traversal.rb) * Hash Table + * [Anagram Checker](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/anagram_checker.rb) + * [Arrays Intersection](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/arrays_intersection.rb) + * [Common Characters](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/common_characters.rb) * [Find All Duplicates In An Array](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/find_all_duplicates_in_an_array.rb) + * [Good Pairs](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/good_pairs.rb) * [Isomorphic Strings](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/isomorphic_strings.rb) * [Richest Customer Wealth](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/richest_customer_wealth.rb) * [Two Sum](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/two_sum.rb) + * [Uncommon Words](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/hash_table/uncommon_words.rb) * Linked Lists * [Circular Linked List](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/linked_lists/circular_linked_list.rb) * [Doubly Linked List](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/linked_lists/doubly_linked_list.rb) * [Singly Linked List](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/linked_lists/singly_linked_list.rb) * Queues + * [Circular Queue](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/queues/circular_queue.rb) * [Queue](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/queues/queue.rb) * Stacks * [Stack](https://github.com/TheAlgorithms/Ruby/blob/master/data_structures/stacks/stack.rb) @@ -54,22 +77,36 @@ ## Dynamic Programming * [Coin Change](https://github.com/TheAlgorithms/Ruby/blob/master/dynamic_programming/coin_change.rb) + * [Count Sorted Vowel Strings](https://github.com/TheAlgorithms/Ruby/blob/master/dynamic_programming/count_sorted_vowel_strings.rb) * [Fibonacci](https://github.com/TheAlgorithms/Ruby/blob/master/dynamic_programming/fibonacci.rb) ## Maths * [Abs](https://github.com/TheAlgorithms/Ruby/blob/master/maths/abs.rb) + * [Abs Max](https://github.com/TheAlgorithms/Ruby/blob/master/maths/abs_max.rb) + * [Abs Min](https://github.com/TheAlgorithms/Ruby/blob/master/maths/abs_min.rb) * [Abs Test](https://github.com/TheAlgorithms/Ruby/blob/master/maths/abs_test.rb) + * [Add](https://github.com/TheAlgorithms/Ruby/blob/master/maths/add.rb) * [Add Digits](https://github.com/TheAlgorithms/Ruby/blob/master/maths/add_digits.rb) * [Aliquot Sum](https://github.com/TheAlgorithms/Ruby/blob/master/maths/aliquot_sum.rb) * [Aliquot Sum Test](https://github.com/TheAlgorithms/Ruby/blob/master/maths/aliquot_sum_test.rb) + * [Armstrong Number](https://github.com/TheAlgorithms/Ruby/blob/master/maths/armstrong_number.rb) + * [Average Mean](https://github.com/TheAlgorithms/Ruby/blob/master/maths/average_mean.rb) + * [Average Median](https://github.com/TheAlgorithms/Ruby/blob/master/maths/average_median.rb) * [Binary To Decimal](https://github.com/TheAlgorithms/Ruby/blob/master/maths/binary_to_decimal.rb) * [Ceil](https://github.com/TheAlgorithms/Ruby/blob/master/maths/ceil.rb) * [Ceil Test](https://github.com/TheAlgorithms/Ruby/blob/master/maths/ceil_test.rb) + * [Count Sorted Vowel Strings](https://github.com/TheAlgorithms/Ruby/blob/master/maths/count_sorted_vowel_strings.rb) * [Decimal To Binary](https://github.com/TheAlgorithms/Ruby/blob/master/maths/decimal_to_binary.rb) + * [Factorial](https://github.com/TheAlgorithms/Ruby/blob/master/maths/factorial.rb) + * [Factorial Non Recursive Non Iterative](https://github.com/TheAlgorithms/Ruby/blob/master/maths/factorial_non_recursive_non_iterative.rb) * [Fibonacci](https://github.com/TheAlgorithms/Ruby/blob/master/maths/fibonacci.rb) + * [Find Max](https://github.com/TheAlgorithms/Ruby/blob/master/maths/find_max.rb) + * [Find Min](https://github.com/TheAlgorithms/Ruby/blob/master/maths/find_min.rb) + * [Lucas Series](https://github.com/TheAlgorithms/Ruby/blob/master/maths/lucas_series.rb) * [Number Of Digits](https://github.com/TheAlgorithms/Ruby/blob/master/maths/number_of_digits.rb) * [Power Of Two](https://github.com/TheAlgorithms/Ruby/blob/master/maths/power_of_two.rb) * [Prime Number](https://github.com/TheAlgorithms/Ruby/blob/master/maths/prime_number.rb) + * [Roman To Integer](https://github.com/TheAlgorithms/Ruby/blob/master/maths/roman_to_integer.rb) * [Square Root](https://github.com/TheAlgorithms/Ruby/blob/master/maths/square_root.rb) * [Square Root Test](https://github.com/TheAlgorithms/Ruby/blob/master/maths/square_root_test.rb) * [Sum Of Digits](https://github.com/TheAlgorithms/Ruby/blob/master/maths/sum_of_digits.rb) @@ -98,28 +135,36 @@ * [Sol1](https://github.com/TheAlgorithms/Ruby/blob/master/project_euler/problem_5/sol1.rb) ## Searches - * [Binary Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/binary_search.rb) - * [Depth First Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/depth_first_search.rb) - * [Double Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/double_linear_search.rb) - * [Jump Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/jump_search.rb) - * [Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/linear_search.rb) - * [Recursive Double Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/recursive_double_linear_search.rb) - * [Recursive Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/recursive_linear_search.rb) - * [Ternary Search](https://github.com/TheAlgorithms/Ruby/blob/master/Searches/ternary_search.rb) + * [Binary Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/binary_search.rb) + * [Depth First Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/depth_first_search.rb) + * [Double Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/double_linear_search.rb) + * [Jump Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/jump_search.rb) + * [Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/linear_search.rb) + * [Recursive Double Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/recursive_double_linear_search.rb) + * [Recursive Linear Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/recursive_linear_search.rb) + * [Ternary Search](https://github.com/TheAlgorithms/Ruby/blob/master/searches/ternary_search.rb) ## Sorting + * [Bead Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bead_sort.rb) + * [Bead Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bead_sort_test.rb) * [Bogo Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bogo_sort.rb) * [Bogo Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bogo_sort_test.rb) * [Bubble Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bubble_sort.rb) * [Bubble Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bubble_sort_test.rb) * [Bucket Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bucket_sort.rb) * [Bucket Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/bucket_sort_test.rb) + * [Cocktail Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/cocktail_sort.rb) + * [Cocktail Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/cocktail_sort_test.rb) + * [Comb Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/comb_sort.rb) + * [Comb Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/comb_sort_test.rb) * [Heap Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/heap_sort.rb) * [Heap Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/heap_sort_test.rb) * [Insertion Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/insertion_sort.rb) * [Insertion Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/insertion_sort_test.rb) * [Merge Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/merge_sort.rb) * [Merge Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/merge_sort_test.rb) + * [Pancake Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/pancake_sort.rb) + * [Pancake Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/pancake_sort_test.rb) * [Quicksort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/quicksort.rb) * [Quicksort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/quicksort_test.rb) * [Radix Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/radix_sort.rb) @@ -128,4 +173,5 @@ * [Selection Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/selection_sort_test.rb) * [Shell Sort](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/shell_sort.rb) * [Shell Sort Test](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/shell_sort_test.rb) + * [Sort Color](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/sort_color.rb) * [Sort Tests](https://github.com/TheAlgorithms/Ruby/blob/master/sorting/sort_tests.rb) diff --git a/backtracking/generate_paranthesis.rb b/backtracking/generate_paranthesis.rb new file mode 100644 index 0000000..5c0e3fa --- /dev/null +++ b/backtracking/generate_paranthesis.rb @@ -0,0 +1,106 @@ +# Given n pairs of parentheses, write a function to generate all combinations +# of well-formed parentheses. +# +# Example 1: +# +# Input: n = 3 +# Output: ["((()))","(()())","(())()","()(())","()()()"] +# Example 2: +# +# Input: n = 1 +# Output: ["()"] +# +# +# Constraints: +# +# 1 <= n <= 8 + +# Approach: +# +# Let's only add '(' or ')' when we know it will remain a valid sequence. +# We can do this by keeping track of the number of opening and closing brackets +# we have placed so far. +# +# We can start an opening bracket if we still have one (of n) left to place. +# And we could start a closing bracket if it'd not exceed the number of opening +# brackets. + +# Complexity Analysis +# +# Time Complexity: O(4^n/sqrt(n)). Each valid sequence has at most n steps during the backtracking procedure. +# Space Complexity: O(4^n/sqrt(n)), as described above, and using O(n) space to store the sequence. + +# Refer to the attached diagram for recursion, +# The numbers next to each node are the counts of left and right parantheses + +# @param {Integer} n +# @return {String[]} +def generate_parenthesis(n) + parenthesis = [] + backtrack(parenthesis, "", 0, 0, n) + parenthesis +end + +def backtrack(parenthesis, curr, open, close, max) + if curr.length == max * 2 + parenthesis.push(curr) + return + end + + if open < max + backtrack(parenthesis, curr + "(", open + 1, close, max) + end + + if close < open + backtrack(parenthesis, curr + ")", open, close + 1, max) + end +end + +n = 3 +print(generate_parenthesis(n)) +# Output: ["((()))","(()())","(())()","()(())","()()()"] + +# *** Example: n = 3 *** Space after each DFS instance +# backtrack called with 0 0 [] +# backtrack called with ( 1 0 [] +# backtrack called with (( 2 0 [] +# backtrack called with ((( 3 0 [] +# backtrack called with ((() 3 1 [] +# backtrack called with ((()) 3 2 [] +# backtrack called with ((())) 3 3 [] +# backtrack return with ((()) 3 2 ["((()))"] +# backtrack return with ((() 3 1 ["((()))"] +# backtrack return with ((( 3 0 ["((()))"] +# backtrack called with (() 2 1 ["((()))"] +# backtrack called with (()( 3 1 ["((()))"] +# backtrack called with (()() 3 2 ["((()))"] +# backtrack called with (()()) 3 3 ["((()))"] +# backtrack return with (()() 3 2 ["((()))", "(()())"] +# backtrack return with (()( 3 1 ["((()))", "(()())"] +# backtrack called with (()) 2 2 ["((()))", "(()())"] +# backtrack called with (())( 3 2 ["((()))", "(()())"] +# backtrack called with (())() 3 3 ["((()))", "(()())"] +# backtrack return with (())( 3 2 ["((()))", "(()())", "(())()"] +# backtrack return with (()) 2 2 ["((()))", "(()())", "(())()"] +# backtrack return with (() 2 1 ["((()))", "(()())", "(())()"] +# backtrack return with (( 2 0 ["((()))", "(()())", "(())()"] +# backtrack called with () 1 1 ["((()))", "(()())", "(())()"] +# backtrack called with ()( 2 1 ["((()))", "(()())", "(())()"] +# backtrack called with ()(( 3 1 ["((()))", "(()())", "(())()"] +# backtrack called with ()(() 3 2 ["((()))", "(()())", "(())()"] +# backtrack called with ()(()) 3 3 ["((()))", "(()())", "(())()"] +# backtrack return with ()(() 3 2 ["((()))", "(()())", "(())()", "()(())"] +# backtrack return with ()(( 3 1 ["((()))", "(()())", "(())()", "()(())"] +# backtrack called with ()() 2 2 ["((()))", "(()())", "(())()", "()(())"] +# backtrack called with ()()( 3 2 ["((()))", "(()())", "(())()", "()(())"] +# backtrack called with ()()() 3 3 ["((()))", "(()())", "(())()", "()(())"] +# backtrack return with ()()( 3 2 ["((()))", "(()())", "(())()", "()(())", "()()()"] +# backtrack return with ()() 2 2 ["((()))", "(()())", "(())()", "()(())", "()()()"] +# backtrack return with ()( 2 1 ["((()))", "(()())", "(())()", "()(())", "()()()"] +# backtrack return with () 1 1 ["((()))", "(()())", "(())()", "()(())", "()()()"] +# backtrack return with ( 1 0 ["((()))", "(()())", "(())()", "()(())", "()()()"] +# backtrack return with 0 0 ["((()))", "(()())", "(())()", "()(())", "()()()"] + +n = 1 +print(generate_parenthesis(n)) +# Output: ["()"] diff --git a/bit_manipulation/binary_and_operator.rb b/bit_manipulation/binary_and_operator.rb new file mode 100644 index 0000000..4cc0f5a --- /dev/null +++ b/bit_manipulation/binary_and_operator.rb @@ -0,0 +1,23 @@ +def binary_and(x, y) + raise 'Input must only contain positive integers' if x < 0 or y < 0 + + "0b" + (x & y).to_s(2) +end + +begin + binary_and(-1, 0) +rescue => e + puts e.message +end +# Input must only contain positive integers + +puts binary_and(1, 1) +# 0b1 +puts binary_and(0, 1) +# 0b0 +puts binary_and(1024, 1024) +# 0b10000000000 +puts binary_and(0, 1023) +# 0b0000000000 +puts binary_and(16, 58) +# 0b010000 diff --git a/bit_manipulation/binary_count_setbits.rb b/bit_manipulation/binary_count_setbits.rb new file mode 100644 index 0000000..4deab5a --- /dev/null +++ b/bit_manipulation/binary_count_setbits.rb @@ -0,0 +1,26 @@ +def binary_count_setbits(x) + raise 'Input must be a positive integer' if x < 0 + + binary = x.to_s(2) + + binary.chars.map { |c| c.to_i }.reduce(:+) +end + +begin + binary_count_setbits(-1) +rescue => e + puts e.message +end +# Input must be a positive integer + +puts binary_count_setbits(0) +# 0 + +puts binary_count_setbits(1) +# 1 + +puts binary_count_setbits(1024) +# 1 + +puts binary_count_setbits(1023) +# 10 diff --git a/bit_manipulation/binary_count_trailing_zeroes.rb b/bit_manipulation/binary_count_trailing_zeroes.rb new file mode 100644 index 0000000..40eafad --- /dev/null +++ b/bit_manipulation/binary_count_trailing_zeroes.rb @@ -0,0 +1,36 @@ +def binary_count_trailing_zeroes(x) + raise 'Input must be a positive integer' if x < 0 + + binary = x.to_s(2) + + count = 0 + binary.chars.reverse_each do |char| + break if char == "1" + + count += 1 + end + + count +end + +begin + binary_count_trailing_zeroes(-1) +rescue => e + puts e.message +end +# Input must be a positive integer + +puts binary_count_trailing_zeroes(0) +# 1 + +puts binary_count_trailing_zeroes(1023) +# 0 + +puts binary_count_trailing_zeroes(1024) +# 10 + +puts binary_count_trailing_zeroes(54) +# 1 + +puts binary_count_trailing_zeroes(121024) +# 6 diff --git a/bit_manipulation/binary_or_operator.rb b/bit_manipulation/binary_or_operator.rb new file mode 100644 index 0000000..43758b7 --- /dev/null +++ b/bit_manipulation/binary_or_operator.rb @@ -0,0 +1,24 @@ +def binary_or(x, y) + raise 'Input must only contain positive integers' if x < 0 or y < 0 + + "0b" + (x | y).to_s(2) +end + +begin + binary_or(-1, 0) +rescue => e + puts e.message +end +# Input must only contain positive integers + +puts binary_or(1, 1) +# 0b1 +puts binary_or(0, 1) +# 0b1 +puts binary_or(1024, 1024) +# 0b10000000000 +puts binary_or(0, 1023) +# 0b1111111111 +puts binary_or(16, 58) +# 0b110010 + diff --git a/bit_manipulation/binary_xor_operator.rb b/bit_manipulation/binary_xor_operator.rb new file mode 100644 index 0000000..634e8df --- /dev/null +++ b/bit_manipulation/binary_xor_operator.rb @@ -0,0 +1,44 @@ +def binary_xor(x, y) + raise 'Input must only contain positive integers' if x < 0 or y < 0 + + binary_x = x.to_s(2) + binary_y = y.to_s(2) + + if binary_x.length > binary_y.length + prefix = "0" * (binary_x.length - binary_y.length) + binary_y = prefix + binary_y + elsif binary_y.length > binary_x.length + prefix = "0" * (binary_y.length - binary_x.length) + binary_x = prefix + binary_x + end + result = "0b" + binary_x.each_char.with_index do |x_char, i| + y_char = binary_y[i] + + if (x_char == "1" && y_char != "1") || (x_char != "1" && y_char == "1") + result += "1" + else + result += "0" + end + end + + result +end + +begin + binary_xor(-1, 0) +rescue => e + puts e.message +end +# Input must only contain positive integers + +puts binary_xor(1, 1) +# 0b0 +puts binary_xor(0, 1) +# 0b1 +puts binary_xor(1024, 1024) +# 0b00000000000 +puts binary_xor(0, 1023) +# 0b1111111111 +puts binary_xor(16, 58) +# 0b101010 diff --git a/bit_manipulation/single_bit_binary_operations.rb b/bit_manipulation/single_bit_binary_operations.rb new file mode 100644 index 0000000..b7da58e --- /dev/null +++ b/bit_manipulation/single_bit_binary_operations.rb @@ -0,0 +1,100 @@ +def set_bit(x, position) + raise "position must be >= 0" if position < 0 + + x | (1 << position) +end + +puts set_bit(0, 0) +# 1 + +puts set_bit(0, 4) +# 16 + +puts set_bit(8, 3) +# 8 + +puts set_bit(8, 4) +# 24 + +begin + puts set_bit(8, -4) +rescue => e + puts e.message +end +# position must be >= 0 + + +def clear_bit(x, position) + raise "position must be > 0" if position < 0 + + x & ~(1 << position) +end + +puts clear_bit(0, 0) +# 0 + +puts clear_bit(0, 4) +# 0 + +puts clear_bit(8, 3) +# 0 + +puts clear_bit(24, 4) +# 8 + +begin + puts clear_bit(0, -4) +rescue => e + puts e.message +end +# position must be > 0 + +def flip_bit(x, position) + raise "position must be > 0" if position < 0 + + x ^ (1 << position) +end + +puts flip_bit(0, 0) +# 1 + +puts flip_bit(0, 4) +# 16 + +puts flip_bit(8, 3) +# 0 + +puts flip_bit(24, 4) +# 8 + +begin + puts flip_bit(0, -4) +rescue => e + puts e.message +end +# position must be > 0 + +def is_bit_set(x, position) + raise "position must be > 0" if position < 0 + + ((x >> position) & 1) == 1 +end + +puts is_bit_set(0, 0) +# false + +puts is_bit_set(1, 0) +# true + +puts is_bit_set(8, 3) +# true + +puts is_bit_set(24, 4) +# true + +begin + puts is_bit_set(0, -4) +rescue => e + puts e.message +end +# position must be > 0 diff --git a/conversions/weight_conversions.rb b/conversions/weight_conversions.rb new file mode 100644 index 0000000..e1f86f1 --- /dev/null +++ b/conversions/weight_conversions.rb @@ -0,0 +1,106 @@ +# A ruby program for weight conversions + +module WeightConversion + # Kilogram -> Gram = (kilogram_value * 1000) grams + def self.kilogram_to_gram(kilogram_input) + raise StandardError unless Integer === kilogram_input + + gram = kilogram_input * 1000 + + "#{kilogram_input} kg = #{gram} g" + end + + # Gram -> Kilogram = (gram_value / 1000) kilograms + def self.gram_to_kilogram(gram_input) + kilogram = gram_input / 1000 + + "#{gram_input} g = #{kilogram} kg" + end + + # Pound -> Ounce = (pound_value * 16) oz + def self.pound_to_ounce(pound_input) + ounce = pound_input * 16 + + "#{pound_input} lb = #{ounce} oz" + end + + # Ounce -> Pound = (ounce_value / 16) lb + def self.ounce_to_pound(ounce_input) + pound = ounce_input / 16 + + "#{ounce_input} oz = #{pound} lb" + end + + # Kilogram -> Pound = (kilogram_input * 2.205) lb + def self.kilogram_to_pound(kilogram_input) + pound = (kilogram_input * 2.205).round(2) + + "#{kilogram_input} kg = #{pound} lb" + end + + # Pound -> Kilogram = (pound_input / 2.205) kg + def self.pound_to_kilogram(pound_input) + raise StandardError unless Integer === pound_input + + kilogram = (pound_input / 2.205).round(2) + + "#{pound_input} lb = #{kilogram} kg" + end +end + +# +# Valid inputs +# + +puts WeightConversion.kilogram_to_gram(2) +# 2 kg = 2000 g +puts WeightConversion.gram_to_kilogram(3000) +# 3000 g = 3 kg +puts WeightConversion.pound_to_ounce(16) +# 16 lb = 256 oz +puts WeightConversion.ounce_to_pound(16) +# 16 oz = 1 lb +puts WeightConversion.kilogram_to_pound(1) +# 1 kg = 2.21 lb +puts WeightConversion.pound_to_kilogram(100) +# 100 lb = 45.35 kg + +# +# Invalid inputs +# + +begin + puts WeightConversion.kilogram_to_gram("a") +rescue StandardError + puts "Error: Please provide number only!" +end + +begin + puts WeightConversion.kilogram_to_gram("3000") +rescue StandardError + puts "Error: Please provide number only!" +end + +begin + puts WeightConversion.kilogram_to_gram("16") +rescue StandardError + puts "Error: Please provide number only!" +end + +begin + puts WeightConversion.kilogram_to_gram("x ") +rescue StandardError + puts "Error: Please provide number only!" +end + +begin + puts WeightConversion.kilogram_to_gram("weight") +rescue StandardError + puts "Error: Please provide number only!" +end + +begin + puts WeightConversion.kilogram_to_gram("100") +rescue StandardError + puts "Error: Please provide number only!" +end diff --git a/data_structures/arrays/good_pairs.rb b/data_structures/arrays/good_pairs.rb new file mode 100644 index 0000000..b62572d --- /dev/null +++ b/data_structures/arrays/good_pairs.rb @@ -0,0 +1,42 @@ +# Challenge name: Number of good pairs +# +# Given an array of integers nums. +# A pair (i,j) is called good if nums[i] == nums[j] and i < j. +# Return the number of good pairs. +# +# @param {Integer[]} nums +# @return {Integer} +# + +# +# Approach 1: Brute Force +# +# Time Complexity: O(n^2) +# +def num_identical_pairs(nums) + count = 0 + nums.each_with_index do |num, i| + target = num + nums.each_with_index do |num, j| + next if i >= j + if num == target + count += 1 + end + end + end + count +end + +nums = [1, 2, 3, 1, 1, 3] +puts(num_identical_pairs(nums)) +# Output: 4 +# Explanation: There are 4 good pairs (0,3), (0,4), (3,4), (2,5) 0-indexed. + +nums = [1, 1, 1, 1] +puts(num_identical_pairs(nums)) +# Output: 6 +# Explanation: Each pair in the array are good. + +nums = [1, 2, 3] +puts(num_identical_pairs(nums)) +# Output: 0 diff --git a/data_structures/arrays/intersection.rb b/data_structures/arrays/intersection.rb new file mode 100644 index 0000000..dfcd49b --- /dev/null +++ b/data_structures/arrays/intersection.rb @@ -0,0 +1,125 @@ +# Challenge name: Intersection of two arrays ii +# +# Given two arrays, write a function to compute their intersection. +# +# @param {Integer[]} nums1 +# @param {Integer[]} nums2 +# @return {Integer[]} + +# +# Approach 1: Brute Force +# +# Time Complexity: O(n^2) +# +def intersect(arr1, arr2) + result = [] + + if arr1.length < arr2.length + shorter = arr1 + longer = arr2 + else + shorter = arr2 + longer = arr1 + end + + shorter.each do |matcher| + longer.each do |number| + next if number != matcher + result.push(number) + break + end + end + + result +end + +nums1 = [1, 2, 2, 1] +nums2 = [2, 2] +puts intersect(nums1, nums2) +# => [2,2] + +nums1 = [4, 9, 5] +nums2 = [9, 4, 9, 8, 4] +puts intersect(nums1, nums2) +# => [4,9] + +# +# Approach 2: Hash +# +# Complexity Analysis +# +# Time Complexity: O(n+m), where n and m are the lengths of the arrays. +# We iterate through the first, and then through the second array; insert +# and lookup operations in the hash map take a constant time. +# +# Space Complexity: O(min(n,m)). We use hash map to store numbers (and their +# counts) from the smaller array. +# +def intersect(arr1, arr2) + result = [] + + hash = Hash.new(0) + + arr2.each {|num| hash[num] += 1 } + + arr1.each do |num| + if hash.has_key?(num) + result << num if hash[num] >= 1 + hash[num] -= 1 + end + end + + result +end + +nums1 = [1, 2, 2, 1] +nums2 = [2, 2] +puts intersect(nums1, nums2) +# => [2,2] + +nums1 = [4, 9, 5] +nums2 = [9, 4, 9, 8, 4] +puts intersect(nums1, nums2) +# => [4,9] + +# +# Approach 3: Two Pointers +# +# Complexity analysis: + +# Time Complexity: O(nlogn + mlogm), where n and m are the lengths of the arrays. We sort two arrays independently and then do a linear scan. +# Space Complexity: from O(logn+logm) to O(n+m), depending on the implementation of the sorting algorithm. +# +def intersect(nums1, nums2) + result = [] + p1 = 0 + p2 = 0 + nums1 = nums1.sort + nums2 = nums2.sort + while p1 < nums1.length && p2 < nums2.length + if nums1[p1] < nums2[p2] + p1 += 1 + elsif nums1[p1] > nums2[p2] + p2 += 1 + elsif nums1[p1] == nums2[p2] + result << nums1[p1] + p1 += 1 + p2 += 1 + end + end + + result +end +nums1 = [1, 2, 2, 1] +nums2 = [2, 2] +intersect(nums1, nums2) + +nums1 = [1, 2, 2, 1] +nums2 = [2, 2] +puts intersect(nums1, nums2) +# => [2,2] + +nums1 = [4, 9, 5] +nums2 = [9, 4, 9, 8, 4] +puts intersect(nums1, nums2) +# => [4,9] diff --git a/data_structures/arrays/next_greater_element.rb b/data_structures/arrays/next_greater_element.rb new file mode 100644 index 0000000..92b3344 --- /dev/null +++ b/data_structures/arrays/next_greater_element.rb @@ -0,0 +1,64 @@ +# You are given two integer arrays nums1 and nums2 both of unique elements, where nums1 is a subset of nums2. +# +# Find all the next greater numbers for nums1's elements in the corresponding places of nums2. +# +# The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. If it does not exist, return -1 for this number. + +# Example 1: +# +# Input: nums1 = [4,1,2], nums2 = [1,3,4,2] +# Output: [-1,3,-1] +# +# Explanation: +# For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1. +# For number 1 in the first array, the next greater number for it in the second array is 3. +# For number 2 in the first array, there is no next greater number for it in the second array, so output -1. +# +# Example 2: +# +# Input: nums1 = [2,4], nums2 = [1,2,3,4] +# Output: [3,-1] +# +# Explanation: +# For number 2 in the first array, the next greater number for it in the second array is 3. +# For number 4 in the first array, there is no next greater number for it in the second array, so output -1. + +# +# Approach: Brute Force +# + +# Complexity Analysis +# +# Time complexity: O(m*n). The complete nums1 array (of size n) needs to be scanned for all the m elements of nums2 in the worst case. +# Space complexity: O(1). No additional space since we're swapping elements in nums1 and returning the input array. + +# @param {Integer[]} nums1 +# @param {Integer[]} nums2 +# @return {Integer[]} +def next_greater_element(nums1, nums2) + nums1.each_with_index do |nums1_value, pointer1| + max = 0 + pos_nums2 = nums2.find_index(nums1_value) + + nums2[pos_nums2..nums2.count].each do |nums2_value| + if nums2_value > nums1_value + max = nums2_value + break + end + end + + nums1[pointer1] = (nums1_value < max ? max : -1) + end + + nums1 +end + +nums1 = [4, 1, 2] +nums2 = [1, 3, 4, 2] +print next_greater_element(nums1, nums2) +# Output: [-1,3,-1] + +nums1 = [2, 4] +nums2 = [1, 2, 3, 4] +print next_greater_element(nums1, nums2) +# Output: [3,-1] diff --git a/data_structures/arrays/shortest_word_distance.rb b/data_structures/arrays/shortest_word_distance.rb new file mode 100644 index 0000000..6bf1697 --- /dev/null +++ b/data_structures/arrays/shortest_word_distance.rb @@ -0,0 +1,33 @@ +# Shortest Word Distance +# Given a list of words and two words word1 and word2, +# return the shortest distance between these two words in the list. +# @param {String[]} words +# @param {String} word1 +# @param {String} word2 +# @return {Integer} + +def shortest_distance(words, word1, word2) + return 0 if word1 == word2 + return 0 unless words.include?(word1) && words.include?(word2) + + minimum_distance = words.length + words.each_with_index do |outer_value, outer_index| + words.each_with_index do |inner_value, inner_index| + if ((inner_value == word1 && outer_value == word2) || (inner_value == word2 && outer_value == word1)) && (minimum_distance > (outer_index - inner_index).abs) + minimum_distance = (outer_index - inner_index).abs + end + end + end + minimum_distance +end + +words = %w[practice makes perfect coding makes] +word1 = 'coding' +word2 = 'practice' +puts(shortest_distance(words, word1, word2)) +# Output: 3 +words = %w[practice makes perfect coding makes] +word1 = 'makes' +word2 = 'coding' +puts(shortest_distance(words, word1, word2)) +# Output: 1 diff --git a/data_structures/arrays/sorted_arrays_intersection.rb b/data_structures/arrays/sorted_arrays_intersection.rb new file mode 100644 index 0000000..a48a7c2 --- /dev/null +++ b/data_structures/arrays/sorted_arrays_intersection.rb @@ -0,0 +1,67 @@ +# Given three integer arrays arr1, arr2 and arr3 sorted in strictly increasing order, return a sorted array of only the integers that appeared in all three arrays. +# +# Example 1: +# +# Input: arr1 = [1,2,3,4,5], arr2 = [1,2,5,7,9], arr3 = [1,3,4,5,8] +# Output: [1,5] +# Explanation: Only 1 and 5 appeared in the three arrays. +# +# Example 2: +# +# Input: arr1 = [197,418,523,876,1356], arr2 = [501,880,1593,1710,1870], arr3 = [521,682,1337,1395,1764] +# Output: [] +# +# + +# +# Approach: Two-pointers +# + +# Complexity Analysis +# +# Time Complexity: O(n), where n is the total length of all of the +# input arrays. +# Space Complexity: O(1), as we only initiate three integer variables +# using constant space. + +# @param {Integer[]} arr1 +# @param {Integer[]} arr2 +# @param {Integer[]} arr3 +# @return {Integer[]} +def sorted_arrays_intersection(arr1, arr2, arr3) + result = [] + + # prepare three pointers to iterate through three arrays + # p1, p2, and p3 point to the beginning of arr1, arr2, and arr3 accordingly + p1 = p2 = p3 = 0 + + while (p1 < arr1.count) && (p2 < arr2.count) && (p3 < arr3.count) + if arr1[p1] == arr2[p2] && arr1[p1] == arr3[p3] + result.push(arr1[p1]) + + p1 += 1 + p2 += 1 + p3 += 1 + elsif arr1[p1] < arr2[p2] + p1 += 1 + elsif arr2[p2] < arr3[p3] + p2 += 1 + else + p3 += 1 + end + end + + result +end + +arr1 = [1, 2, 3, 4, 5] +arr2 = [1, 2, 5, 7, 9] +arr3 = [1, 3, 4, 5, 8] +print(sorted_arrays_intersection(arr1, arr2, arr3)) +# Output: [1,5] + +arr1 = [197, 418, 523, 876, 1356] +arr2 = [501, 880, 1593, 1710, 1870] +arr3 = [521, 682, 1337, 1395, 1764] +print(sorted_arrays_intersection(arr1, arr2, arr3)) +# Output: [] diff --git a/data_structures/arrays/strings/anagram_checker.rb b/data_structures/arrays/strings/anagram_checker.rb index bcd5e36..1918b07 100644 --- a/data_structures/arrays/strings/anagram_checker.rb +++ b/data_structures/arrays/strings/anagram_checker.rb @@ -15,7 +15,7 @@ # @return {Boolean} # -# Approach 1: Sort and Compare +# Approach: Sort and Compare # # Complexity analysis: # diff --git a/data_structures/arrays/strings/palindrome.rb b/data_structures/arrays/strings/palindrome.rb new file mode 100644 index 0000000..587e242 --- /dev/null +++ b/data_structures/arrays/strings/palindrome.rb @@ -0,0 +1,127 @@ +# Challenge name: Valid Palindrome +# +# Given a string s, determine if it is a palindrome, +# considering only alphanumeric characters and ignoring cases. +# +# Example 1: +# Input: s = "A man, a plan, a canal: Panama" +# Output: true +# Explanation: "amanaplanacanalpanama" is a palindrome. +# +# Example 2: +# Input: s = "race a car" +# Output: false +# Explanation: "raceacar" is not a palindrome. +# +# Constraints: +# 1 <= s.length <= 2 * 105 +# s consists only of printable ASCII characters. +# @param {String} s +# @return {Boolean} + +# +# Approach 1: Using Ruby method .reverse +# +# Time Complexity: O(n) +# +def is_palindrome(s) + letters_only = s.downcase.gsub(/[^0-9a-z]/i, '') + letters_only.reverse == letters_only +end + +s = 'A man, a plan, a canal: Panama' +puts is_palindrome(s) +# Output: true +# Explanation: "amanaplanacanalpanama" is a palindrome. + +s = 'race a car' +puts is_palindrome(s) +# Output: false +# Explanation: "raceacar" is not a palindrome. + +s = 'ab_a' +puts is_palindrome(s) +# Output: true +# Explanation: "aba" is a palindrome. + +# +# Approach 2: Reversed array +# +# Complexity Analysis: +# +# Time Complexity: O(n), in length n of the string. +# +# We need to iterate through the string: When we filter out non-alphanumeric characters and convert the remaining +# characters to lower-case. When we reverse the string. When we compare the original and the reversed strings. +# Each iteration runs linearly in time (since each character operation completes in constant time). +# Thus, the effective run-time complexity is linear. +# +# Space Complexity: O(n), in length n of the string. We need O(n) additional +# space to store the filtered string and the reversed string. +# +def is_palindrome(s) + letters_only_array = s.downcase.gsub(/[^0-9a-z]/i, '').split('') + reversed_array = [] + letters_only_array.each do |letter| + reversed_array.unshift(letter) + end + letters_only_array == reversed_array +end + +s = 'A man, a plan, a canal: Panama' +puts is_palindrome(s) +# Output: true +# Explanation: "amanaplanacanalpanama" is a palindrome. + +s = 'race a car' +puts is_palindrome(s) +# Output: false +# Explanation: "raceacar" is not a palindrome. + +s = 'ab_a' +puts is_palindrome(s) +# Output: true +# Explanation: "aba" is a palindrome. + +# +# Approach 2: Two Pointers +# + +# +# Complexity Analysis: +# +# Time Complexity: O(n), in length n of the string. We traverse over each +# character at most once until the two pointers meet in the middle, or when +# we break and return early. +# Space Complexity: O(1). No extra space required, at all. +# +def is_palindrome(s) + letters_only = s.downcase.gsub(/[^0-9a-z]/i, '') + p1 = 0 + p2 = letters_only.length - 1 + + while p1 < p2 + if letters_only[p1] == letters_only[p2] + p1 += 1 + p2 -= 1 + else + return false + end + end + true +end + +s = 'A man, a plan, a canal: Panama' +puts is_palindrome(s) +# Output: true +# Explanation: "amanaplanacanalpanama" is a palindrome. + +s = 'race a car' +puts is_palindrome(s) +# Output: false +# Explanation: "raceacar" is not a palindrome. + +s = 'ab_a' +puts is_palindrome(s) +# Output: true +# Explanation: "aba" is a palindrome. diff --git a/data_structures/arrays/sudoku.rb b/data_structures/arrays/sudoku.rb new file mode 100644 index 0000000..9a6b3ae --- /dev/null +++ b/data_structures/arrays/sudoku.rb @@ -0,0 +1,292 @@ +# Challenge name: Valid Sudoku + +# Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules: +# Each row must contain the digits 1-9 without repetition. +# Each column must contain the digits 1-9 without repetition. +# Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without repetition. + +# @param {Character[][]} board +# @return {Boolean} + +# +# Approach 1: Hash & Brute Force +# +def is_valid_sudoku(board) + rows = [] + columns = [] + grids = [] + + # make each row into a hash to track for duplicated values + board.each do |row| + row_hash = Hash.new(0) + row.each do |num| + next if num == "." + row_hash[num] += 1 + end + rows << row_hash + end + + # check each row hash for duplicated value + rows.each do |row| + row.each do |k, v| + if v > 1 + return false + end + end + end + + # make each column into a hash to track for duplicated values + (0..8).each do |i| + column_hash = Hash.new(0) + board.each do |row| + next if row[i] == "." + column_hash[row[i]] += 1 + columns << column_hash + end + end + + # check each column hash for duplicated value + columns.each do |column| + column.each do |k, v| + if v > 1 + return false + end + end + end + + # make each 3x3 grid into a hash to track for duplicated values + (0..2).each do |i| + grid_hash = Hash.new(0) + board.first(3).each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + board[3..5].each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + board.each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + end + (3..5).each do |i| + grid_hash = Hash.new(0) + board.first(3).each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + board[3..5].each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + board.each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + end + (6..8).last(3).each do |i| + grid_hash = Hash.new(0) + board.first(3).each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + board[3..5].each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + board.each do |row| + next if row[i] == "." + grid_hash[row[i]] += 1 + grids << grid_hash + end + end + + # check each grid hash for duplicated value + grids.each do |grid| + grid.each do |k, v| + if v > 1 + return false + end + end + end + + true +end + +board = [["5","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => true + +board = [["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => false +# explanation: duplicated value in column + +board = [["8","3",".",".","7",".","3",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => false +# explanation: duplicated value in row + +board = [["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + [".",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => false +# explanation: duplicated value in 3x3 grid + +# +# Approach 2: Sets +# +require 'set' + +def is_valid_sudoku(board) + return false unless check_rows(board) + return false unless check_cols(board) + row = -3 + while (row += 3) < 9 + col = - 3 + while (col += 3) < 9 + start_point = [row, col] + return false unless check_grid(board, start_point) + end + end + true +end + +def check_grid(board, start_point) + row = start_point[0] + col = start_point[1] + ss = Set.new + (row..(row + 2)).each do |cur_row| + (col..(col + 2)).each do |cur_col| + next if board[cur_row][cur_col] == "." + return false if ss.member?(board[cur_row][cur_col]) + ss.add board[cur_row][cur_col] + end + end + true +end + +def check_col(board, col) + ss = Set.new + (0..8).each do |row| + next if board[row][col] == "." + return false if ss.member?(board[row][col]) + ss.add board[row][col] + end + true +end + +def check_row(board, row) + ss = Set.new + (0..8).each do |col| + next if board[row][col] == "." + return false if ss.member?(board[row][col]) + ss.add board[row][col] + end + true +end + +def check_rows(board) + (0..8).each do |row| + return false unless check_row(board, row) + end + true +end + +def check_cols(board) + (0..8).each do |col| + return false unless check_col(board, col) + end + true +end + +board = [["5","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => true + +board = [["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => false +# explanation: duplicated value in column + +board = [["8","3",".",".","7",".","3",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => false +# explanation: duplicated value in row + +board = [["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + [".",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"]] +print(is_valid_sudoku(board)) +# => false +# explanation: duplicated value in 3x3 grid \ No newline at end of file diff --git a/data_structures/hash_table/anagram_checker.rb b/data_structures/hash_table/anagram_checker.rb new file mode 100644 index 0000000..e15eed4 --- /dev/null +++ b/data_structures/hash_table/anagram_checker.rb @@ -0,0 +1,161 @@ +# Challenge name: Is anagram +# +# Given two strings s and t , write a function to determine +# if t is an anagram of s. +# +# Note: +# You may assume the string contains only lowercase alphabets. +# +# Follow up: +# What if the inputs contain unicode characters? +# How would you adapt your solution to such case? +# +# @param {String} s +# @param {String} t +# @return {Boolean} + +# +# Approach: Hash table +# + +# Complexity analysis: +# +# Time complexity: O(n). Time complexity is O(n) since accessing the counter +# table is a constant time operation. +# Space complexity: O(1). Although we do use extra space, +# the space complexity is O(1) because the table's size stays constant no +# matter how large n is. +# +def is_anagram(s, t) + s_length = s.length + t_length = t.length + counter = Hash.new(0) + + return false unless s_length == t_length + + (0...s_length).each do |i| + counter[s[i]] += 1 + counter[t[i]] -= 1 + end + + counter.each do |k, v| + return false unless v == 0 + end + + true +end + +s = 'anagram' +t = 'nagaram' +puts(is_anagram(s, t)) +# => true + +s = 'rat' +t = 'car' +puts(is_anagram(s, t)) +# => false + +s = 'a' +t = 'ab' +puts(is_anagram(s, t)) +# => false + +# +# Approach 2: Hash table +# + +# Algorithm: we could also first increment the counter for s, +# then decrement the counter for t. If at any point the counter +# drops below zero, we know that t contains an extra letter, +# not in s, and return false immediately. + +# Complexity analysis: +# +# Time complexity: O(n). +# Space complexity: O(1). +# + +def is_anagram(s, t) + s_length = s.length + t_length = t.length + counter = Hash.new(0) + + return false unless s_length == t_length + + (0...s_length).each do |i| + counter[s[i]] += 1 + end + + (0...s_length).each do |i| + counter[t[i]] -= 1 + + return false if counter[t[i]] < 0 + end + + true +end + +s = 'anagram' +t = 'nagaram' +puts(is_anagram(s, t)) +# => true + +s = 'rat' +t = 'car' +puts(is_anagram(s, t)) +# => false + +s = 'a' +t = 'ab' +puts(is_anagram(s, t)) +# => false + +# +# Approach 3: populate 2 hashes and compare them +# + +def is_anagram(s, t) + s = s.chars + t = t.chars + + return false if s.count != t.count + + hash1 = {} + s.each do |value| + hash1[value] = if hash1[value] + hash1[value] + 1 + else + 1 + end + end + + hash2 = {} + t.each do |value| + hash2[value] = if hash2[value] + hash2[value] + 1 + else + 1 + end + end + + hash1.keys.each do |key| + return false if hash2[key] != hash1[key] + end + + true +end + +s = 'anagram' +t = 'nagaram' +puts(is_anagram(s, t)) +# => true + +s = 'rat' +t = 'car' +puts(is_anagram(s, t)) +# => false + +s = 'a' +t = 'ab' +puts(is_anagram(s, t)) +# => false diff --git a/data_structures/hash_table/arrays_intersection.rb b/data_structures/hash_table/arrays_intersection.rb new file mode 100644 index 0000000..f100e37 --- /dev/null +++ b/data_structures/hash_table/arrays_intersection.rb @@ -0,0 +1,56 @@ +# Given three integer arrays arr1, arr2 and arr3 sorted in strictly increasing order, return a sorted array of only the integers that appeared in all three arrays. +# +# Example 1: +# +# Input: arr1 = [1,2,3,4,5], arr2 = [1,2,5,7,9], arr3 = [1,3,4,5,8] +# Output: [1,5] +# Explanation: Only 1 and 5 appeared in the three arrays. +# +# Example 2: +# +# Input: arr1 = [197,418,523,876,1356], arr2 = [501,880,1593,1710,1870], arr3 = [521,682,1337,1395,1764] +# Output: [] +# +# + +# +# Approach: Hash table +# + +# Complexity Analysis + +# Time Complexity: O(n) - n is the total length of +# all of the input arrays. +# Space Complexity: O(n) - n is the total length of all of the +# input arrays. This is because we adopted a Hash table to store all +# numbers and their number of appearances as values. + +def arrays_intersection(arr1, arr2, arr3) + hash = Hash.new(0) + + add_to_hash(arr1, hash) + add_to_hash(arr2, hash) + add_to_hash(arr3, hash) + + hash.select { |key, value| value == 3 }.keys +end + +def add_to_hash(arr, hash) + arr.count.times do |pointer| + value = arr[pointer] + hash[value] += 1 + end +end + +arr1 = [1, 2, 3, 4, 5] +arr2 = [1, 2, 5, 7, 9] +arr3 = [1, 3, 4, 5, 8] +print arrays_intersection(arr1, arr2, arr3) +# Output: [1,5] +# Explanation: Only 1 and 5 appeared in the three arrays. + +arr1 = [197, 418, 523, 876, 1356] +arr2 = [501, 880, 1593, 1710, 1870] +arr3 = [521, 682, 1337, 1395, 1764] +print arrays_intersection(arr1, arr2, arr3) +# Output: [] diff --git a/data_structures/hash_table/common_characters.rb b/data_structures/hash_table/common_characters.rb new file mode 100644 index 0000000..6d126c5 --- /dev/null +++ b/data_structures/hash_table/common_characters.rb @@ -0,0 +1,51 @@ +# Challenge name: Find Common Characters +# +# Given an array A of strings made only from lowercase letters, return a list +# of all characters that show up in all strings within the list +# (including duplicates). For example, if a character occurs 3 times in all +# strings but not 4 times, you need to include that character three times in +# the final answer. +# +# You may return the answer in any order. +# +# Example 1: +# Input: ["bella","label","roller"] +# Output: ["e","l","l"] +# +# Example 2: +# Input: ["cool","lock","cook"] +# Output: ["c","o"] + +# +# Approach 1: Hash +# +# Time Complexity: O(n) +# +def common_characters(arr) + target_count = arr.count + + hash = Hash.new(0) + (0...target_count).each do |i| + arr[i].split('').each do |letter| + hash[letter] += 1 + end + end + + result = [] + hash.each do |k, v| + while v >= target_count + if v >= target_count + result << k + v -= target_count + end + end + end + + result +end + +puts common_characters(["bella","label","roller"]) +# => ["e","l","l"] + +puts common_characters(["cool","lock","cook"]) +# => ["c","o"] \ No newline at end of file diff --git a/data_structures/hash_table/good_pairs.rb b/data_structures/hash_table/good_pairs.rb new file mode 100644 index 0000000..9f7beeb --- /dev/null +++ b/data_structures/hash_table/good_pairs.rb @@ -0,0 +1,43 @@ +# Challenge name: Number of good pairs +# +# Given an array of integers nums. +# A pair (i,j) is called good if nums[i] == nums[j] and i < j. +# Return the number of good pairs. +# +# @param {Integer[]} nums +# @return {Integer} +# + +# +# Approach 1: Hash +# +# Time Complexity: O(n) +def num_identical_pairs(nums) + hash = Hash.new(0) + + nums.each do |num| + hash[num] = hash[num] + 1 + end + + counter = 0 + # Count how many times each number appears. + # If a number appears n times, then n * (n – 1) / 2 good pairs + # can be made with this number. + hash.values.each do |val| + counter += (val * (val - 1) / 2) + end + + counter +end + +nums = [1, 2, 3, 1, 1, 3] +puts(num_identical_pairs(nums)) +# Output: 4 + +nums = [1, 1, 1, 1] +puts(num_identical_pairs(nums)) +# Output: 6 + +nums = [1, 2, 3] +puts(num_identical_pairs(nums)) +# Output: 0 \ No newline at end of file diff --git a/data_structures/hash_table/uncommon_words.rb b/data_structures/hash_table/uncommon_words.rb new file mode 100644 index 0000000..ff0ace9 --- /dev/null +++ b/data_structures/hash_table/uncommon_words.rb @@ -0,0 +1,48 @@ +# Challenge name: Uncommon words from two sentences +# +# We are given two sentences A and B. +# (A sentence is a string of space separated words. +# Each word consists only of lowercase letters.) +# +# A word is uncommon if it appears exactly once in one of the sentences, +# and does not appear in the other sentence. +# +# Return a list of all uncommon words. +# You may return the list in any order. +# +# Example 1: +# Input: A = "this apple is sweet", B = "this apple is sour" +# Output: ["sweet","sour"] +# +# Example 2: +# Input: A = "apple apple", B = "banana" +# Output: ["banana"] + +# +# Approach 1: Hash +# +# Time Complexitiy: O(n) + +def find_uncommon_words(strA, strB) + array = strA.concat(" ", strB).split(" ") + hash = Hash.new(0) + result = [] + + array.each do |word| + hash[word] += 1 + end + + hash.each do |k, v| + if v < 2 + result.push(k) + end + end + + result +end + +puts find_uncommon_words("this apple is sweet", "this apple is sour") +# => ["sweet", "sour"] + +puts find_uncommon_words("apple apple", "banana") +# => ["banana"] diff --git a/data_structures/queues/circular_queue.rb b/data_structures/queues/circular_queue.rb new file mode 100644 index 0000000..0905ab5 --- /dev/null +++ b/data_structures/queues/circular_queue.rb @@ -0,0 +1,88 @@ +# Challenge name: Circular Queue +# +# Design the implementation of a circular queue. +# The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and +# the last position is connected back to the first position to make a circle. It is also called "Ring Buffer". +# + +# +# Complexity Analysis +# +# Time complexity: O(1). +# All of the methods in our circular data structure are of constant time complexity. +# +# Space Complexity: O(N). +# The overall space complexity of the data structure is linear, where N is the pre-assigned capacity of the queue. +# However, it is worth mentioning that the memory consumption of the data structure remains as its pre-assigned capacity during its entire life cycle. + +class CircularQueue + def initialize(max_size) + @max_size = max_size + @queue = Array.new(max_size, nil) + @front = 0 + @back = 0 + @size = 0 + end + + attr_accessor :front, :back, :size + attr_reader :max_size, :queue + + def empty? + size == 0 + end + + def peek + return nil if empty? + + queue[front] + end + + def add(x) + raise "Queue is at max capacity" if size == max_size + + + queue[back] = x + @back = (back + 1) % max_size + @size += 1 + end + + def pop + raise "Queue is empty" if size == 0 + + temp = queue[front] + queue[front] = nil + @front = (front + 1) % max_size + @size -= 1 + + temp + end +end + +queue = CircularQueue.new(3) + +begin + queue.pop +rescue => e + puts e.message +end + +queue.add(1) +queue.add(2) +queue.add(3) + +begin + queue.add(4) +rescue => e + puts e.message +end + +puts queue.inspect +# => # + +puts queue.peek +# => 1 + +queue.pop + +puts queue.peek +# => 2 diff --git a/dynamic_programming/count_sorted_vowel_strings.rb b/dynamic_programming/count_sorted_vowel_strings.rb new file mode 100644 index 0000000..e6a9fbe --- /dev/null +++ b/dynamic_programming/count_sorted_vowel_strings.rb @@ -0,0 +1,91 @@ +# Challenge name: Count Sorted Vowel Strings +# +# Given an integer n, return the number of strings of length n that consist only +# of vowels (a, e, i, o, u) and are lexicographically sorted. +# +# A string s is lexicographically sorted if for all valid i, s[i] is the same as +# or comes before s[i+1] in the alphabet. +# +# +# Example 1: +# +# Input: n = 1 +# Output: 5 +# Explanation: The 5 sorted strings that consist of vowels only are ["a","e","i","o","u"]. +# +# Example 2: +# +# Input: n = 2 +# Output: 15 +# Explanation: The 15 sorted strings that consist of vowels only are +# ["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"]. +# Note that "ea" is not a valid string since 'e' comes after 'a' in the alphabet. +# +# Example 3: +# +# Input: n = 33 +# Output: 66045 + +# +# Approach: Using Recursion + Memoization, Top Down Dynamic Programming +# + +# +# Algorithm: Dynamic Programming state transition. +# +# F(0) = 1 +# F(n)a = F(n-1)a + F(n-1)e + F(n-1)i + F(n-1)o +F(n-1)u +# F(n)e = F(n-1)e + F(n-1)i + F(n-1)o + F(n-1)u +# F(n)i = F(n-1)i + F(n-1)o +F(n-1)u +# F(n)o = F(n-1)o + F(n-1)u +# F(n)u = F(n-1)u +# + +# @param {Integer} n +# @return {Integer} + +def count_vowel_strings(n, letter = 'a') + return 1 if n < 1 + + @h ||= {} + key = [n, letter] + return @h[key] if @h[key] + + result = case letter + when 'a' + count_vowel_strings(n - 1, letter = 'a') + + count_vowel_strings(n - 1, letter = 'e') + + count_vowel_strings(n - 1, letter = 'i') + + count_vowel_strings(n - 1, letter = 'o') + + count_vowel_strings(n - 1, letter = 'u') + when 'e' + count_vowel_strings(n - 1, letter = 'e') + + count_vowel_strings(n - 1, letter = 'i') + + count_vowel_strings(n - 1, letter = 'o') + + count_vowel_strings(n - 1, letter = 'u') + when 'i' + count_vowel_strings(n - 1, letter = 'i') + + count_vowel_strings(n - 1, letter = 'o') + + count_vowel_strings(n - 1, letter = 'u') + when 'o' + count_vowel_strings(n - 1, letter = 'o') + + count_vowel_strings(n - 1, letter = 'u') + when 'u' + count_vowel_strings(n - 1, letter = 'u') + end + + @h[key] = result + @h[key] +end + +n = 33 +puts count_vowel_strings(n) +# Output: 66045 + +n = 2 +puts count_vowel_strings(n) +# Output: 15 + +n = 1 +puts count_vowel_strings(n) +# Output: 5 diff --git a/maths/abs_max.rb b/maths/abs_max.rb new file mode 100644 index 0000000..d3130d9 --- /dev/null +++ b/maths/abs_max.rb @@ -0,0 +1,30 @@ +# A ruby program to find absolute maximum +# Mathematical representation of abs max = ((a + b + absoulte(a - b)) / 2) + +def abs_max(x, y) + num = x - y + max_value = ((x + y + num.abs()) / 2) + "The Abs Max of #{x} and #{y} is #{max_value}." + rescue + "Error: Provide number only!" +end + +# Valid inputs +puts abs_max(10, 20) +# The Abs Max of 10 and 20 is 20. + +puts abs_max(-10, -1) +# The Abs Max of -10 and -1 is -1. + +puts abs_max(9, -121) +# The Abs Max of 9 and -121 is 9. + +# Invalid inputs +puts abs_max(2, "-1") +# Error: Provide number only! + +puts abs_max("3", "5") +# Error: Provide number only! + +puts abs_max("a", "5") +# Error: Provide number only! diff --git a/maths/abs_min.rb b/maths/abs_min.rb new file mode 100644 index 0000000..20e341c --- /dev/null +++ b/maths/abs_min.rb @@ -0,0 +1,34 @@ +# A ruby program to find absolute minimum +# Mathematical representation of abs min = ((a + b - absoulte(a - b)) / 2) + +def abs_min(x, y) + num = x - y + min_value = ((x + y - num.abs()) / 2) + "The Abs Min of #{x} and #{y} is #{min_value}." + rescue + "Error: Provide number only!" +end + +# +# Valid inputs +# +puts abs_min(10, 20) +# The Abs Min of 10 and 20 is 10. + +puts abs_min(-10, -1) +# The Abs Min of -10 and -1 is -10. + +puts abs_min(9, -121) +# The Abs Min of 9 and -121 is -121. + +# +# Invalid inputs +# +puts abs_min(2, "-1") +# Error: Provide number only! + +puts abs_min("3", "5") +# Error: Provide number only! + +puts abs_min("a", "5") +# Error: Provide number only! diff --git a/maths/add.rb b/maths/add.rb new file mode 100644 index 0000000..a8a6c35 --- /dev/null +++ b/maths/add.rb @@ -0,0 +1,34 @@ +# A ruby program to add numbers +# Addition or sum of numbers means adding each and every element of the inputs +# Sum or addition of 1 and 3 is 1 + 3 = 4 + +def add(*array) + sum = 0 + array.each { |a| sum+=a } + puts "The sum of following elements #{array} is #{sum}" + rescue + puts "Error: Please provide number only!" +end + +# +# Valid inputs +# + +puts add(1) +# The sum of following elements [1] is 1 + +puts add(2, 5, -4) +# The sum of following elements [2, 5, -4] is 3 + +puts add(25, 45) +# The sum of following elements [25, 45] is 70 + +# +# Invalid inputs +# + +puts add("1", 2, 3) +# Error: Please provide number only! + +puts add("a", 1) +# Error: Please provide number only! diff --git a/maths/armstrong_number.rb b/maths/armstrong_number.rb new file mode 100644 index 0000000..b312891 --- /dev/null +++ b/maths/armstrong_number.rb @@ -0,0 +1,57 @@ +# A ruby program to determine whether a given number is an Armstrong number +# Wiki url: https://en.wikipedia.org/wiki/Narcissistic_number +# other resources: https://pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html + +# +# Examples: -> 153 = (1 * 1 * 1) + (5 * 5 * 5) + (3 * 3 * 3) [Armstrong number] and -> 125 != (1 * 1 * 1) + (2 * 2 * 2) + (5 * 5 * 5) +# -> 1634 = (1 * 1 * 1 * 1) + (6 * 6 * 6 * 6) + (3 * 3 * 3 * 3) + (4 * 4 * 4 * 4) +# + +def armstrong_number(number) + num = number + sum = 0 + len = number.digits.count + while(number != 0) + remainder = number % 10 + sum += remainder ** len + number /= 10 + end + + if num == sum + "The number #{num} is an Armstrong number." + else + "The number #{num} is not an Armstrong number." + end + rescue + "Error: Please provide number only!" +end + +# +# Valid inputs +# +puts armstrong_number(153) +# The number 153 is an Armstrong number. + +puts armstrong_number(0) +# The number 0 is an Armstrong number. + +puts armstrong_number(370) +# The number 370 is an Armstrong number. + +puts armstrong_number(10) +# The number 10 is not an Armstrong number. + +puts armstrong_number(1634) +# The number 1634 is an Armstrong number. + +puts armstrong_number(123) +# The number 123 is not an Armstrong number. + +# +# Invalid inputs +# +puts armstrong_number("153") +# Error: Please provide number only! + +puts armstrong_number("a") +# Error: Please provide number only! diff --git a/maths/average_mean.rb b/maths/average_mean.rb new file mode 100644 index 0000000..0efd5d0 --- /dev/null +++ b/maths/average_mean.rb @@ -0,0 +1,28 @@ +# A ruby program for finding average mean + +module AverageMean + # Average mean = sum of elements / total number of elements + def self.average_mean(n, *array) + if n.instance_of? Integer + if n == array.size + puts "The average mean of the following elements #{array} is #{array.sum/array.size}." + else + puts "Provide the required #{n} elements properly!" + end + else + raise + end + rescue + puts "Error: Please provide number only!" + end +end + +# Valid inputs +AverageMean.average_mean(2, 3, 1) +AverageMean.average_mean(5, 1, 2, 3, 4, 5) +AverageMean.average_mean(3, 2, 2, 2) + +# Invalid inputs +AverageMean.average_mean(2, 3, 1, 5) +AverageMean.average_mean(2, 3, "a") +AverageMean.average_mean("a", 1, 2) diff --git a/maths/average_median.rb b/maths/average_median.rb new file mode 100644 index 0000000..9e447bd --- /dev/null +++ b/maths/average_median.rb @@ -0,0 +1,60 @@ +# A ruby program to find average median +# Reference: https://dev.to/colerau/how-to-find-the-median-and-mean-of-an-array-in-ruby-4f04 + +module AverageMedian + def self.average_median(n, *array) + if n.instance_of? Integer + if n == array.size + array.sort + if array.size % 2 == 0 + mid_element_1 = array.size/2 + mid_element_2 = mid_element_1 + 1 + puts "The average median of the following elements #{array} is #{(array[mid_element_1 - 1] + array[mid_element_2 - 1]) / 2}." + else + mid_element = (array.size + 1) / 2 + puts "The average median of the following elements #{array} is #{array[mid_element - 1]}." + end + else + puts "Provide the required #{n} elements properly!" + end + else + raise + end + rescue + puts "Error: Please provide number only!" + end +end + +# +# Valid inputs +# + +puts AverageMedian.average_median(2, 3, 1) +# The average median of the following elements [3, 1] is 2. + +puts AverageMedian.average_median(5, 1, 2, 3, 4, 5) +# The average median of the following elements [1, 2, 3, 4, 5] is 3. + +puts AverageMedian.average_median(3, 2, 2, 2) +# The average median of the following elements [2, 2, 2] is 2. + +puts AverageMedian.average_median(1, 5) +# The average median of the following elements [5] is 5. + +# +# Invalid inputs +# + +puts AverageMedian.average_median(2, 3, 1, 5) +# Provide the required 2 elements properly! + +puts AverageMedian.average_median(2, 3, "a") +# Traceback (most recent call last): +# 4: from /Users/voliveira/.rvm/rubies/ruby-2.7.0/bin/irb:23:in `
' +# 3: from /Users/voliveira/.rvm/rubies/ruby-2.7.0/bin/irb:23:in `load' +# 2: from /Users/voliveira/.rvm/rubies/ruby-2.7.0/lib/ruby/gems/2.7.0/gems/irb-1.2.1/exe/irb:11:in `' +# 1: from (irb):30 +# NameError (undefined local variable or method `verageMedian' for main:Object) + +puts AverageMedian.average_median("a", 1, 2) +# Error: Please provide number only! diff --git a/maths/count_sorted_vowel_strings.rb b/maths/count_sorted_vowel_strings.rb new file mode 100644 index 0000000..630f1f8 --- /dev/null +++ b/maths/count_sorted_vowel_strings.rb @@ -0,0 +1,65 @@ +# Challenge name: Count Sorted Vowel Strings +# +# Given an integer n, return the number of strings of length n that consist only +# of vowels (a, e, i, o, u) and are lexicographically sorted. +# +# A string s is lexicographically sorted if for all valid i, s[i] is the same as +# or comes before s[i+1] in the alphabet. +# +# +# Example 1: +# +# Input: n = 1 +# Output: 5 +# Explanation: The 5 sorted strings that consist of vowels only are ["a","e","i","o","u"]. +# +# Example 2: +# +# Input: n = 2 +# Output: 15 +# Explanation: The 15 sorted strings that consist of vowels only are +# ["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"]. +# Note that "ea" is not a valid string since 'e' comes after 'a' in the alphabet. +# +# Example 3: +# +# Input: n = 33 +# Output: 66045 + + +# +# Approach: Math +# + +# +# Intuition and Algorithm +# +# The problem is a variant of finding Combinations. +# Mathematically, the problem can be described as, +# given 5 vowels (let k = 5), we want to find the +# number of combinations using only n vowels. Also, +# we can repeat each of those vowels multiple times. +# + +# Complexity Analysis +# +# Time Complexity: O(1), as the approach runs in constant time. +# Space Complexity: O(1), as the approach uses constant extra space. + +# @param {Integer} n +# @return {Integer} +def count_vowel_strings(n) + (n + 4) * (n + 3) * (n + 2) * (n + 1) / 24 +end + +n = 33 +puts count_vowel_strings(n) +# Output: 66045 + +n = 2 +puts count_vowel_strings(n) +# Output: 15 + +n = 1 +puts count_vowel_strings(n) +# Output: 5 diff --git a/maths/factorial.rb b/maths/factorial.rb new file mode 100644 index 0000000..e83b347 --- /dev/null +++ b/maths/factorial.rb @@ -0,0 +1,31 @@ +=begin +A ruby program calculate factorial of a given number. +Mathematical Explanation: The factorial of a number is the product of all the integers from 1 to that number. +i.e: n! = n*(n-1)*(n-2)......*2*1 +=end + +# +# Approach: Interative +# + +def factorial(n) + return nil if n < 0 + + fac = 1 + + while n > 0 + fac *= n + n -= 1 + end + + fac +end + +puts '4! = ' + factorial(4).to_s +# 4! = 24 + +puts '0! = ' + factorial(0).to_s +# 0! = 1 + +puts '10! = ' + factorial(10).to_s +# 10! = 3628800 diff --git a/maths/factorial_non_recursive_non_iterative.rb b/maths/factorial_non_recursive_non_iterative.rb new file mode 100644 index 0000000..f992a92 --- /dev/null +++ b/maths/factorial_non_recursive_non_iterative.rb @@ -0,0 +1,43 @@ +# A ruby program to find factorial of a given integer +# Factorial of a given integer is defined as the product of all the positive integers less than or equal to the given integer +# Mathematical representation: n! = n * (n - 1) * (n - 2) * ... * 1 + +# +# Non-recursive and non-iterative approach +# + +def factorial(number) + if number < 0 + "Please check your input number! The given number is a negative number." + elsif number == 0 + "The factorial of #{number} is 1." + else + result = (1..number).inject(:*) + "The factorial of #{number} is #{result}." + end + rescue + "Error: Please provide integer only!" +end + +# Valid inputs +puts factorial(0) +# The factorial of 0 is 1. + +puts factorial(4) +# The factorial of 4 is 24. + +puts factorial(10) +# The factorial of 10 is 3628800. + +puts factorial(1) +# The factorial of 1 is 1. + +puts factorial(-5) +# Please check your input number! The given number is a negative number. + +# Invalid inputs +puts factorial("a") +# Error: Please provide integer only! + +puts factorial("2") +# Error: Please provide integer only! diff --git a/maths/find_max.rb b/maths/find_max.rb new file mode 100644 index 0000000..ec13cfb --- /dev/null +++ b/maths/find_max.rb @@ -0,0 +1,64 @@ +# A ruby program to find max from a set of elements + +# This find_max method will return the max element out of the array +def find_max(*array) + max = array[0] + array.each do |a| + if a >= max + max = a + end + end + "The Max of the following elements #{array} is #{max}." + rescue + "Error: Please provide number only!" +end + +# Max method will return the maximum element from the set of input elements provided +def predefined_max(*array) + "The Max of the following elements #{array} is #{array.max}." + rescue + "Error: Please provide number only!" +end + +# Sort method will sort the elements in ascending order. Last method will return the end element out of the array +def predefined_sort_last_max(*array) + "The Max of the following elements #{array} is #{array.sort.last}." + rescue + "Error: Please provide number only!" +end + +# Using find_max +# Valid inputs +puts find_max(11, 29, 33) +# The Max of the following elements [11, 29, 33] is 33. + +puts find_max(-221, -852, -1100, -10) +# The Max of the following elements [-221, -852, -1100, -10] is -10. + +# Invalid inputs +puts find_max(5, "95", 2) +# Error: Please provide number only! + +# Using predefined_max +# Valid inputs +puts predefined_max(51, 82, 39) +# The Max of the following elements [51, 82, 39] is 82. + +puts predefined_max(-11, -51, -10, -10) +# The Max of the following elements [-11, -51, -10, -10] is -10. + +# Invalid inputs +puts predefined_max("x", 5, 95, 2) +# Error: Please provide number only! + +# Using predefined_sort_last_max +# Valid inputs +puts predefined_sort_last_max(1, 2, 3) +# The Max of the following elements [1, 2, 3] is 3. + +puts predefined_sort_last_max(-21, -52, -100, -1) +# The Max of the following elements [-21, -52, -100, -1] is -1. + +# Invalid inputs +puts predefined_sort_last_max(5, 95, 2, "a") +# Error: Please provide number only! diff --git a/maths/find_min.rb b/maths/find_min.rb new file mode 100644 index 0000000..f78e4f4 --- /dev/null +++ b/maths/find_min.rb @@ -0,0 +1,64 @@ +# A ruby program to find min from a set of elements + +# This find_min method will return the min element out of the array +def find_min(*array) + min = array[0] + array.each do |a| + if a <= min + min = a + end + end + "The Min of the following elements #{array} is #{min}." + rescue + "Error: Please provide number only!" +end + +# Min method will return the minimum element from the set of input elements provided +def predefined_min(*array) + "The Min of the following elements #{array} is #{array.min}." + rescue + "Error: Please provide number only!" +end + +# Sort method will sort the elements in ascending order. First method will return the beginning element out of the array +def predefined_sort_first_min(*array) + "The Min of the following elements #{array} is #{array.sort.first}." + rescue + "Error: Please provide number only!" +end + +# Using find_min +# Valid inputs +puts find_min(11, 29, 33) +# The Min of the following elements [11, 29, 33] is 33. + +puts find_min(-221, -852, -1100, -10) +# The Min of the following elements [-221, -852, -1100, -10] is -10. + +# Invalid inputs +puts find_min(5, "95", 2) +# Error: Please provide number only! + +# Using predefined_min +# Valid inputs +puts predefined_min(51, 82, 39) +# The Min of the following elements [51, 82, 39] is 82. + +puts predefined_min(-11, -51, -10, -10) +# The Min of the following elements [-11, -51, -10, -10] is -10. + +# Invalid inputs +puts predefined_min("x", 5, 95, 2) +# Error: Please provide number only! + +# Using predefined_sort_first_min +# Valid inputs +puts predefined_sort_first_min(1, 2, 3) +# The Min of the following elements [1, 2, 3] is 3. + +puts predefined_sort_first_min(-21, -52, -100, -1) +# The Min of the following elements [-21, -52, -100, -1] is -1. + +# Invalid inputs +puts predefined_sort_first_min(5, 95, 2, "a") +# Error: Please provide number only! diff --git a/maths/lucas_series.rb b/maths/lucas_series.rb new file mode 100644 index 0000000..85fb41f --- /dev/null +++ b/maths/lucas_series.rb @@ -0,0 +1,31 @@ +# A ruby program for Lucas series +# +# The Lucas numbers, commonly denoted L(n) form a sequence, +# called the Lucas series, such that each number is the sum +# of the two preceding ones, starting from 2 and 1. That is, +# +# L(0) = 2, L(1) = 1 +# L(n) = L(n - 1) + L(n - 2), for n > 1. +# +# Given n, calculate L(n). +# Example: 2 1 3 4 7 11 18... +# Resource: https://en.wikipedia.org/wiki/Lucas_number + +def lucas(number) + golden_ratio = (1 + 5**0.5) / 2 + ((golden_ratio**number).round()).to_i + rescue + "Error: Provide number only!" +end + +puts lucas(4) +# 7 + +puts lucas(3) +# 4 + +puts lucas("3") +# Error: Provide number only! + +puts lucas(2) +# 3 diff --git a/maths/roman_to_integer.rb b/maths/roman_to_integer.rb new file mode 100644 index 0000000..c2f2d3d --- /dev/null +++ b/maths/roman_to_integer.rb @@ -0,0 +1,96 @@ +# Challenge name: Roman to Integer +# +# Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. +# +# Symbol Value +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 1000 +# +# For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. +# +# Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: +# +# I can be placed before V (5) and X (10) to make 4 and 9. +# X can be placed before L (50) and C (100) to make 40 and 90. +# C can be placed before D (500) and M (1000) to make 400 and 900. +# Given a roman numeral, convert it to an integer. +# +# + +# Approach 1: Left-to-Right Pass +# + +# Complexity Analysis +# +# Let n be the length of the input string (the total number of symbols in it). +# +# Time complexity: O(1). +# As there is a finite set of roman numerals. +# +# Space complexity: O(1). +# Because only a constant number of single-value variables are used, the space complexity is O(1). + +ROM_NUMS = { + "I" => 1, + "V" => 5, + "X" => 10, + "L" => 50, + "C" => 100, + "D" => 500, + "M" => 1000 +} + +# Now, recall that each symbol adds its own value, except for when a smaller +# valued symbol is before a larger valued symbol. In those cases, instead of +# adding both symbols to the total, we need to subtract the large from the +# small, adding that instead. + +# Therefore, the simplest algorithm is to use a pointer to scan through the +# string, at each step deciding whether to add the current symbol and +# go forward 1 place, or add the difference of the next 2 symbols and +# go forward 2 places. + +def roman_to_int(s) + res = 0 + temp = 0 + + s.chars.each_with_index do |el, i| + # subtractive case: if at least 2 symbols remaining AND value of s[i] < value of s[i + 1] + if ROM_NUMS[s[i + 1]] && ROM_NUMS[el] < ROM_NUMS[s[i+1]] + temp = ROM_NUMS[el] + else + # Else this is NOT the subtractive case. + res += (ROM_NUMS[el] - temp) + temp = 0 + end + end + + res +end + +s = "III" +puts roman_to_int(s) +# Output: 3 + +s = "IV" +puts roman_to_int(s) +# Output: 4 + +s = "IX" +puts roman_to_int(s) +# Output: 9 + +s = "LVIII" +puts roman_to_int(s) +# Output: 58 +# Explanation: L = 50, V= 5, III = 3. + +s = "MCMXCIV" +puts roman_to_int(s) +# Output: 1994 +# Explanation: M = 1000, CM = 900, XC = 90 and IV = 4. diff --git a/Searches/binary_search.rb b/searches/binary_search.rb similarity index 88% rename from Searches/binary_search.rb rename to searches/binary_search.rb index bbc8efa..d0710a2 100644 --- a/Searches/binary_search.rb +++ b/searches/binary_search.rb @@ -8,18 +8,21 @@ def binary_search(array, key) return middle if array[middle] == key if key < array[middle] - back = middle - 1 -else - front = middle + 1 -end + back = middle - 1 + else + front = middle + 1 + end end + nil end puts "Enter a sorted space-separated list:" arr = gets.chomp.split(' ').map(&:to_i) + puts "Enter the value to be searched:" value = gets.chomp.to_i + puts if binary_search(arr, value) != nil "Found at index #{binary_search(arr, value)}" else diff --git a/Searches/depth_first_search.rb b/searches/depth_first_search.rb similarity index 100% rename from Searches/depth_first_search.rb rename to searches/depth_first_search.rb diff --git a/Searches/double_linear_search.rb b/searches/double_linear_search.rb similarity index 100% rename from Searches/double_linear_search.rb rename to searches/double_linear_search.rb diff --git a/Searches/jump_search.rb b/searches/jump_search.rb similarity index 100% rename from Searches/jump_search.rb rename to searches/jump_search.rb diff --git a/Searches/linear_search.rb b/searches/linear_search.rb similarity index 100% rename from Searches/linear_search.rb rename to searches/linear_search.rb diff --git a/Searches/recursive_double_linear_search.rb b/searches/recursive_double_linear_search.rb similarity index 100% rename from Searches/recursive_double_linear_search.rb rename to searches/recursive_double_linear_search.rb diff --git a/Searches/recursive_linear_search.rb b/searches/recursive_linear_search.rb similarity index 100% rename from Searches/recursive_linear_search.rb rename to searches/recursive_linear_search.rb diff --git a/Searches/ternary_search.rb b/searches/ternary_search.rb similarity index 100% rename from Searches/ternary_search.rb rename to searches/ternary_search.rb diff --git a/sorting/bead_sort.rb b/sorting/bead_sort.rb new file mode 100644 index 0000000..e7f5f5f --- /dev/null +++ b/sorting/bead_sort.rb @@ -0,0 +1,24 @@ +class Array + def columns + x = map(&:length).max + Array.new(x) do |row| + Array.new(length) { |column| self[column][row] }.compact + end + end +end + +def bead_sort(array) + array + .map { |element| [1] * element } + .columns + .columns + .map(&:length) + .reverse +end + +if $0 == __FILE__ + puts 'Enter a list of numbers separated by space' + + list = gets.split.map(&:to_i) + p bead_sort(list) +end diff --git a/sorting/bead_sort_test.rb b/sorting/bead_sort_test.rb new file mode 100644 index 0000000..93bf030 --- /dev/null +++ b/sorting/bead_sort_test.rb @@ -0,0 +1,11 @@ +require 'minitest/autorun' +require_relative './sort_tests' +require_relative './bead_sort' + +class TestInsertionSort < Minitest::Test + include SortTests + + def sort(input) + bead_sort(input) + end +end diff --git a/sorting/cocktail_sort.rb b/sorting/cocktail_sort.rb new file mode 100644 index 0000000..fd83d23 --- /dev/null +++ b/sorting/cocktail_sort.rb @@ -0,0 +1,24 @@ +def cocktail_sort(array) + start = 0 + finish = array.length - 1 + way = 1 + loop do + swapped = false + start.step(finish - way, way) do |i| + if (array[i] <=> array[i + way]) == way + array[i], array[i + way] = array[i + way], array[i] + swapped = i + end + end + break unless swapped + start, finish, way = swapped, start, -way + end + array +end + +if $0 == __FILE__ + puts 'Enter a list of numbers separated by space' + + list = gets.split.map(&:to_i) + p cocktail_sort(list) +end diff --git a/sorting/cocktail_sort_test.rb b/sorting/cocktail_sort_test.rb new file mode 100644 index 0000000..ce669ba --- /dev/null +++ b/sorting/cocktail_sort_test.rb @@ -0,0 +1,12 @@ +require 'minitest/autorun' +require_relative './sort_tests' +require_relative './cocktail_sort' + +class TestInsertionSort < Minitest::Test + include SortTests + + def sort(input) + cocktail_sort(input) + end +end + diff --git a/sorting/comb_sort.rb b/sorting/comb_sort.rb new file mode 100644 index 0000000..3cb20ea --- /dev/null +++ b/sorting/comb_sort.rb @@ -0,0 +1,22 @@ +def comb_sort(array) + gap = array.length + swaps = true + while gap > 1 or swaps + gap = [1, (gap / 1.25).to_i].max + swaps = false + 0.upto(array.length - gap - 1) do |i| + if array[i] > array[i + gap] + array[i], array[i + gap] = array[i + gap], array[i] + swaps = true + end + end + end + array +end + +if $0 == __FILE__ + puts 'Enter a list of numbers separated by space' + + list = gets.split.map(&:to_i) + p insertion_sort(list) +end diff --git a/sorting/comb_sort_test.rb b/sorting/comb_sort_test.rb new file mode 100644 index 0000000..cedfbb6 --- /dev/null +++ b/sorting/comb_sort_test.rb @@ -0,0 +1,11 @@ +require 'minitest/autorun' +require_relative './sort_tests' +require_relative './comb_sort' + +class TestInsertionSort < Minitest::Test + include SortTests + + def sort(input) + comb_sort(input) + end +end diff --git a/sorting/pancake_sort.rb b/sorting/pancake_sort.rb new file mode 100644 index 0000000..3f07e0f --- /dev/null +++ b/sorting/pancake_sort.rb @@ -0,0 +1,18 @@ +def pancake_sort(array) + return array if array.length <= 1 + (array.length - 1).downto(1) do |index| + max_index = array[0..index].index(array[0..index].max) + next if max_index == index + + array[0..max_index] = array[0..max_index].reverse if max_index > 0 + array[0..index] = array[0..index].reverse + end + array +end + +if $0 == __FILE__ + puts 'Enter a list of numbers separated by space' + + list = gets.split.map(&:to_i) + p pancake_sort(list) +end diff --git a/sorting/pancake_sort_test.rb b/sorting/pancake_sort_test.rb new file mode 100644 index 0000000..bcebfc3 --- /dev/null +++ b/sorting/pancake_sort_test.rb @@ -0,0 +1,11 @@ +require 'minitest/autorun' +require_relative './sort_tests' +require_relative './pancake_sort' + +class TestInsertionSort < Minitest::Test + include SortTests + + def sort(input) + pancake_sort(input) + end +end diff --git a/sorting/sort_color.rb b/sorting/sort_color.rb new file mode 100644 index 0000000..441679e --- /dev/null +++ b/sorting/sort_color.rb @@ -0,0 +1,65 @@ +# Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. +# +# We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively. +# +# Example 1: +# +# Input: nums = [2,0,2,1,1,0] +# Output: [0,0,1,1,2,2] +# +# Example 2: +# +# Input: nums = [2,0,1] +# Output: [0,1,2] +# +# Example 3: +# +# Input: nums = [0] +# Output: [0] +# +# Example 4: +# +# Input: nums = [1] +# Output: [1] + +# @param {Integer[]} nums +# @return {Void} Do not return anything, modify nums in-place instead. +def sort_colors(nums) + bubble_sort(nums) +end + +def bubble_sort(array) + array_length = array.size + return array if array_length <= 1 + + loop do + swapped = false + + (array_length - 1).times do |i| + if array[i] > array[i + 1] + array[i], array[i + 1] = array[i + 1], array[i] + swapped = true + end + end + + break unless swapped + end + + array +end + +nums = [2, 0, 2, 1, 1, 0] +puts sort_colors(nums) +# Output: [0,0,1,1,2,2] + +nums = [2, 0, 1] +puts sort_colors(nums) +# Output: [0,1,2] + +nums = [0] +puts sort_colors(nums) +# Output: [0] + +nums = [1] +puts sort_colors(nums) +# Output: [1]