6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaAdvent Calendar 2024

Day 20

Javaで学ぶアルゴリズム -バケットソート

Posted at

バケットソート

バケットソート(ビンソート [Bin Sort] とも呼ばれる)はあらかじめデータがとりうる値すべての容器(バケット)を順番どおりに並べて用意しておき、値を対応する容器に移すことでソートを行う整列アルゴリズムです。

データがとりうる値がわかっていなければソートのためのバケット [Bucket] を準備することができないのでこのアルゴリズムは使えません。比較を用いない整列アルゴリズムです。

実行環境

  • JUnit 5
  • Java 8

テストコード

BucketSortTest.java
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class BucketSortTest {

	@DisplayName("配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchange() {
		int[] array = { 5, 12, 7, 0, 1, 14, 6, 9, 8, 3, 2, 11, 4, 13, 10 };
		int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("要素が1つしかない配列を渡すと要素が1つしかない配列が返ること")
	@Test
	void testStraightExchangeOneElement() {
		int[] array = { 5 };
		int[] expected = { 5 };

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("空の配列を渡すと空の配列が返ること")
	@Test
	void testStraightExchangeEmpty() {
		int[] array = {};
		int[] expected = {};

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("すでに昇順でソートされた配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchangeAlreadySorted() {
		int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
		int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}

	@DisplayName("要素が重複した配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchangeDuplicateElement() {
		int[] array = { 5, 3, 5, 2, 1, 5, 3, 2, 5, 3, 5 };
		int[] expected = { 1, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5 };

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}
}

バケットソートアルゴリズム

BucketSort.java
import java.util.Arrays;

public class BucketSort {

	/**
	 * バケットソートだが、実装自体は度数ソートに近い
	 * @param array
	 * @return sortedArray
	 */
	public static int[] bucketSort(int[] array) {
		int[] sortedArray = array.clone();
		if (sortedArray.length == 0) {
			return sortedArray;
		}

		// 配列の最大値と最小値を見つける
		int maxValue = sortedArray[0];
		int minValue = sortedArray[0];
		for (int num : sortedArray) {
			if (num > maxValue) {
				maxValue = num;
			}
			if (num < minValue) {
				minValue = num;
			}
		}

		// バケットの数を決定
		int[][] buckets = new int[maxValue - minValue + 1][0];

		// 各要素を対応するバケットに追加
		for (int num : sortedArray) {
			int bucketIndex = num - minValue;
			buckets[bucketIndex] = appendToArray(buckets[bucketIndex], num);
		}

		// デバッグ用
        printForDebug(buckets);

		// 各バケットを順に結合
		int index = 0;
		for (int[] bucket : buckets) {
			Arrays.sort(bucket); // 各バケット内をソート
			for (int num : bucket) {
				sortedArray[index++] = num; // ソートされているバケットを元の配列に追加
			}
		}
		return sortedArray;
	}

	/**
	 * 配列に要素を追加する
	 * @param array
	 * @param value
	 * @return array
	 */
	private static int[] appendToArray(int[] array, int value) {
		array = Arrays.copyOf(array, array.length + 1);
		array[array.length - 1] = value;
		return array;
	}

	/**
	 * デバッグ用: バケットに格納された後の状況を出力
	 * @param buckets
	 */
	private static void printForDebug(int[][] buckets) {
		System.out.println("Buckets after element insertion:");
		for (int i = 0; i < buckets.length; i++) {
			System.out.println("Bucket " + i + ": " + Arrays.toString(buckets[i]));
		}
	}
}

デバッグ出力

テスト1
	@DisplayName("配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchange() {
		int[] array = { 5, 12, 7, 0, 1, 14, 6, 9, 8, 3, 2, 11, 4, 13, 10 };
		int[] expected = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}
出力結果
Buckets after element insertion:
Bucket 0: [0]
Bucket 1: [1]
Bucket 2: [2]
Bucket 3: [3]
Bucket 4: [4]
Bucket 5: [5]
Bucket 6: [6]
Bucket 7: [7]
Bucket 8: [8]
Bucket 9: [9]
Bucket 10: [10]
Bucket 11: [11]
Bucket 12: [12]
Bucket 13: [13]
Bucket 14: [14]
テスト2
	@DisplayName("要素が重複した配列を渡すとソートされた配列が返ること")
	@Test
	void testStraightExchangeDuplicateElement() {
		int[] array = { 5, 3, 5, 2, 1, 5, 3, 2, 5, 3, 5 };
		int[] expected = { 1, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5 };

		int[] actual = BucketSort.bucketSort(array);
		assertThat(actual, is(expected));
	}
出力結果
Buckets after element insertion:
Bucket 0: [1]
Bucket 1: [2, 2]
Bucket 2: [3, 3, 3]
Bucket 3: []
Bucket 4: [5, 5, 5, 5, 5]
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?