영권's

정렬 1 (선택 정렬, 버블 정렬) 본문

컴퓨터 공학/자료구조

정렬 1 (선택 정렬, 버블 정렬)

ykkkk 2020. 10. 28. 01:57

정렬이란?

주어진 자료들을 어떤 기준에 의하여 크기 순서대로 나열한 것

• 정렬 알고리즘의 핵심 요소는 교환, 선택, 삽입이며 대부분의 정렬 알고리즘은 이 세가지 요소를 응용한 것입니다.

데이터를 정렬해야 하는 이유는 탐색효율적으로 하기 위해서 입니다.

 

선택 정렬 

선택 정렬 : 잘못된 위치에 들어가 있는 원소를 찾아 그것을 올바른 위치에 집어넣는 원소 교환으로 정렬을 하는 방법이다.

그래서 먼저 작은 원소를 찾아 첫 번째 위치에 있는 원소와 교환하고 다음에는 두 번째로 작은 원소를 찾아 두 번째 위치에 있는 원소와 교환한다. 이렇게 매번 나머지(a[i] ... a[n-1]) 원소 중에서 가장 작은 원소를 찾아 a[i] 원소와 교환하는 작업을 모든 원소가 올바른 위치에 있게 될 때까지 계속하면 원하는 오름차순 정렬이 된다.

 

단순 선택 정렬 과정

package chap06;
import java.util.Scanner;
// 단순 선택 정렬

class SelectionSort {
	// 배열 요소 a[idx1]과 a[idx2]의 값을 바꿉니다.
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1]; a[idx1] = a[idx2]; a[idx2] = t;
	}

	// 단순 선택 정렬
	static void selectionSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++) {
			int min = i;			// 아직 정렬되지 않은 부분에서 가장 작은 요소의 인덱스를 기록합니다.
			for (int j = i + 1; j < n; j++)
				if (a[j] < a[min])
					min = j;
			swap(a, i, min);		// 아직 정렬되지 않은 부분의 첫 요소와 가장 작은 요소를 교환합니다.
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("단순 선택 정렬");
		System.out.print("요솟수:");
		int nx = stdIn.nextInt();
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.print("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		selectionSort(x, nx);			// 배열x를 단순선택정렬

		System.out.println("오름차순으로 정렬했습니다.");
		for (int i = 0; i < nx; i++)
			System.out.println("x[" + i + "]=" + x[i]);
	}
}

 

버블 정렬

버블 정렬은 배열을 검사하여 두 인접한 원소가 정렬 순서에 맞지 않으면 이들을 서로 교환하고 이것을 반복한다.

 

즉, 먼저 a[0]과 a[1]을 비교하여 정렬 순서에 맞지 않으면 서로 교환한다.

다음 a[1]과 a[2]를 다시 비교하여 순서에 맞지 않으면 교한하는데 이 과정을 a[n-2]와 a[n-1]이 될 때까지 반복한다.

이렇게 하면 가장 큰 원소가 배열의 n-1위치로 끌어 오르게(bubbled up) 된다.

그러나 이것은 배열 전체에 대한 첫 번째 패스이며 이 정렬은 다시 배열 처음부터 인접한 원소를 비교하여 순서가 맞지 않으면 교환하면서 검사하게 되는데 이 때는 마지막 비교가 a[n-3]과 a[n-2]가 된다.

왜냐하면 제일 큰 원소는 이미 위치 n-1에 선정되어 있기 때문이다.

이 과정은 마지막 패스로 a[0]과 a[1]을 비교하여 순서가 맞지 않으면 서로 교환할 때까지 계속한다.

 

버블 정렬

 

버블 정렬 코드

package chap06;
import java.util.Scanner;
// 버블 정렬(버전 1)

class BubbleSort {
	// a[idx1]와 a[idx2]의 값을 바꿉니다. 
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1]; 
		a[idx1] = a[idx2]; 
		a[idx2] = t;
	}

	// 버블 정렬
	static void bubbleSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++)
			for (int j = n - 1; j > i; j--)
				if (a[j - 1] > a[j])
					swap(a, j - 1, j);
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("버블 정렬(버전 1)");
		System.out.print("요솟수:");
		int nx = stdIn.nextInt();
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.print("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);				// 배열 x를 버블 정렬합니다.

		System.out.println("오름차순으로 정렬했습니다.");
		for (int i = 0; i < nx; i++)
			System.out.println("x[" + i + "]=" + x[i]);
	}
}

버블 정렬의 시간복잡도

비교 횟수

  • 최상, 평균, 최악 모두 일정
  • n-1, n-2, ... 2, 1 번 = n(n-1)/2

교환 횟수

  • 입력 자료가 역순으로 정렬 되어 있는 최악의 경우, 한 번 교환하기 위하여 3번의 이동(SWAP 함수의 작업)이 필요하므로 (비교 횟수 *3 )번 = 3n(n-1) /2
  • 입력 자료가 이미 정렬 되어 있는 최상의 경우, 자료의 이동이 발생하지 않는다.

T(n_ = O(n^2)

 

 

알고리즘 개선(1)

 

사진에서 세번째 패스가 마치고 나면 4가 3번째 자리에 위치합니다.

 

사진은 네번째 패스로 여기서는 요소의 교환이 한번도 이루어 지지 않습니다. 왜냐하면 이미 세 번째 패스에서 정렬이 끝났기 때문입니다.

 

즉, 어떤 패스에서 요소의 교환 횟수가 0이면 더 이상 정렬할 요소가 없다는 뜻이기 때문에 정렬 작업을 멈추면 됩니다.

 

만약 처음부터 정렬이 되어있는 배열이라고 가정하면 첫번째 패스에서 교환이 수행되지 않으므로 첫 번째 패스를 마친 다음에 정렬 작업을 마치게 됩니다.

 

다음 코드는 이런 '멈춤'으로 개선한 버블 정렬 코드 입니다.

package chap06;
import java.util.Scanner;
// 버블 정렬(버전 2)

class BubbleSort2 {
	// 배열 요소 a[idx1]과 a[idx2]의 값을 바꿉니다.
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1]; a[idx1] = a[idx2]; a[idx2] = t;
	}

	// 버블 정렬(버전 2)
	static void bubbleSort(int[] a, int n) {
		for (int i = 0; i < n - 1; i++) {
			int exchg = 0;							// 패스의 교환 횟수를 기록합니다.
			for (int j = n - 1; j > i; j--)
				if (a[j - 1] > a[j]) {
					swap(a, j - 1, j);
					exchg++;
				}
			if (exchg == 0) break;					// 교환이 이루어지지 않으면 종료합니다.
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("버블 정렬(버전 2)");
		System.out.print("요솟수:");
		int nx = stdIn.nextInt();
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.print("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);				// 배열 x를 단순교환정렬

		System.out.println("오름차순으로 정렬했습니다.");
		for (int i = 0; i < nx; i++)
			System.out.println("x[" + i + "]=" + x[i]);
	}
}

 

알고리즘 개선(2)

 

 

다음 사진은 버블 정렬의 첫 번째 패스의 비교, 교환 과정을 나타낸 것입니다.

이렇게 각각의 패스에서 비교, 교환을 하다가 어떤 시점 이후에 교환이 수행되지 않는다면 그보다 앞쪽의 요소는이미 정렬을 마친 상태입니다. 따라서 두 번째 패스는 첫 요소를 제외한 6개의 요소가 아니라 4개의 요소에 대해서 비교, 교환을 수행하면 됩니다.

 

버블 정렬의 두 번째 패스

 

앞의 내용을 바탕으로 개선한 코드입니다.

package chap06;
import java.util.Scanner;
// 버블 정렬(버전 3)

class BubbleSort3 {
	// 배열 요소 a[idx1]과 a[idx2]의 값을 바꿉니다.
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1]; a[idx1] = a[idx2]; a[idx2] = t;
	}

	// 버블 정렬(버전 3)
	static void bubbleSort(int[] a, int n) {
		int k = 0;						// a[k]보다 앞쪽은 정렬을 마친 상태
		while (k < n - 1) {
			int last = n - 1;			// 마지막으로 요소를 교환한 위치
			for (int j = n - 1; j > k; j--)
				if (a[j - 1] > a[j]) {
					swap(a, j - 1, j);
					last = j;
				}
			k = last;
		}
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("버블 정렬(버전 3)");
		System.out.print("요솟수:");
		int nx = stdIn.nextInt();
		int[] x = new int[nx];

		for (int i = 0; i < nx; i++) {
			System.out.print("x[" + i + "]:");
			x[i] = stdIn.nextInt();
		}

		bubbleSort(x, nx);		// 배열 x를 단순교환정렬

		System.out.println("오름차순으로 정렬했습니다.");
		for (int i = 0; i < nx; i++)
			System.out.println("x[" + i + "]=" + x[i]);
	}
}

 

정렬 알고리즘 시간복잡도 비교

 

  • 단순(구현 간단) 하지만 비효율적인 방법
    • 삽입 정렬, 선택 정렬, 버블 정렬
  • 복잡하지만 효율적인 방법
    • 퀵 정렬, 힙 정렬, 합병 정렬, 기수 정렬

 

 

참고 

자료구조와 C(도서)

Do It 자료구조와 함께 배우는 알고리즘 입문(자바 편)

 

gmlwjd9405.github.io/2018/05/06/algorithm-bubble-sort.html

 

'컴퓨터 공학 > 자료구조' 카테고리의 다른 글

정렬2 (단순 삽입 정렬, 병합 정렬)  (0) 2020.10.29
그래프  (0) 2020.10.21
트리(Tree)  (0) 2020.10.17
스택(Stack) , 큐(Queue)  (0) 2020.10.15
선형검색 , 이진 검색 , 해시법  (0) 2020.10.13
Comments