🐼

Rustのリスト操作練習問題集#2(初級編2)

に公開

Rustのリスト操作練習問題集

Rustのイテレータとコレクション操作を練習するための問題集です。

基本的にはiter操作と配列の操作だけで解けるようにしています。 テスト用のリストも載せてあります。そのままコピペで使えるようにしてあります。別解などありましたらコメントくださるとありがたいです。

初級21: any/allで条件判定

問題

  • スライスが正の数を含むか(any)と全て非負か(all)を判定する。
  • 入力: input: &[i32]
  • 出力: (bool, bool)

テンプレートとテスト

fn solution(input: &[i32]) -> (bool, bool) {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[-1,0,2]), (true, false));
    assert_eq!(solution(&[0,0,0]), (false, true));
    assert_eq!(solution(&[1,2,3]), (true, true));
    assert_eq!(solution(&[-5,-4]), (false, false));
}
解答と解説
fn solution(input: &[i32]) -> (bool, bool) {
    let any_pos = input.iter().any(|&x| x > 0);
    let all_nonneg = input.iter().all(|&x| x >= 0);
    (any_pos, all_nonneg)
}
fn solution(input: &[i32]) -> (bool, bool) {
    input.iter().fold((false, true), |(any_pos, all_nonneg), &x| {
        (any_pos || x > 0, all_nonneg && x >= 0)
    })
}
  • fold(any, all) を同時に集計できる。

初級22: nthでn番目要素

問題

  • イテレータでn番目(0始まり)の要素を安全に取得する。
  • 入力: input: &[i32], n: usize
  • 出力: Option<i32>

テンプレートとテスト

fn solution(input: &[i32], n: usize) -> Option<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[10,20,30], 0), Some(10));
    assert_eq!(solution(&[10,20,30], 2), Some(30));
    assert_eq!(solution(&[10,20,30], 3), None);
    assert_eq!(solution(&[], 0), None);
}
解答と解説
fn solution(input: &[i32], n: usize) -> Option<i32> {
    input.iter().cloned().nth(n)
}
fn solution(input: &[i32], n: usize) -> Option<i32> {
    input.iter().skip(n).next().copied()
}
  • skip(n).next()nth(n) と等価の取り出し方になる。

初級23: take/skipで範囲抽出

問題

  • 最初のk個をスキップし、その後m個を取得したベクタを返す。
  • 入力: input: &[i32], k: usize, m: usize
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(input: &[i32], k: usize, m: usize) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[1,2,3,4,5], 1, 3), vec![2,3,4]);
    assert_eq!(solution(&[1,2], 5, 2), vec![] as Vec<i32>);
    assert_eq!(solution(&[], 0, 1), vec![] as Vec<i32>);
    assert_eq!(solution(&[9,8,7], 2, 5), vec![7]);
}
解答と解説
fn solution(input: &[i32], k: usize, m: usize) -> Vec<i32> {
    input.iter().cloned().skip(k).take(m).collect()
}
fn solution(input: &[i32], k: usize, m: usize) -> Vec<i32> {
    let end = (k + m).min(input.len());
    input.get(k..end).unwrap_or(&[]).to_vec()
}
  • 範囲抽出は get(k..end)to_vec() の組み合わせでも書ける。

初級24: chainで2列結合

問題

  • 2つのスライスを連結し、1つのVec<i32>にする。
  • 入力: a: &[i32], b: &[i32]
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(a: &[i32], b: &[i32]) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[1,2], &[3,4]), vec![1,2,3,4]);
    assert_eq!(solution(&[], &[]), vec![] as Vec<i32>);
    assert_eq!(solution(&[5], &[]), vec![5]);
    assert_eq!(solution(&[], &[6,7]), vec![6,7]);
}
解答と解説
fn solution(a: &[i32], b: &[i32]) -> Vec<i32> {
    a.iter().cloned().chain(b.iter().cloned()).collect()
}
fn solution(a: &[i32], b: &[i32]) -> Vec<i32> {
    a.iter().copied().chain(b.iter().copied()).collect()
}
  • Copy な要素は cloned() ではなく copied() を使うと簡潔。

初級25: peekableで山数カウント

問題

  • 隣接3点(a,b,c)a<b>cとなる山の数を数える。
  • 入力: input: &[i32]
  • 出力: usize

テンプレートとテスト

fn solution(input: &[i32]) -> usize {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[1,3,2,4,3]), 2);
    assert_eq!(solution(&[1,2,3]), 0);
    assert_eq!(solution(&[]), 0);
    assert_eq!(solution(&[5,4,3,2]), 0);
}
解答と解説
fn solution(input: &[i32]) -> usize {
    if input.len() < 3 { return 0; }
    input.windows(3).filter(|w| w[0] < w[1] && w[1] > w[2]).count()
}
fn solution(input: &[i32]) -> usize {
    input
        .windows(3)
        .filter(|w| matches!(w, &[a, b, c] if a < b && b > c))
        .count()
}
  • windows(3) の各要素を配列パターンで受けて条件式を簡潔に書ける。

初級26: revで逆順ベクタ

問題

  • スライスを逆順イテレータで走査し、逆順のVec<i32>を返す。
  • 入力: input: &[i32]
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(input: &[i32]) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[1,2,3]), vec![3,2,1]);
    assert_eq!(solution(&[]), vec![] as Vec<i32>);
    assert_eq!(solution(&[5]), vec![5]);
    assert_eq!(solution(&[9,9]), vec![9,9]);
}
解答と解説
fn solution(input: &[i32]) -> Vec<i32> {
    input.iter().cloned().rev().collect()
}
fn solution(input: &[i32]) -> Vec<i32> {
    std::iter::FromIterator::from_iter(input.iter().copied().rev())
}
  • collect() と等価に FromIterator::from_iter でベクタ生成ができる。

初級27: タプル列の和(パターン)

問題

  • Vec<(i32,i32)> の各タプル和を Vec<i32> にする。
  • 入力: pairs: &[(i32,i32)]
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(pairs: &[(i32,i32)]) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[(1,2),(3,4)]), vec![3,7]);
    assert_eq!(solution(&[]), vec![] as Vec<i32>);
    assert_eq!(solution(&[(0,0)]), vec![0]);
    assert_eq!(solution(&[(-1,1)]), vec![0]);
}
解答と解説
fn solution(pairs: &[(i32,i32)]) -> Vec<i32> {
    pairs.iter().map(|&(a,b)| a + b).collect()
}
fn solution(pairs: &[(i32,i32)]) -> Vec<i32> {
    pairs.iter().cloned().map(|(a,b)| a + b).collect()
}
  • タプルの分解は map(|&(a,b)| ...) とパターンで直接取り出せる。

初級28: filter_mapで数値抽出

問題

  • 文字列列から数値文字列だけを数値に変換して集める。
  • 入力: tokens: &[&str]
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(tokens: &[&str]) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&["10","x","-3","42"]), vec![10,-3,42]);
    assert_eq!(solution(&[]), vec![] as Vec<i32>);
    assert_eq!(solution(&["a","b"]), vec![] as Vec<i32>);
    assert_eq!(solution(&["0","1"]), vec![0,1]);
}
解答と解説
fn solution(tokens: &[&str]) -> Vec<i32> {
    tokens.iter().filter_map(|&s| s.parse::<i32>().ok()).collect()
}
fn solution(tokens: &[&str]) -> Vec<i32> {
    tokens.iter().copied().filter_map(|s| s.parse().ok()).collect()
}
  • filter_map(|s| s.parse().ok()) で数値化に失敗した要素を自然に落とせる。

初級29: 文字列長でmax/min_by_key

問題

  • 文字列スライスの中で最長・最短の要素を求める。
  • 入力: words: &[&str]
  • 出力: (Option<&str>, Option<&str>)(max_len, min_len)

テンプレートとテスト

fn solution(words: &[&str]) -> (Option<&str>, Option<&str>) {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&["a","abcd","bc"]).0, Some("abcd"));
    assert_eq!(solution(&["a","abcd","bc"]).1, Some("a"));
    assert_eq!(solution(&[]), (None, None));
    assert_eq!(solution(&["x"]).0, Some("x"));
}
解答と解説
fn solution(words: &[&str]) -> (Option<&str>, Option<&str>) {
    let max = words.iter().max_by_key(|s| s.len()).copied();
    let min = words.iter().min_by_key(|s| s.len()).copied();
    (max, min)
}
fn solution(words: &[&str]) -> (Option<&str>, Option<&str>) {
    let max = words.iter().copied().max_by_key(|s| s.len());
    let min = words.iter().copied().min_by_key(|s| s.len());
    (max, min)
}
  • iter().copied()&&str&str に落としてから by_key すると扱いやすい。

初級30: dedupで連続重複除去

問題

  • 連続要素の重複を1つにまとめる。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![1,1,2,2,2,3]), vec![1,2,3]);
    assert_eq!(solution(vec![]), vec![] as Vec<i32>);
    assert_eq!(solution(vec![5]), vec![5]);
    assert_eq!(solution(vec![7,7,7,7]), vec![7]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.dedup();
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.dedup_by(|a, b| a == b);
    input
}
  • 連続同値の扱いを変えたいときは dedup_by で等価関係を指定できる。

初級31: spliceで範囲置換

問題

  • 指定範囲を別の列で置き換える。
  • 入力: input: Vec<i32>, range: std::ops::Range<usize>, with_: &[i32]
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>, range: std::ops::Range<usize>, with_: &[i32]) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![1,2,3,4,5], 1..4, &[9,9]), vec![1,9,9,5]);
    assert_eq!(solution(vec![1,2], 0..0, &[7]), vec![7,1,2]);
    assert_eq!(solution(vec![], 0..0, &[]), vec![] as Vec<i32>);
    assert_eq!(solution(vec![5,6,7], 1..2, &[0,0,0]), vec![5,0,0,0,7]);
}
解答と解説
fn solution(mut input: Vec<i32>, range: std::ops::Range<usize>, with_: &[i32]) -> Vec<i32> {
    input.splice(range, with_.iter().cloned());
    input
}
fn solution(input: Vec<i32>, range: std::ops::Range<usize>, with_: &[i32]) -> Vec<i32> {
    let left = input.iter().take(range.start).cloned();
    let mid  = with_.iter().cloned();
    let right = input.iter().skip(range.end).cloned();
    left.chain(mid).chain(right).collect()
}
  • 範囲置換は take/skipchain の合成でも再構築できる。

初級32: drainで範囲削除

問題

  • 指定範囲の要素を削除し、残りを返す。
  • 入力: input: Vec<i32>, range: std::ops::Range<usize>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>, range: std::ops::Range<usize>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![1,2,3,4,5], 1..4), vec![1,5]);
    assert_eq!(solution(vec![1], 0..1), vec![] as Vec<i32>);
    assert_eq!(solution(vec![], 0..0), vec![] as Vec<i32>);
    assert_eq!(solution(vec![7,8,9], 0..0), vec![7,8,9]);
}
解答と解説
fn solution(mut input: Vec<i32>, range: std::ops::Range<usize>) -> Vec<i32> {
    input.drain(range);
    input
}
fn solution(input: Vec<i32>, range: std::ops::Range<usize>) -> Vec<i32> {
    input
        .into_iter()
        .enumerate()
        .filter(|(i, _)| !range.contains(i))
        .map(|(_, v)| v)
        .collect()
}
  • enumerate() の添字で範囲外のみを filter 抽出して再構築できる。

初級33: split_at_mutで両側更新

問題

  • 長さ4以上のベクタを左右に分け、左端に+1, 右端に-1する。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![1,2,3,4]), vec![2,2,3,3]);
    assert_eq!(solution(vec![5,6,7,8,9]), vec![6,6,7,8,8]);
    assert_eq!(solution(vec![0,0,0,0]), vec![1,0,0,-1]);
    assert_eq!(solution(vec![10,20,30,40,50,60]), vec![11,20,30,40,50,59]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    if input.len() < 4 { return input; }
    let (l, r) = input.split_at_mut( input.len()/2 );
    if let Some(x) = l.first_mut() { *x += 1; }
    if let Some(x) = r.last_mut() { *x -= 1; }
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    if input.len() < 4 { return input; }
    let (left_head, left_tail) = input.split_at_mut(input.len()/2);
    if let Some((head, _)) = left_head.split_first_mut() { *head += 1; }
    if let Some((_, last)) = left_tail.split_last_mut() { *last -= 1; }
    input
}
  • 端要素アクセスは split_first_mut/split_last_mut でも書ける。

初級34: rotate_left/rotate_rightで回転

問題

  • ベクタを左にk回転させた結果を返す。
  • 入力: input: Vec<i32>, k: usize
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>, k: usize) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![1,2,3,4,5], 2), vec![3,4,5,1,2]);
    assert_eq!(solution(vec![1], 10), vec![1]);
    assert_eq!(solution(vec![], 3), vec![] as Vec<i32>);
    assert_eq!(solution(vec![9,8], 1), vec![8,9]);
}
解答と解説
fn solution(mut input: Vec<i32>, k: usize) -> Vec<i32> {
    if input.is_empty() { return input; }
    let r = k % input.len();
    input.rotate_left(r);
    input
}
fn solution(mut input: Vec<i32>, k: usize) -> Vec<i32> {
    if input.is_empty() { return input; }
    let r = k % input.len();
    input.rotate_right(input.len() - r);
    input
}
  • 左回転は長さ n に対して右回転 n - (k % n) と等価。

初級35: copy_from_sliceでコピー

問題

  • 同じ長さのスライス間でコピーする。
  • 入力: src: &[i32](長さN), dst: &mut [i32](長さN)
  • 出力: Vec<i32>(コピー後のdst)

テンプレートとテスト

fn solution(src: &[i32], mut dst: Vec<i32>) -> Vec<i32> {
    // ここに解答(dstの長さはsrcと同じと仮定)
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[1,2,3], vec![0,0,0]), vec![1,2,3]);
    assert_eq!(solution(&[], vec![]), vec![] as Vec<i32>);
    assert_eq!(solution(&[9], vec![7]), vec![9]);
    assert_eq!(solution(&[5,5], vec![1,2]), vec![5,5]);
}
解答と解説
fn solution(src: &[i32], mut dst: Vec<i32>) -> Vec<i32> {
    let n = src.len();
    let slice = &mut dst[..n];
    slice.copy_from_slice(src);
    dst
}
fn solution(src: &[i32], mut dst: Vec<i32>) -> Vec<i32> {
    dst.clear();
    dst.extend_from_slice(src);
    dst
}
  • 同長コピーの代替として、clear()extend_from_slice(src) で値を複製できる。

初級36: fillで一括埋め

問題

  • 長さNのベクタを値vで埋める。
  • 入力: n: usize, v: i32
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(n: usize, v: i32) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(3, 9), vec![9,9,9]);
    assert_eq!(solution(0, 1), vec![] as Vec<i32>);
    assert_eq!(solution(1, -5), vec![-5]);
    assert_eq!(solution(5, 0), vec![0,0,0,0,0]);
}
解答と解説
fn solution(n: usize, v: i32) -> Vec<i32> {
    let mut res = vec![0; n];
    res.fill(v);
    res
}
fn solution(n: usize, v: i32) -> Vec<i32> {
    vec![v; n]
}
  • 値の一括埋めはリテラル vec![v; n] が最も簡潔。

初級37: starts_with/ends_with(数列)

問題

  • 数列が特定の前置/後置と一致するかを判定。
  • 入力: input: &[i32], head: &[i32], tail: &[i32]
  • 出力: (bool, bool)

テンプレートとテスト

fn solution(input: &[i32], head: &[i32], tail: &[i32]) -> (bool, bool) {
    (input.starts_with(head), input.ends_with(tail))
}
fn solution(input: &[i32], head: &[i32], tail: &[i32]) -> (bool, bool) {
    let starts = input.len() >= head.len() && &input[..head.len()] == head;
    let ends = input.len() >= tail.len() && &input[input.len()-tail.len()..] == tail;
    (starts, ends)
}
  • starts_with/ends_with はスライス比較 == で手書き実装も可能。
    ::::

初級38: containsで要素存在確認

問題

  • スライスに特定の要素が含まれているかを確認する。
  • 入力: input: &[i32], x: i32
  • 出力: bool

テンプレートとテスト

fn solution(input: &[i32], x: i32) -> bool {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&[10,20,30], 20), true);
    assert_eq!(solution(&[10,20,30], 30), true);
    assert_eq!(solution(&[10,20,30], 40), false);
    assert_eq!(solution(&[], 10), false);
}
解答と解説
fn solution(input: &[i32], x: i32) -> bool {
    input.contains(&x)
}
fn solution(input: &[i32], x: i32) -> bool {
    input.iter().any(|&v| v == x)
}
  • 含有判定は iter().any(|&v| v == x) でも自然に書ける。

初級39: joinで文字列結合

問題

  • 文字列スライスを指定の区切り文字で結合する。
  • 入力: parts: &[&str], sep: &str
  • 出力: String

テンプレートとテスト

fn solution(parts: &[&str], sep: &str) -> String {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(&["a", "b", "c"], ", "), "a, b, c".to_string());
    assert_eq!(solution(&["one", "two", "three"], " "), "one two three".to_string());
    assert_eq!(solution(&["", "b", ""], ", "), ", b, ".to_string());
    assert_eq!(solution(&["a", "", "c"], ", "), "a, , c".to_string());
}
解答と解説
fn solution(parts: &[&str], sep: &str) -> String {
    parts.join(sep)
}
fn solution(parts: &[&str], sep: &str) -> String {
    parts.iter().copied().collect::<Vec<&str>>().join(sep)
}
  • 内部を一旦ベクタに集めてから join(sep) しても結果は同じ(itertools::Itertools::intersperse 等の手もある)。

初級40: 空白文字列のカウント

問題

  • 空白文字列の単語数を数える。
  • 入力: s: &str
  • 出力: usize

テンプレートとテスト

fn solution(s: &str) -> usize {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(""), 0);
    assert_eq!(solution(" "), 0);
    assert_eq!(solution("a b c"), 3);
    assert_eq!(solution("a  b  c"), 3);
    assert_eq!(solution("a   b   c"), 3);
}
解答と解説
fn solution(s: &str) -> usize {
    s.split_whitespace().count()
}
fn solution(s: &str) -> usize {
    s.split_whitespace().filter(|w| !w.is_empty()).count()
}
  • split_whitespace() は連続する空白を1つの区切りとして扱う。
  • 空文字列は自動的に除外される。

初級41: sortで昇順ソート

問題

  • 整数配列を昇順にソートする。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![3,1,4,1,5]), vec![1,1,3,4,5]);
    assert_eq!(solution(vec![5,4,3,2,1]), vec![1,2,3,4,5]);
    assert_eq!(solution(vec![]), vec![] as Vec<i32>);
    assert_eq!(solution(vec![42]), vec![42]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort();
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_by(|a, b| a.cmp(b));
    input
}
  • sort()sort_by(|a, b| a.cmp(b)) の省略形。
  • cmp() で自然順序(昇順)を指定できる。

初級42: sort_byでカスタムソート

問題

  • 整数配列を絶対値の昇順でソートする。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![3,-1,4,-1,5]), vec![-1,-1,3,4,5]);
    assert_eq!(solution(vec![-5,4,-3,2,-1]), vec![-1,2,-3,4,-5]);
    assert_eq!(solution(vec![]), vec![] as Vec<i32>);
    assert_eq!(solution(vec![0]), vec![0]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_by(|a, b| a.abs().cmp(&b.abs()));
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_by_key(|&x| x.abs());
    input
}
  • sort_by_key() はキー関数を指定してソートする簡潔な方法。
  • sort_by()sort_by_key() は用途に応じて使い分ける。

初級43: sort_by_keyで複合キーソート

問題

  • 文字列配列を長さの昇順、同じ長さなら辞書順でソートする。
  • 入力: input: Vec<String>
  • 出力: Vec<String>

テンプレートとテスト

fn solution(mut input: Vec<String>) -> Vec<String> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec!["abc".to_string(), "a".to_string(), "ab".to_string()]), 
               vec!["a".to_string(), "ab".to_string(), "abc".to_string()]);
    assert_eq!(solution(vec!["xyz".to_string(), "abc".to_string(), "def".to_string()]), 
               vec!["abc".to_string(), "def".to_string(), "xyz".to_string()]);
    assert_eq!(solution(vec![]), vec![] as Vec<String>);
}
解答と解説
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_by_key(|s| (s.len(), s.clone()));
    input
}
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_by(|a, b| {
        match a.len().cmp(&b.len()) {
            std::cmp::Ordering::Equal => a.cmp(b),
            other => other,
        }
    });
    input
}
  • タプル (len, string) で複合キーを指定できる。
  • タプルの比較は第1要素から順番に行われる。

初級44: sort_unstableで高速ソート

問題

  • 整数配列を降順にソートする(安定性は不要)。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![3,1,4,1,5]), vec![5,4,3,1,1]);
    assert_eq!(solution(vec![1,2,3,4,5]), vec![5,4,3,2,1]);
    assert_eq!(solution(vec![]), vec![] as Vec<i32>);
    assert_eq!(solution(vec![42]), vec![42]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_unstable_by(|a, b| b.cmp(a));
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_unstable();
    input.reverse();
    input
}
  • sort_unstable_by() は安定性を犠牲にして高速化したソート。
  • reverse() で昇順ソート結果を反転させる方法もある。

初級45: sort_unstable_by_keyでカスタム高速ソート

問題

  • 文字列配列を文字数の降順でソートする(安定性は不要)。
  • 入力: input: Vec<String>
  • 出力: Vec<String>

テンプレートとテスト

fn solution(mut input: Vec<String>) -> Vec<String> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec!["a".to_string(), "abc".to_string(), "ab".to_string()]), 
               vec!["abc".to_string(), "ab".to_string(), "a".to_string()]);
    assert_eq!(solution(vec!["hello".to_string(), "world".to_string(), "rust".to_string()]), 
               vec!["world".to_string(), "hello".to_string(), "rust".to_string()]);
    assert_eq!(solution(vec![]), vec![] as Vec<String>);
}
解答と解説
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_unstable_by_key(|s| std::cmp::Reverse(s.len()));
    input
}
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_unstable_by(|a, b| b.len().cmp(&a.len()));
    input
}
  • std::cmp::Reverse でキーをラップすると降順ソートになる。
  • sort_unstable_by() で直接比較関数を指定する方法もある。

初級46: 部分ソート(k個の最小要素)

問題

  • 配列の最初のk個の要素をソートし、残りはそのままにする。
  • 入力: input: Vec<i32>, k: usize
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>, k: usize) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![3,1,4,1,5], 3), vec![1,1,3,4,5]);
    assert_eq!(solution(vec![5,4,3,2,1], 2), vec![4,5,3,2,1]);
    assert_eq!(solution(vec![1,2,3], 5), vec![1,2,3]);
    assert_eq!(solution(vec![], 0), vec![] as Vec<i32>);
}
解答と解説
fn solution(mut input: Vec<i32>, k: usize) -> Vec<i32> {
    let actual_k = k.min(input.len());
    input[..actual_k].sort();
    input
}
fn solution(mut input: Vec<i32>, k: usize) -> Vec<i32> {
    if k >= input.len() {
        input.sort();
    } else {
        input[..k].sort();
    }
    input
}
  • スライス input[..k] で部分範囲を指定してソートできる。
  • min() で配列長を超えないよう安全に処理する。

初級47: 条件付きソート

問題

  • 偶数を先頭に、奇数を後ろに配置し、それぞれを昇順ソートする。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![3,1,4,1,5]), vec![4,1,1,3,5]);
    assert_eq!(solution(vec![2,4,6,1,3,5]), vec![2,4,6,1,3,5]);
    assert_eq!(solution(vec![1,3,5]), vec![1,3,5]);
    assert_eq!(solution(vec![2,4,6]), vec![2,4,6]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_by_key(|&x| (x % 2, x));
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_by(|a, b| {
        match (a % 2, b % 2) {
            (0, 0) => a.cmp(b),  // 両方偶数
            (1, 1) => a.cmp(b),  // 両方奇数
            (0, 1) => std::cmp::Ordering::Less,   // 偶数 < 奇数
            (1, 0) => std::cmp::Ordering::Greater, // 奇数 > 偶数
            _ => unreachable!(),
        }
    });
    input
}
  • (x % 2, x) で偶奇を第1キー、値自体を第2キーとしてソート。
  • カスタム比較関数で偶奇の優先順位を明示的に指定する方法もある。

初級48: 安定ソートの確認

問題

  • 文字列配列を長さの昇順でソートし、同じ長さの要素の相対順序を保持する。
  • 入力: input: Vec<String>
  • 出力: Vec<String>

テンプレートとテスト

fn solution(mut input: Vec<String>) -> Vec<String> {
    // ここに解答
    unimplemented!()
}

fn main(){
    let mut input = vec!["abc".to_string(), "def".to_string(), "a".to_string(), "b".to_string()];
    let result = solution(input);
    assert_eq!(result, vec!["a".to_string(), "b".to_string(), "abc".to_string(), "def".to_string()]);
    
    assert_eq!(solution(vec![]), vec![] as Vec<String>);
    assert_eq!(solution(vec!["hello".to_string()]), vec!["hello".to_string()]);
}
解答と解説
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_by_key(|s| s.len());
    input
}
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_by(|a, b| a.len().cmp(&b.len()));
    input
}
  • sort_by_key()sort_by() は安定ソートを保証する。
  • 同じキー値を持つ要素の相対順序が保持される。

初級49: 逆順ソート

問題

  • 整数配列を降順にソートする。
  • 入力: input: Vec<i32>
  • 出力: Vec<i32>

テンプレートとテスト

fn solution(mut input: Vec<i32>) -> Vec<i32> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec![3,1,4,1,5]), vec![5,4,3,1,1]);
    assert_eq!(solution(vec![1,2,3,4,5]), vec![5,4,3,2,1]);
    assert_eq!(solution(vec![]), vec![] as Vec<i32>);
    assert_eq!(solution(vec![42]), vec![42]);
}
解答と解説
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort_by(|a, b| b.cmp(a));
    input
}
fn solution(mut input: Vec<i32>) -> Vec<i32> {
    input.sort();
    input.reverse();
    input
}
  • b.cmp(a) で比較順序を逆転させて降順ソート。
  • sort() + reverse() の組み合わせでも同じ結果が得られる。

初級50: 複数条件ソート

問題

  • 文字列配列を長さの昇順でソートし、同じ長さなら辞書順の降順でソートする。
  • 入力: input: Vec<String>
  • 出力: Vec<String>

テンプレートとテスト

fn solution(mut input: Vec<String>) -> Vec<String> {
    // ここに解答
    unimplemented!()
}

fn main(){
    assert_eq!(solution(vec!["abc".to_string(), "def".to_string(), "a".to_string(), "b".to_string()]), 
               vec!["b".to_string(), "a".to_string(), "def".to_string(), "abc".to_string()]);
    assert_eq!(solution(vec!["hello".to_string(), "world".to_string(), "rust".to_string()]), 
               vec!["rust".to_string(), "world".to_string(), "hello".to_string()]);
    assert_eq!(solution(vec![]), vec![] as Vec<String>);
}
解答と解説
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_by_key(|s| (s.len(), std::cmp::Reverse(s.clone())));
    input
}
fn solution(mut input: Vec<String>) -> Vec<String> {
    input.sort_by(|a, b| {
        match a.len().cmp(&b.len()) {
            std::cmp::Ordering::Equal => b.cmp(a), // 同じ長さなら辞書順降順
            other => other,
        }
    });
    input
}
  • (len, Reverse(string)) で長さ昇順、文字列降順の複合ソート。
  • カスタム比較関数で複数条件を明示的に指定する方法もある。

Discussion