CS/Algorithm

정렬(버블정렬, 삽입정렬, 퀵정렬, 병합정렬)

창문닦이 2019. 1. 22. 19:56

JUnit

@Test 어노테이션을 통해서 단위테스트 진행 (이클립스에 있는 JUnit 라이브러리 이용. JUnit은 자바용 단위 테스트 툴.)

경로 : 프로젝트 > properties > Libraries > Add Library > JUnit

Run as > JUnit Test 로 테스트 결과 조회.

 

import static org.junit.Assert.*;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

//유닛테스트 활용
public class Test1 {
	
	//1. int형 배열 정렬
	@Test
	public void sortInts(){
		final int[] numbers = {-3,-5,1,7,4,-2};
		final int[] expected1 = {-5,-3,-2,1,4,7};
		
		Arrays.sort(numbers);
		assertArrayEquals(expected1,numbers);
	}

	//2. String형 배열 정렬
	@Test
	public void sortObjects(){
		final String[] strings = {"z", "x", "y", "abc", "zzz", "zazzy"};
		final String[] expected2 = {"abc", "x", "y", "z", "zazzy", "zzz"};
		
		Arrays.sort(strings);
		assertArrayEquals(expected2,strings);
	}	
    
    //3. Comparable 인터페이스 사용하지 않고 정렬하기
    @Test
    public void sortNotComparable() {
        final List<NotComparable> objects = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            objects.add(new NotComparable(i));
        }

        try {
            Arrays.sort(objects.toArray());
        } catch (Exception e) { 
            return;
        }

        fail();
    }
	
	//3-1. 이너클래스 생성
    private static class NotComparable {
        private int i;
        private NotComparable(final int i) {//생성자로 변수 초기화
            this.i = i;
        }
    }
	
	//4. 사용자가 지정한 순서로 정렬하기
	@Test
	public void customSort(){
		final List<Integer> numbers = Arrays.asList(4,7,1,6,3,5,4);
		final List<Integer> expected3 = Arrays.asList(7,6,5,4,4,3,1);
		
		Collections.sort(numbers,new ReverseNumericalOrder());
		assertEquals(expected3,numbers);
	}	
	
}
//4-1. 사용자가 지정한 순서로 정렬하기(역순)
class ReverseNumericalOrder implements Comparator<Integer>{
	@Override
	public int compare(Integer arg0, Integer arg1) {
		return arg1 - arg0;
	}
}

 


버블정렬

배열에서 현재데이터(array[i])와 다음데이터(array[i+1]) 값의 크기를 계속 비교함. 더 이상 자리 교체가 없을때까지 반복한다.

소스코드로 구현하는 것은 간단하나 엄청나게 비효율적이다.

O(n²) : 역순으로 정렬된 데이터를 버블정렬할 경우의 성능. 루프를 돌때마다 하나의 값만 변경되기 때문이다

O(n) : 이미 정렬된 데이터를 버블정렬할 경우의 성능

 

package com.sort;

import org.junit.Test;

import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;

public class BubbleSort {

public void bubbleSort(int[] numbers) {
    boolean numbersSwitched;
    do {
        numbersSwitched = false;
        for (int i = 0; i < numbers.length - 1; i++) {
            if (numbers[i + 1] < numbers[i]) {
                int tmp = numbers[i + 1];
                numbers[i + 1] = numbers[i];
                numbers[i] = tmp;
                numbersSwitched = true;
            }
        }
    } while (numbersSwitched);
}

    @Test
    public void testBubble() {
        final int[] numbers = {6, 4, 9, 5};
        final int[] expected = {4, 5, 6, 9};

        bubbleSort(numbers);
        assertArrayEquals(expected, numbers);
    }
}

 

 


삽입정렬

정렬된 데이터를 새로운 리스트에 담아서 반환한다.(새로운 리스트를 생성해서 반환)

이때, LinkedList로 반환. 링크드리스트는 리스트의 중간에 데이터를 추가하는 데 효율적. 포인터를 재배열하는 작업이 간단하기 때문이다.

- 버블정렬보다 좋은점 : 정렬이 끝나면 즉시 반환 가능. 버블정렬은 순서대로 리스트를 확인하느라 불필요한 연산을 진행한다

O(n²) : 이미 정렬된 리스트를 다시 삽입정렬한 경우의 성능. 매번 원소를 삽입할때마다 새 리스트의 끝까지 반복문을 실행한다.

O(n) : 역순으로 정렬된 리스트를 삽입정렬 한 경우의 성능. 해당 리스트앞에 있는 원소를 새리스트에 넣는다.

 

 

package com.sort;

import org.junit.Test;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import static junit.framework.Assert.assertEquals;

public class InsertSort {

public static List<Integer> insertSort(final List<Integer> numbers) {
    final List<Integer> sortedList = new LinkedList<>();

    originalList: for (Integer number : numbers) {
        for (int i = 0; i < sortedList.size(); i++) {
            if (number < sortedList.get(i)) {
                sortedList.add(i, number);
                continue originalList;
            }
        }
        sortedList.add(sortedList.size(), number);
    }

    return sortedList;
}

    @Test
    public void insertSort() {
        final List<Integer> numbers = Arrays.asList(6, 4, 9, 5);
        final List<Integer> expected = Arrays.asList(4, 5, 6, 9);

        final List<Integer> actual = insertSort(numbers);
        assertEquals(expected, actual);
    }
}

 


퀵 정렬

재귀적. 임의의 숫자(pivot)을 선정하여  pivot >=(크거나 같다), <(작다)로 두 가지로 분류한다

그리고 두 가지로 분류된 각 리스트는 다시 스스로를 정렬하는 메소드를 호출한다.

버블정렬, 삽입정렬보다 훨씬 효율적인 성능을 가진다. 데이터를 pivot을 기준으로 2개의 리스트로 분류하는 시간 복잡도는 O(n)이다.

각각의  재귀호출은 각 리스트 숫자의 절반만큼 발생한다. 따라서 퀵정렬 알고리즘의 평균적 성능은 O(n log n)이다.

 

package com.sort;


import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static junit.framework.Assert.assertEquals;

public class Quicksort {

public static List<Integer> quicksort(List<Integer> numbers) {
    if (numbers.size() < 2) {
        return numbers;
    }

    final Integer pivot = numbers.get(0);
    final List<Integer> lower = new ArrayList<>();
    final List<Integer> higher = new ArrayList<>();

    for (int i = 1; i < numbers.size(); i++) {
        if (numbers.get(i) < pivot) {
            lower.add(numbers.get(i));
        } else {
            higher.add(numbers.get(i));
        }
    }

    final List<Integer> sorted = quicksort(lower);

    sorted.add(pivot);
    sorted.addAll(quicksort(higher));

    return sorted;
}

    @Test
    public void testQuicksort() {
        final List<Integer> numbers = Arrays.asList(6, 4, 9, 5);
        final List<Integer> expected = Arrays.asList(4, 5, 6, 9);

        final List<Integer> actual = quicksort(numbers);
        assertEquals(expected, actual);
    }
}

병합정렬

분할정복 알고리즘(divide and conquer). 리스트를 두개로 나누고 각 하위 리스트를 정렬한 후 둘을 하나로 합친다.

 

package com.sort;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static junit.framework.Assert.assertEquals;

public class Mergesort {

    public static List<Integer> mergesort(final List<Integer> values) {
        if (values.size() < 2) {
            return values;
        }

        final List<Integer> leftHalf =
                values.subList(0, values.size() / 2);
        final List<Integer> rightHalf =
                values.subList(values.size() / 2, values.size());

        return merge(mergesort(leftHalf), mergesort(rightHalf));
    }

    private static List<Integer> merge(final List<Integer> left,
                                       final List<Integer> right) {
        int leftPtr = 0;
        int rightPtr = 0;

        final List<Integer> merged =
                new ArrayList<>(left.size() + right.size());

        while (leftPtr < left.size() && rightPtr < right.size()) {
            if (left.get(leftPtr) < right.get(rightPtr)) {
                merged.add(left.get(leftPtr));
                leftPtr++;
            } else {
                merged.add(right.get(rightPtr));
                rightPtr++;
            }
        }

        while (leftPtr < left.size()) {
            merged.add(left.get(leftPtr));
            leftPtr++;
        }

        while (rightPtr < right.size()) {
            merged.add(right.get(rightPtr));
            rightPtr++;
        }

        return merged;
    }

    @Test
    public void testMergesort() {
        final List<Integer> numbers = Arrays.asList(6, 4, 9, 5);
        final List<Integer> expected = Arrays.asList(4, 5, 6, 9);

        final List<Integer> actual = mergesort(numbers);
        assertEquals(expected, actual);
    }
}

 

'CS > Algorithm' 카테고리의 다른 글

[알고리즘] 하노이의 탑  (0) 2020.11.01
빅오표기법  (0) 2019.01.21