[关闭]
@smilence 2020-02-28T10:23:31.000000Z 字数 5806 阅读 968

Chapter 3: Collections

Ruby SDE-Foundations


0 概述

Collection 包括String, Array, Hash

0.1 如果String没有对应的函数,那么除非有in-place处理的要求,总是可以用str.chars或者str.split("")String的问题转化成Array的问题

  1. e.g.
  2. def caesar_cipher(message, n)
  3. alphabet = ("a".."z").to_a
  4. new_message = ""
  5. new_chars = message.chars.map do |char|
  6. old_index = alphabet.index(char)
  7. new_char = (old_index != nil ? alphabet[(old_index + n)%26] : char)
  8. new_char
  9. end
  10. return new_chars.join("")
  11. end

1 处理Sentence的问题

sentence中包含多个词,用空格隔开,处理每个词

处理这样的string, 总是通过str.split(" ")把问题转换成array问题 (array of strings), 然后对array进行遍历 (arr.each/ arr.each_with_index / arr.map) 处理每个分隔开的word.

1.1 sentence中包含多个词,用空格隔开,转换成另一句sentence

通过str.split(" ")把问题转换成array问题 (array of strings), 然后把array用array.map变换成新array new_arr, 最后再用new_arr.join(" ")转回为string

  1. words = str.split(" ")
  2. # 把parts按照题意转换成目标array
  3. new_words = words.map do |part|
  4. new_word = ...
  5. new_word
  6. end
  7. return new_Words.join(" ")

2 Collection的聚合类问题,输入输出是n => 1的结构

2.1 由一组元素得出一个非boolean的结果

给定一组元素,得出一个非boolean的结果(boolean见2.2),通常是array元素同样的类型, 用array.injectaccele得到new_acc再结算

e.g. Write a method matrix_addition_reloaded that accepts any number of matrices as arguments. The method should return a new matrix representing the sum of the arguments. Matrix addition can only be performed on matrices of similar dimensions, so if all of the given matrices do not have the same "height" and "width", then return nil.

  1. def matrix_addition_reloaded(*matrices)
  2. return matrices.inject() do |acc, m|
  3. return nil if acc.length != m.length || acc[0].length != m[0].length
  4. matrix_addition(acc, m)
  5. end
  6. end

2.2 对一组元素做一个判断(boolean) 的问题

给定一组元素, 对某个事实做true or false判断的问题,可以认为是inject的特殊情况, 用:

全都是 / 只存在 => array.all? or (0..num).all

  1. # Write a method, `only_vowels?(str)`, that accepts a string as an arg.
  2. # The method should return true if the string contains only vowels.
  3. # The method should return false otherwise.
  4. def only_vowels?(str)
  5. vowels = "aeiou"
  6. str.split("").all? { |char| vowels.include?(char) }
  7. end
  1. def is_sorted(arr)
  2. (0...arr.length - 1).all? { |i| arr[i] <= arr[i + 1] }
  3. end

有没有 / 至少有一个 => array.any? or (0..num).any

  1. # Write a method, adult_in_group?(people), that accepts an array containing people.
  2. # The method should return true if there is at least 1 person with an age of 18 or greater.
  3. # The method should return false otherwise.
  4. def adult_in_group?(people)
  5. people.any? { |person| person[:age] >= 18 }
  6. end

完全没有 / 除去某个就没有了 => array.none? or (0..num).none

Write a method, coprime?(num_1, num_2), that accepts two numbers as args.The method should return true if the only common divisor between the two numbers is 1. The method should return false otherwise. For example coprime?(25, 12) is true because 1 is the only number that divides both 25 and 12.

  1. def coprime?(num_1, num_2)
  2. (2..num_1).none? { |factor| num_1 % factor == 0 && num_2 % factor == 0}
  3. end

3. 在Collection中处理相邻的元素关系的问题

对于需要比较或者交换前后元素的情况,用(0...arr.length-1)配合arr[idx]比直接用arr.each.with_index更好, 因为有时候我们需要去掉头尾的部分. 如果头尾具有特殊性,则对头尾和中间元素 ((1...arr.length-1))分开处理.

  1. def peak_finder(arr)
  2. peaks = []
  3. return [] if arr.length == 0
  4. if arr.length > 0 && arr[0] > arr[1]
  5. peaks << arr[0]
  6. end
  7. (1...arr.length-1).each do |idx|
  8. if arr[idx] > arr[idx-1] && arr[idx] > arr[idx+1]
  9. peaks << arr[idx]
  10. end
  11. end
  12. if arr.length > 0 && arr[-1] > arr[-2]
  13. peaks << arr[-1]
  14. end
  15. return peaks
  16. end

3.1 分段处理的问题

  1. 先处理collection为空的情况;
  2. 用一个变量来记录当前段的状态,然后当arr[idx] != arr[idx+1]时清算之前段,并且reset这个变量。注意变量的初始值应该把每段的第1个元素考虑在内,即count = 1
  3. 处理好最后一个idx,即idx == nums.length-1
  1. e.g. # p compress_str("aaabbc") # => "3a2bc"
  2. def compress_str(str)
  3. return str if str.empty?
  4. count = 1
  5. new_str = ""
  6. (0...str.length).each do |idx|
  7. if idx != str.length - 1 && str[idx] == str[idx+1]
  8. count += 1
  9. else
  10. new_str += count == 1 ? str[idx] : count.to_s + str[idx]
  11. count = 1
  12. end
  13. end
  14. return new_str
  15. end
  1. # 674. Longest Continuous Increasing Subsequence
  2. def find_length_of_lcis(nums)
  3. return 0 if nums.empty?
  4. max_count, count = 1, 1
  5. (0...nums.length).each do |idx|
  6. # 可以认为越界元素一定不会满足条件
  7. if idx != nums.length-1 && nums[idx] < nums[idx+1]
  8. count += 1
  9. else
  10. max_count = count if count > max_count
  11. count = 1
  12. end
  13. end
  14. return max_count
  15. end

4. 对Collection进行反复操作直到满足条件的问题

可以用一个变量completed = false, 然后用completed这个boolean变量去记录每一轮的情况,直到completedtrue

  1. completed = false
  2. until completed
  3. completed = true # 默认这一轮completed为true
  4. (0...arr.length).each do |idx|
  5. # 处理每一个arr[idx]
  6. if condition 满足一定条件下
  7. # 修改arr[idx]
  8. completed = false
  9. end
  10. end
  11. end
  12. return arr
  1. e.g. bubble sort
  2. def bubble_sort(array)
  3. sorted = false
  4. until sorted
  5. sorted = true
  6. (0...array.length - 1).each do |i|
  7. if array[i] > array[i + 1]
  8. array[i], array[i + 1] = array[i + 1], array[i]
  9. sorted = false
  10. end
  11. end
  12. end
  13. return array
  14. end

5. 在Collection中查找的问题

5.1 查找所有满足条件的元素(的数量)

array.select,block里输出需要满足的条件。如果只需要元素的数量,则可以用arr.count

5.2 查找第一个/最后一个满足条件的元素

这类问题适合单独用一个函数实现(因为找到后可能需要立即退出查找),用变量idx = 0idx = arr.length-1, 用while循环找到,即返回.

  1. def hipsterfy(word)
  2. vowels = "aeiou"
  3. i = word.length - 1
  4. while i >= 0
  5. if vowels.include?(word[i])
  6. return word[0...i] + word[i+1..-1]
  7. end
  8. i -= 1
  9. end
  10. word
  11. end

5.3 已知range查找的问题

例如找因子, 找公因子, 找质数, 这类问题等同于在collection中查找的问题。

5.3.1 知道算子的范围,用range (1..num).each来逐个检查
  1. def is_prime?(num) # 实际上是查找因子
  2. return false if num <= 1
  3. (2..num).each do |n|
  4. return false if num % n == 0
  5. end
  6. return true
  7. end
5.3.2 知道结果的范围,用while result <= numi += 1来逐个检查
  1. def perfect_square?(num)
  2. i = 1
  3. square = nil
  4. while square <= num
  5. square = i * i
  6. return true if square == num
  7. i += 1
  8. end
  9. return false
  10. end
  1. def power_of_two?(num)
  2. return true if num == 1
  3. result = 1
  4. while result <= num
  5. result = result * 2
  6. return true if result == num
  7. end
  8. return false
  9. end

5.4 查找第n个或者前n个满足条件的元素

通常这类问题都不能用select因为我们并不知道元素所处的范围

idx = 0开始,
查找第n个元素,用一个count = 0来记录元素的数量,until count == n
查找前n个元素,用一个array = []来记录所有元素,until array.length == n
在循环内,idx递增,每次找到都记录给count或者array

  1. def nth_prime(n)
  2. count = 0
  3. num = 1
  4. until count == n
  5. num += 1
  6. if is_prime?(num)
  7. count += 1
  8. end
  9. end
  10. num
  11. end

6 对称和反转

检查一个Collection是否对称,或者对一个Collection进行反转,用头尾i = 0, j = arr.length - 1两个marker,以及while i < j相向而行。然后每次都对arr[i]arr[j]进行比较或者交换。

  1. e.g.
  2. def palindrome?(string)
  3. i, j = 0, string.length - 1
  4. while i < j
  5. return false if string[i] != string[j]
  6. i += 1
  7. j -= 1
  8. end
  9. return true
  10. end

7 求collection内元素的两两组合

  1. arr.each_with_index do |ele1, idx1|
  2. arr.each_with_index do |ele2, idx2|
  3. if idx2 > idx1
  4. # do something about ele1 and ele2
  1. e.g.
  2. def substrings(string)
  3. subs = []
  4. (0...string.length).each do |start_idx|
  5. (0...string.length).each do |end_idx|
  6. subs << string[start_idx..end_idx] if start_idx <= end_idx
  7. end
  8. end
  9. subs
  10. end

8 Collection内统计所有元素的问题

例如是否unique,是否duplicate,两个collection元素是否相同,一般可以新建一个Hash.new作为counter,然后对collection进行循环的同时,检查 counter内已有的记录,并且记录新的元素。

  1. def unique_chars?(string)
  2. hash = Hash.new(0)
  3. string.each_char do |char|
  4. return false if hash.has_key?(char)
  5. hash[char] += 1
  6. end
  7. true
  8. end
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注