paiza プログラミング

[Ruby]paiza 線形探索メニュー 第k要素の探索

paiza解説_第k要素の探索

今回はpaiza 線形探索メニュー  セクション4【第k要素の探索】を解説します。

セクション4【第k要素の探索】は、与えられた数列データの中から、k番目に大きな値を探し出す問題です。

  • STEP1:n個数列aが与えられ、その中で2番目に大きな数を出力する
  • FINAL:n個数列a整数kが与えられ、その中でk番目に大きな数を出力する

STEP問題を解いてみる

簡単な解説は付けていますが、難しいと感じたら下記の記事も参考にしてみて下さい。

STEP1: 2番目に大きな値 (paizaランク C 相当)

n個数列aが与えられ、その中で2番目に大きな数を出力する問題です。

 解答例1

INPUT1 = <<~"EOS"
  5
  -9 10 6 0 -3
EOS
OUTPUT1 = <<~"EOS"
  6
EOS

def solve1(input_lines)
  n, *ary = input_lines.split.map(&:to_i)

  2.times do |i|
    0.upto(n - 2 - i) do |j|
      if ary[j] > ary[j + 1]
        ary[j], ary[j + 1] = ary[j + 1], ary[j]
      end
    end
  end

  ary[-2]
end

puts solve1(STDIN.read)
# puts solve1(INPUT1)
# > 6
  • 整数n, 配列aryの入力データを受け取る
  • i=0~1 (2回)の繰り返し処理を設定する
    ※ iの繰り返しをn-1回行えばバブルソートになりますね!

    • j=0~n-i-2 の繰り返し処理を設定する
      • もしary[j]ary[j+1]より大きかったら、ary[j]ary[j+1]を入れ替える

処理の流れ

i = 0, j = 0 ~ n - i - 2 (3) のループ開始

第k要素の探索-01

i = 0, j = 0

ary[0](-9) > ary[1](10) -> false

入れ替えは行わない


第k要素の探索-02

第k要素の探索-03

i = 0, j = 1

ary[1](10) > ary[2](6) -> true

ary[1](10) と ary[2](6) を入れ替える


第k要素の探索-04

第k要素の探索-05

i = 0, j = 2

ary[2](10) > ary[3](0) -> true

ary[2](10) と ary[3](0) を入れ替える


第k要素の探索-06

第k要素の探索-07

i = 0, j = 3

ary[3](10) > ary[4](3) -> true

ary[3](10) と ary[4](3) を入れ替える


i = 0, j = 0 ~ n - i - 2 (3) のループ終了

第k要素の探索-08

aryの中で1番目に大きい値(10)ary[-1]に移動してきてi = 0 のループ終了


i = 1, j = 0 ~ n - i - 2 (2) のループ開始

第k要素の探索-09

i = 1, j = 0

ary[0](-9) > ary[1](6) -> false

入れ替えは行わない


第k要素の探索-10

第k要素の探索-11

i = 1, j = 1

ary[1](6) > ary[2](0) -> true

ary[1](6) と ary[2](0) を入れ替える


第k要素の探索-12

第k要素の探索-13

i = 1, j = 2

ary[2](6) > ary[3](3) -> true

ary[2](6) と ary[3](3) を入れ替える


i = 1, j = 0 ~ n - i - 2 (2) のループ終了

第k要素の探索-14

aryの中で2番目に大きい値(6)ary[-2]に移動してきてi = 1 のループ終了


解答例2

INPUT1 = <<~"EOS"
  5
  -9 10 6 0 -3
EOS
OUTPUT1 = <<~"EOS"
  6
EOS

def solve2(input_lines)
  n, *ary = input_lines.split.map(&:to_i)

  ary.sort[-2]
end

puts solve2(STDIN.read)
# > puts solve2(INPUT1)
# > 6

sortメソッドで、aryを昇順に並び替えて後ろから2番目の値を参照します。


解答例3

def solve3(input_lines)
  n, *ary = input_lines.split.map(&:to_i)

  ary.max(2).last
end

puts solve3(STDIN.read)
# puts solve3(INPUT1)
# > 6

maxメソッドで、引数にkを渡すとk番目までの大きい数を降順の配列で返します。


k番目に大きな値 (paizaランク C 相当)を解いてみる

※ paiza 線形探索メニューより

問題

整数 n と、数列 a_1, ... , a_n と、整数 k が与えられます。

数列に含まれる数のうち、k 番目に大きいものを出力してください。

入力される値

入力は以下のフォーマットで与えられます。

n
a_1 a_2 ... a_n
k

  • 1行目に、数列の長さを表す整数 n が与えられます。
  • 2行目に、数列の値 a_i が半角スペース区切りで与えられます。
  • 3行目に、整数 k が与えられます。

入力値最終行の末尾に改行が1つ入ります。

期待する出力

数列に含まれる数のうち、k 番目に大きいものを出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

条件

すべてのテストケースにおいて、以下の条件をみたします。

  • 入力は全て整数
  • 1 ≦ n ≦ 10,000
  • -1,000,000,000 ≦ a_i ≦ 1,000,000,000
  • i ≠ j ならば a_i ≠ a_j
  • 1 ≦ k ≦ n
入力例1
5
-9 10 6 0 -3
4
出力例1
-3
攻略ポイント

ポイント

  • 要素を並び替えてk番目に大きい数を探索する

デバッグを楽にするためにメソッドを作成します。メソッドについては下記の記事も参考にしてみて下さい。

問題を解く流れ

入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。正しく受け取れていれば確認用コードは削除します。

INPUT1 = <<~"EOS"
  5
  -9 10 6 0 -3
  4
EOS
OUTPUT1 = <<~"EOS"
  -3
EOS

# 確認用コード
p INPUT1
# > "5\n-9 10 6 0 -3\n4\n"
p OUTPUT1
# > "-3\n"

続いて問題を解くメソッド( solve メソッドとします)を変数の下に定義します。

まず、入力データを受け取る処理を書きます。

def solve(input_lines)
  n, *ary, k = input_lines.split.map(&:to_i)

  # 確認用コード
  [n, ary, k]
end

# 確認用コード
p solve(INPUT1)
# > [5, [-9, 10, 6, 0, -3], 4]

データが正しく受け取れていれば、solve メソッドに数列aryの中でk番目に大きな値を探索する処理を追加していきます。

  • i=0~k-1 (k回)の繰り返し処理を設定する
    • j=0~n-i-2 の繰り返し処理を設定する
      • もしary[j]ary[j+1]より大きかったら、ary[j]ary[j+1]を入れ替える
def solve(input_lines)
  n, *ary, k = input_lines.split.map(&:to_i)

  k.times do |i|
    0.upto(n - 2 - i) do |j|
      if ary[j] > ary[j + 1]
        ary[j], ary[j + 1] = ary[j + 1], ary[j]
      end
    end
  end

  # 確認用コード
  ary
end

# 確認用コード
p solve(INPUT1)
# > [-9, -3, 0, 6, 10]
p solve(INPUT1) == OUTPUT1
# > false

あとはary[-k](k番目に大きい数)を出力すれば完成です。

p solve(INPUT1) == OUTPUT1 -> true を確認するために、ary[-k]の値を文字列に変換して末尾に改行を加えます。

def solve(input_lines)
  n, *ary, k = input_lines.split.map(&:to_i)

  k.times do |i|
    0.upto(n - 2 - i) do |j|
      if ary[j] > ary[j + 1]
        ary[j], ary[j + 1] = ary[j + 1], ary[j]
      end
    end
  end

  # ary[-k] を文字列に変換して末尾に改行を追加
  ary[-k].to_s << "\n"
end

# 確認用コード
p solve(INPUT1)
# > "-3\n"
p solve(INPUT1) == OUTPUT1
# > true

確認用コードを標準入力からのデータ受け取りに変更、標準出力をpメソッドからputsメソッドに変更して、動作確認をしたら提出します。(STDIN.readの入力終了は Ctrl+D 又は Ctrl+Z)

解答コード
def solve(input_lines)
  n, *ary, k = input_lines.split.map(&:to_i)

  k.times do |i|
    0.upto(n - 2 - i) do |j|
      if ary[j] > ary[j + 1]
        ary[j], ary[j + 1] = ary[j + 1], ary[j]
      end
    end
  end

  # ary[-k] を文字列に変換して末尾に改行を追加
  ary[-k].to_s << "\n"
end

puts solve(STDIN.read)
他の解答例

STEP1と同様に以下の方法が使えます

  • sortメソッドで配列aryを昇順に並び替えてからary[-k]を参照する
  • maxメソッド引数にkを与えて返ってきた配列の末尾を参照する
INPUT1 = <<~"EOS"
  5
  -9 10 6 0 -3
  4
EOS
OUTPUT1 = <<~"EOS"
  -3
EOS

def solve1(input_lines)
  n, *ary, k = input_lines.split.map(&:to_i)

  ary.sort[-k]
end

puts solve1(STDIN.read)
# puts solve1(INPUT1)
# > -3

def solve2(input_lines)
  n, *ary, k = input_lines.split.map(&:to_i)

  ary.max(k).last
end

puts solve2(STDIN.read)
# puts solve2(INPUT1)
# > -3

今回のまとめ

セクション4では、線形探索法(と言うかバブルソート)を使ってk番目に大きな要素の探索を行いました。
nの最大が10,000だったのでバブルソートO(n2)ではタイムオーバーになるかと思いましたが、何とか通過出来ました。
今回は勉強のために自前でアルゴリズムを実装しましたが、通常はsortメソッドmax(k)メソッドを利用しましょう。

  • バブルソートでテストケース12 (n=9805) にかかった時間 5.35秒
  • sortメソッドでテストケース12 (n=9805) にかかった時間 0.12秒
  • max(k)メソッドでテストケース12 (n=9805) にかかった時間 0.13秒

単純な処理なら組み込みメソッドを利用したほうが格段に速いですね!



【PR】アルゴリズム学習でお世話になった本


アルゴリズム関連の技術書は大抵C/C++で書かれていますが、ある程度プログラムが出来れば、処理の流れは理解することが出来ます。






通称: 螺旋本。C++で解説されています。AOJ(Aizu Online Judge)を運営している会津大学の渡辺准教授が書いた本です。データ構造や計算量の内容から丁寧に書いてありますのでアルゴリズム学習の最初の参考書としてオススメです。







通称: 蟻本。C++で解説されています。競技プログラミング中級者の定番書と言われていて、競技プログラミングで利用できる色々なアルゴリズムを学ぶことが出来ます。かなり高度な内容も含まれているので1冊分を完全に理解するのにはかなりの時間がかかりそうですが、手元に置いて何度も読み返しています。







通称: チーター本。C#, C++, JAVAでTopcoderの問題を解説しています。初心者~中級者向けに書かれているので解説が非常に丁寧です。







Python3でアルゴリズムを解説している本です。講義スタイルの本で、図やフローチャートを使ってアルゴリズムとデータ構造についてしっかりと説明されています。代わりにコードは少なめです。Pythonコードが読めなくても十分理解できると思います。


-paiza, プログラミング
-, ,

© 2024 じゃいごテック