특정 원소들을 번호순이나 사전 순서와 같이 일정한 순서대로 열거하는 알고리즘
private static void bubbleSort(int[] nums){ // 3 4 1 5 2
int size = nums.length;
for(int i=0; i<size-1; i++){ //Outer 루프: 전체 요소를 반복
for(int j=0; j<size-i-1; j++){ //Inner 루프: 그 안에서 정렬을 해야하는 요소를 반복
if(nums[j]>nums[j+1]){//두 개의 요소 비교를 통해 왼쪽에 있는 값이 더 큰지 확인하고 더 크면 스왑
swap(nums, j, j+1);
}
}
}
}
private static void selectionSort(int[] nums){// 3 4 1 5 2
int size = nums.length;
for(int i=0; i<size-1; i++){//전체 범위 반복
int minIdx = i; //최솟값을 담을 인덱스 생성
for (int j=i+1; j<size; j++){
if(nums[j]<nums[minIdx]){ //반복과정에서 최솟값을 찾음
minIdx = j;
}
}
swap(nums, i, minIdx); //찾은 최솟값을 왼쪽으로 교환
}
}
private static void insertSort(int[] nums){
int size = nums.length;
for(int i=1; i<size; i++){ //전체 범위 반복
System.out.println((i) + "번째 정렬 시작");
int current = nums[i]; //아직 정렬되지 않은 배열에서 삽입될 i번째 숫자를 현재 변수에 저장
int tobeInsertedIdx = i-1; //정렬된 부분에서 삽입할 위치 인덱스도 저장
while(tobeInsertedIdx>=0 && nums[tobeInsertedIdx] > current){ //정렬된 부분에서 i번째 숫자가 삽입될 위치를 탐색
nums[tobeInsertedIdx+1] = nums[tobeInsertedIdx];
tobeInsertedIdx--; //삽입할 위치 인덱스 1 감소
}
nums[tobeInsertedIdx+1] = current;
System.out.println((i) + "번째 정렬 종료: "+ Arrays.toString(nums));
}
}
public static void mergeSort(int[] nums, int first, int last){
if(first < last){ //분할된 요소가 하나 이상인지 확인
//중간 값
int mid = (first + last) / 2;
//분할
mergeSort(nums, first, mid);
mergeSort(nums, mid+1, last);
//병합
mergeTwoArea(nums, first, mid, last);
}
}
private static void mergeTwoArea(int[] nums, int first, int mid, int last){
int leftIdx = first; //첫번째 영역에서의 첫번째 인덱스
int rightIdx = mid +1; //두번째 영역에서의 첫번째 인덱스
int[] sortedArray = new int[last+1]; //병합한 결과를 담을 배열
int sortIdx = first; //병합 한 결과를 담을 배열의 인덱스
//병합할 두 영역의 데이터들을 비교
while(leftIdx<=mid && rightIdx <= last){
if(nums[leftIdx]<=nums[rightIdx]){//첫번째 영역의 값이 더 작은 경우
sortedArray[sortIdx] = nums[leftIdx];
leftIdx++;
}else{//두번째 영역의 값이 더 작은 경우
sortedArray[sortIdx] = nums[rightIdx];
rightIdx++;
}
sortIdx++;
}
//비교 후 두 영역 중 남은 숫자 대입
if(leftIdx > mid){ //배열의 앞부분이 모두 sortedArray에 옮겨졌다면
for(int i = rightIdx; i<=last; i++){
sortedArray[sortIdx] = nums[i];
sortIdx++;
}
}else{ //배열의 뒷부분이 모두 sortedArray에 옮겨졌다면
for(int i=leftIdx; i<=mid; i++){
sortedArray[sortIdx] = nums[i];
sortIdx++;
}
}
//정렬된 결과를 담은 배열에서 원래 배열로 복사
for(int i =first; i<=last; i++){
nums[i] = sortedArray[i];
}
}
먼저 pivot을 설정함(pivot을 설정하는 기준은 다양함)
pivot을 기준으로 왼쪽엔 작은 값, 오른쪽엔 큰 값이 정렬되게 한다.
정렬이되면 각각 왼쪽과 오른쪽을 분할하여 각각 부분에서 다시 퀵 정렬을 사용하게 됨
private static void quickSort(int[] nums, int first, int last){
if(first<last){
int pivot = partition(nums, first, last);
quickSort(nums, first, pivot-1); //왼쪽 영역 정렬
quickSort(nums, pivot+1, last); //오른쪽 영역 정렬
}
}
private static int partition(int[] nums, int first, int last){
int pivot = nums[first]; //피벗의 위치 (가장 왼쪽으로 설정)
int low = first + 1;
int high = last;
//교차되지 않을 때까지 반복
while(low<=high){
//피벗보다 큰 값을 찾는 과정
while(pivot > nums[low] && low <= last){
low++;
}
//피벗보다 작은 값을 찾는 과정
while (pivot < nums[high] && high >= (first + 1)) {
high--;
}
//교차되지 않은 상태라면 swap
if(low <= high){
swap(nums, low, high);
}
}
swap(nums, first, high); //피벗과 high가 가리키는 대상 swap
return high; //옮겨진 피벗의 위치정보 반환 (정렬이 완료된 인덱스 반환)
}
어떤 정렬알고리즘을 써야하는가?
→ 시간복잡도가 전부는 아님! 알고리즘별로 각각 장단점이 있으므로 상황에 맞게 선택!
1. 안정성
- O : 버블, 삽입, 병합
- X : 선택, 퀵, 힙
2. 공간복잡도: 메모리 사용이 중요할 때
- 1 :버블, 삽입, 선택, 힙
- n : 병합
3. 지역성(Locality)
4. 키 값들의 분포 상태 등..