読者です 読者をやめる 読者になる 読者になる

ここ数年、Cでクイックソートを書いていないことに気がついたので、思い出しつつ書いてみることに。

#include <stdio.h>

#define SWAP(a, b) { \
  int tmp; \
  tmp = a; a = b; b = tmp; \
}

static int avg(int elms[], int left, int right)
{
  int i, sum = 0;

  for (i = left; i <= right; i++)
    sum += elms[i];

  return (sum / (right - left + 1));
}

static void print_list(int elms[], int left, int right)
{
  int i;
  for (i = left; i <= right; i++)
    printf("%d ", elms[i]);

  printf("\n");
}

static void qs(int elms[], int left, int right)
{
  int l, r;
  int pivot = avg(elms, left, right);

  for (l = left, r = right; l < r; l++, r--) {
    for (; elms[l] < pivot; l++);
    for (; elms[r] > pivot; r--);

    if (r < l) break;

    SWAP(elms[l], elms[r]);
  }

  if (left < l - 1)
    qs(elms, left, l - 1);

  if (l < right)
    qs(elms, l, right);
}

int main(void)
{
  int len;
  int elms[] = { 7, 2, 5, 9, 6, 1, 3, 8, 4 };

  len = sizeof(elms) / sizeof(elms[0]);

  qs(elms, 0, len - 1);

  print_list(elms, 0, len - 1);

  return 0;
}

う〜ん、書き方がまずいのかコードを見ても意図が伝わりにくい気がするなぁ

OCamlだと

let qs l =
  let rec part pivot left right = function
    | [] -> (left, right)
    | hd :: tl when hd < pivot ->
        part pivot (hd :: left) right tl
    | hd :: tl ->
        part pivot left (hd :: right) tl
  in
  let rec sort = function
    | [] -> []
    | hd :: tl ->
        let left, right = part hd [] [] tl in
        sort left @ hd :: sort right
  in
  sort l

let _ =
  let elms = [7; 2; 5; 9; 6; 1; 3; 8; 4] in
  List.iter
    (fun x -> Printf.printf "%d " x) (qs elms);
  print_newline ()

と末尾再帰になっていないが、こちらのほうが意図がわかりやすくて嬉しい.
使用メモリや計算量は多いけど…