From b982662efbe36c5446c83741dcd522e2b67ce9b6 Mon Sep 17 00:00:00 2001 From: Marcio Oliveira Date: Sat, 1 Jul 2023 14:08:41 -0300 Subject: [PATCH] Added first algorithms in Ruby programming language --- .github/workflows/ruby.yml | 40 +++++++++++++ .gitignore | 21 +++++++ README.md | 3 + algorithms/Ruby/.rubocop.yml | 16 +++++ algorithms/Ruby/Arrays/contains_duplicate.rb | 30 ++++++++++ algorithms/Ruby/Arrays/majority_element.rb | 57 ++++++++++++++++++ algorithms/Ruby/Arrays/max_subarray_sum.rb | 39 ++++++++++++ algorithms/Ruby/Arrays/reverse_array.rb | 24 ++++++++ algorithms/Ruby/Arrays/single_number.rb | 37 ++++++++++++ algorithms/Ruby/Arrays/two_sum.rb | 43 ++++++++++++++ algorithms/Ruby/Gemfile | 5 ++ algorithms/Ruby/Gemfile.lock | 62 ++++++++++++++++++++ algorithms/Ruby/README.md | 9 +++ 13 files changed, 386 insertions(+) create mode 100644 .github/workflows/ruby.yml create mode 100644 algorithms/Ruby/.rubocop.yml create mode 100644 algorithms/Ruby/Arrays/contains_duplicate.rb create mode 100644 algorithms/Ruby/Arrays/majority_element.rb create mode 100644 algorithms/Ruby/Arrays/max_subarray_sum.rb create mode 100644 algorithms/Ruby/Arrays/reverse_array.rb create mode 100644 algorithms/Ruby/Arrays/single_number.rb create mode 100644 algorithms/Ruby/Arrays/two_sum.rb create mode 100644 algorithms/Ruby/Gemfile create mode 100644 algorithms/Ruby/Gemfile.lock create mode 100644 algorithms/Ruby/README.md diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 00000000..f54bca17 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,40 @@ +name: Ruby +on: + push: + branches: [ main ] + paths: '**.rb' + pull_request: + branches: [ main ] + paths: '**.rb' +permissions: + contents: read +jobs: + lint: + name: Run Ruby linter + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.2' + bundler-cache: true + - name: Install dependencies + run: bundle install + - name: Run RuboCop + run: bundle exec rubocop + test: + name: Run Ruby tests + needs: [ lint ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.2' + bundler-cache: true + - name: Install dependencies + run: bundle install + - name: Run tests + run: bundle exec rspec **/*.rb diff --git a/.gitignore b/.gitignore index c15b5c75..30092b24 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,24 @@ Thumbs.db # Visual Studio Code .vscode/ + +# Ruby +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ +/.bundle/ +/vendor/bundle +/lib/bundler/man/ +.rvmrc diff --git a/README.md b/README.md index 62ef05c0..451690f7 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Python](https://github.com/MakeContributions/DSA/actions/workflows/python.yml/badge.svg)](https://github.com/MakeContributions/DSA/actions/workflows/python.yml) [![codespell](https://github.com/MakeContributions/DSA/actions/workflows/codespell.yml/badge.svg)](https://github.com/MakeContributions/DSA/actions/workflows/codespell.yml) [![CodeQL](https://github.com/MakeContributions/DSA/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/MakeContributions/DSA/actions/workflows/codeql-analysis.yml) +[![Ruby](https://github.com/MakeContributions/DSA/actions/workflows/ruby.yml/badge.svg)](https://github.com/MakeContributions/DSA/actions/workflows/ruby.yml) [![Discord](https://img.shields.io/discord/863049619734790185?color=7389D8&logo=discord&logoColor=ffffff&label=&labelColor=6A7EC2)](https://discord.gg/ydWxdqbTyK) # Data Structures and Algorithm @@ -102,6 +103,7 @@ It can be any of the following ones - **JavaScript**: `package.json` and `package-lock.json` - **Rust**: `Cargo.toml` and `Cargo.lock` - **Go**: `go.mod` +- **Ruby**: `Gemfile` #### Source Code File @@ -127,6 +129,7 @@ The programming should keep the naming convention rule of each programming langu | Go | @ayo-ajayi | | Python | @Arsenic-ATG, @sridhar-5 | | JavaScript | @ming-tsai | +| Ruby | @oliveiramarcio | ## Contributors diff --git a/algorithms/Ruby/.rubocop.yml b/algorithms/Ruby/.rubocop.yml new file mode 100644 index 00000000..43395cdb --- /dev/null +++ b/algorithms/Ruby/.rubocop.yml @@ -0,0 +1,16 @@ +require: + - rubocop-rspec + +Bundler/OrderedGems: + Exclude: + - 'Gemfile' + +AllCops: + DisplayCopNames: true + DisplayStyleGuide: true + TargetRubyVersion: 3.2.2 + NewCops: enable + +Style/FrozenStringLiteralComment: + Exclude: + - 'Gemfile' diff --git a/algorithms/Ruby/Arrays/contains_duplicate.rb b/algorithms/Ruby/Arrays/contains_duplicate.rb new file mode 100644 index 00000000..efa1288c --- /dev/null +++ b/algorithms/Ruby/Arrays/contains_duplicate.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Given an integer array nums, return true if any value appears at least, +# twice in the array and return false if every element is distinct. +# Example: +# Input: nums = [1, 2, 3, 1] +# Output: true +# Time: O(n) +# Space: O(n) +module Arrays + def self.contains_duplicate(nums) + !nums.uniq!.nil? + end +end + +RSpec.describe Arrays do + describe '.contains_duplicate(nums)' do + it 'returns true if any value appears at least twice in the array' do + expect(Arrays.contains_duplicate([1, 2, 3, 1])).to be true + end + + it 'returns false if every element is distinct' do + expect(Arrays.contains_duplicate([1, 2, 3, 4])).to be false + end + + it 'returns false for an empty array' do + expect(Arrays.contains_duplicate([])).to be false + end + end +end diff --git a/algorithms/Ruby/Arrays/majority_element.rb b/algorithms/Ruby/Arrays/majority_element.rb new file mode 100644 index 00000000..9199322f --- /dev/null +++ b/algorithms/Ruby/Arrays/majority_element.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Given an array nums of size n, return the majority element. +# The majority element appears more than n/2 times +# Time: O(n) +# Space: O(1) +module Arrays + def self.majority_element(nums) + candidate, count = find_candidate(nums) + count.positive? ? candidate : nil + end + + def self.find_candidate(nums) + candidate = nil + count = 0 + + nums.each do |num| + candidate, count = update_candidate(candidate, count, num) + end + + [candidate, count] + end + + def self.update_candidate(candidate, count, num) + if count.zero? + candidate = num + count = 1 + elsif candidate == num + count += 1 + else + count -= 1 + end + + [candidate, count] + end +end + +RSpec.describe Arrays do + describe '.majority_element' do + it 'returns the majority element in the array' do + expect(Arrays.majority_element([3, 2, 3])).to eq(3) + expect(Arrays.majority_element([2, 2, 1, 1, 1, 2, 2])).to eq(2) + end + + it 'handles different input sizes' do + expect(Arrays.majority_element([1])).to eq(1) + expect(Arrays.majority_element([1, 1])).to eq(1) + expect(Arrays.majority_element([1, 2, 2])).to eq(2) + expect(Arrays.majority_element([3, 3, 4, 2, 4, 4, 2, 4, 4])).to eq(4) + end + + it 'handles negative numbers' do + expect(Arrays.majority_element([-1, -1, -1, 2, 2, 2, -1, -1])).to eq(-1) + expect(Arrays.majority_element([-3, -2, -3, -3])).to eq(-3) + end + end +end diff --git a/algorithms/Ruby/Arrays/max_subarray_sum.rb b/algorithms/Ruby/Arrays/max_subarray_sum.rb new file mode 100644 index 00000000..9a2c00ea --- /dev/null +++ b/algorithms/Ruby/Arrays/max_subarray_sum.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# Algorithm Name: Max Sum of Sub Array +# Time: O(n) +# Space: O(n) +module Arrays + def self.max_subarray_sum(arr) + arr_size = arr.length + max_sum = arr[0] + max_sum_curr = arr[0] + + (1...arr_size).each do |i| + max_sum_curr = [arr[i], max_sum_curr + arr[i]].max + max_sum = [max_sum, max_sum_curr].max + end + + max_sum + end +end + +RSpec.describe Arrays do + describe '.max_subarray_sum' do + it 'returns the max subarray sum for positive numbers' do + expect(Arrays.max_subarray_sum([1, 2, 3, 4])).to eq(10) + end + + it 'returns the max subarray sum for negative numbers' do + expect(Arrays.max_subarray_sum([-2, -3, -4, -1, -2, -1, -5, -3])).to eq(-1) + end + + it 'returns the max subarray sum for a mix of positive and negative numbers' do + expect(Arrays.max_subarray_sum([3, 2, -4, 9])).to eq(10) + end + + it 'returns the max subarray sum for an array with a single element' do + expect(Arrays.max_subarray_sum([5])).to eq(5) + end + end +end diff --git a/algorithms/Ruby/Arrays/reverse_array.rb b/algorithms/Ruby/Arrays/reverse_array.rb new file mode 100644 index 00000000..163a26a4 --- /dev/null +++ b/algorithms/Ruby/Arrays/reverse_array.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Description: This program reverses array elements by swapping the first half part of the array +# Time Complexity : O(n/2), where n is the array size +# Auxiliary Space : O(1) +module Arrays + def self.reverse_array(arr) + n = arr.length + mid = n / 2 + (0...mid).each { |i| arr[i], arr[n - i - 1] = arr[n - i - 1], arr[i] } + arr + end +end + +RSpec.describe Arrays do + describe '.reverse_array' do + it 'reverses the elements of an array' do + expect(Arrays.reverse_array([1, 2, 3, 4, 5])).to eq([5, 4, 3, 2, 1]) + expect(Arrays.reverse_array([1, 2, 3, 4])).to eq([4, 3, 2, 1]) + expect(Arrays.reverse_array([1])).to eq([1]) + expect(Arrays.reverse_array([])).to eq([]) + end + end +end diff --git a/algorithms/Ruby/Arrays/single_number.rb b/algorithms/Ruby/Arrays/single_number.rb new file mode 100644 index 00000000..64c1ccb1 --- /dev/null +++ b/algorithms/Ruby/Arrays/single_number.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# Given a non-empty array of int, every element appears twice except for 1. Find that element. +# Time: O(n) +# Space: O(n) +module Arrays + def self.single_number(arr) + frequency = Hash.new(0) + + arr.each do |num| + frequency[num] += 1 + end + + frequency.each do |num, count| + return num if count == 1 + end + + nil + end +end + +RSpec.describe Arrays do + describe '.single_number' do + it 'returns the single number in the array' do + expect(Arrays.single_number([1])).to eq(1) + expect(Arrays.single_number([1, 2, 2])).to eq(1) + expect(Arrays.single_number([1, 2, 1])).to eq(2) + expect(Arrays.single_number([4, 2, 2, 3, 4, 1, 1])).to eq(3) + expect(Arrays.single_number([5, 5, 6, 6, 7, 7, 8])).to eq(8) + end + + it 'returns nil when no single number is found' do + expect(Arrays.single_number([1, 1, 2, 2, 3, 3])).to be_nil + expect(Arrays.single_number([1, 1, 2, 2, 3, 3, 4, 4])).to be_nil + end + end +end diff --git a/algorithms/Ruby/Arrays/two_sum.rb b/algorithms/Ruby/Arrays/two_sum.rb new file mode 100644 index 00000000..6da4484d --- /dev/null +++ b/algorithms/Ruby/Arrays/two_sum.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# Problem Statement: given an array of integers nums and an integer target, +# return indices of the two numbers such that they add up to target. +# +# You may assume that each input would have exactly one solution, +# and you may not use the same element twice. +# +# Input: An array of integers and a target (int) +# Output: array of indexes of len(2) with sum of element at that index equal to target or nil +# +# Time: O(n) +# Space: O(n) +module Arrays + def self.two_sum(nums, target) + complement_hash = {} + + nums.each_with_index do |num, index| + complement = target - num + + return [complement_hash[complement], index] if complement_hash.key?(complement) + + complement_hash[num] = index + end + + nil + end +end + +RSpec.describe Arrays do + describe '.two_sum' do + it 'returns the correct indices' do + expect(Arrays.two_sum([2, 7, 11, 15], 9)).to eq([0, 1]) + expect(Arrays.two_sum([3, 2, 4], 6)).to eq([1, 2]) + expect(Arrays.two_sum([3, 3], 6)).to eq([0, 1]) + end + + it 'returns nil if no solution is found' do + expect(Arrays.two_sum([1, 2, 3, 4], 9)).to be_nil + expect(Arrays.two_sum([], 5)).to be_nil + end + end +end diff --git a/algorithms/Ruby/Gemfile b/algorithms/Ruby/Gemfile new file mode 100644 index 00000000..e202433f --- /dev/null +++ b/algorithms/Ruby/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'rspec' +gem 'rubocop' +gem 'rubocop-rspec' diff --git a/algorithms/Ruby/Gemfile.lock b/algorithms/Ruby/Gemfile.lock new file mode 100644 index 00000000..dce64528 --- /dev/null +++ b/algorithms/Ruby/Gemfile.lock @@ -0,0 +1,62 @@ +GEM + remote: https://rubygems.org/ + specs: + ast (2.4.2) + diff-lcs (1.5.0) + json (2.6.3) + language_server-protocol (3.17.0.3) + parallel (1.23.0) + parser (3.2.2.3) + ast (~> 2.4.1) + racc + racc (1.7.1) + rainbow (3.1.1) + regexp_parser (2.8.1) + rexml (3.2.5) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.53.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.3) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.28.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.29.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.18.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.23.1) + rubocop (~> 1.33) + rubocop-rspec (2.22.0) + rubocop (~> 1.33) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + unicode-display_width (2.4.2) + +PLATFORMS + arm64-darwin-22 + +DEPENDENCIES + rspec + rubocop + rubocop-rspec + +BUNDLED WITH + 2.4.15 diff --git a/algorithms/Ruby/README.md b/algorithms/Ruby/README.md new file mode 100644 index 00000000..02d27cda --- /dev/null +++ b/algorithms/Ruby/README.md @@ -0,0 +1,9 @@ +# Ruby + +## Arrays +- [Contains Duplicate](arrays/contains_duplicate.rb) +- [Majority Element](arrays/majority_element.rb) +- [Maximum subarray sum (Kadane's Algorithm)](arrays/maximum-subarray-sum.rb) +- [Reverse Array](arrays/reverse-array.rb) +- [Single Number](arrays/single-number.rb) +- [Two Sum](arrays/two-sum.rb)