[C#] Lec 13 - 정렬
[C#] Lec 13 - 정렬
기초 자료 구조
배열의 기본 연산
- 대부분의 자료 구조는 네 가지 기본 동작 원래 가지며 이를 연산이라고 함
- 읽기 : 자료 구조 내 특정 위치를 찾는 것
- 검색 : 자료 구조 내 특정 값을 찾는 것
- 삽입 : 자료 구조 내 새로운 값을 추가하는 것
- 삭제 : 자료 구조 내에 특정 값을 제거하는 것
- 읽기 : Array[n] 접근하려면, (총 한 단계)
- 배열의 인덱스는 0부터 시작. Array[0]의 메모리 주소 get
- Array[0]의 메모리주소 + n 으로 이동.
- 검색 : “melon” 접근하려면, (최대 n단계)
- Array[0] 인덱스로 검색 → “apple”이므로 다음
- [1], [2], [3] 이동하며 확인
- 삽입 : “potato” 삽입하기 위해 (최대 n+1단계 필요)
- 추가 메모리 1개 할당
- 삽입할 index 뒤의 값들을 한칸씩 뒤로 옮김
- “potato”를 그 자리에 삽입
- 삭제 : “tomato” 삭제 하기 위해 (최대 n단계)
- “tomato”를 메모리에서 삭제
- 빈 공간 뒤쪽 값들을 한칸씩 앞으로 옮김
- 마지막에 빈 공간 할당 해제
정렬된 배열에서의 과정
- 검색 : 선형 검색 → 최대 N단계 / 이진 검색 → 최대 log_2 N 단계
- 삽입 : N단계
빅 오 표기법
- O(1) : 데이터가 아무리 많아도 단계 수가 상수로 유지
- O(N) 보다 O(1)알고리즘의 연산이 더 많을 수는 있으나, 특정 데이터 수부터 반드시 O(1)이 효율적인 점이 존재
- 선형검색 : O(N), but 최선의 시나리오 - O(1), 최악의 시나리오 : O(N)
- 최악의 시나리오에 알고리즘이 얼마나 비효율적인지 아는것이 중요
- O(log N) : 이진 검색 알고리즘.
정렬
버블, 선택, 삽입 정렬
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using System;
namespace ConsoleApp2
{
class Program
{
public static void Main(string[] args)
{
SortableArray a = new SortableArray(new int[] { 9, 32, 4, 6, 7, 1, 5 });
Console.WriteLine(a);
a.BubbleSort();
Console.WriteLine(a);
}
}
public class SortableArray
{
private int[] array;
public SortableArray(int a )
{
array = new int[a];
}
public int this[int index]
{
get { return array[index]; }
set
{
if(index > array.Length-1)
{
throw new ArgumentOutOfRangeException();
}
array[index] = value;
}
}
public int Length
{
get { return array.Length; }
}
public SortableArray (SortableArray arr)
{
array = new int[arr.Length];
for (int i=0;i<arr.Length;i++)
{
array[i] = arr[i];
}
}
public SortableArray(int[] arr)
{
array = new int[arr.Length];
for (int i = 0;i<arr.Length;i++)
{
array[i] = arr[i];
}
}
public override string ToString()
{
string s = "";
foreach (var i in array)
{
s += i;
s += " ";
}
return s;
}
public void BubbleSort()
{
for (int i = 0;i<this.Length-1;i++ )
{
for (int j = 0; j<this.Length-(i+1);j++)
{
if (array[j]>array[j+1])
{
int tmp = array[j + 1];
array[j + 1] = array[j];
array[j] = tmp;
}
}
}
}
public void SelectionSort()
{
for (int i = 0;i<this.Length;i++)
{
int lowestNumIndex = i;
for (int j = i+1; j<this.Length;j++)
{
if (array[j] < array[lowestNumIndex])
{
lowestNumIndex = j;
}
}
if (lowestNumIndex!= i)
{
int temp = array[i];
array[i] = array[lowestNumIndex];
array[lowestNumIndex] = temp;
}
}
}
public void InsertionSort()
{
for (int i = 1; i<array.Length;i++)
{
int position = i;
int tempValue = array[i];
while(position>0 && array[position-1]>tempValue)
{
array[position] = array[position - 1];
position--;
}
array[position] = tempValue;
}
}
}
}
- 퀵 정렬
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using System;
namespace ConsoleApp2
{
class Program
{
public static void Main(string[] args)
{
}
}
public class SortableArray
{
private int[] array;
public SortableArray(int a )
{
array = new int[a];
}
public int this[int index]
{
get { return array[index]; }
set
{
if(index > array.Length-1)
{
throw new ArgumentOutOfRangeException();
}
array[index] = value;
}
}
public int Length
{
get { return array.Length; }
}
public SortableArray (SortableArray arr)
{
array = new int[arr.Length];
for (int i=0;i<arr.Length;i++)
{
array[i] = arr[i];
}
}
public SortableArray(int[] arr)
{
array = new int[arr.Length];
for (int i = 0;i<arr.Length;i++)
{
array[i] = arr[i];
}
}
public override string ToString()
{
string s = "";
foreach (var i in array)
{
s += i;
s += " ";
}
return s;
}
public int Partition(int leftIndex, int rightIndex)
{
int pivotPos = rightIndex;
int pivot = array[pivotPos];
int rightPointer = rightIndex - 1;
int leftPointer = leftIndex;
while(true)
{
while (array[leftPointer] < pivot)
leftPointer++;
while (array[rightPointer] > pivot)
rightPointer--;
if (leftPointer >= rightPointer)
break;
else
{
Swap(leftPointer, rightPointer);
}
Swap(leftPointer, rightPointer);
}
return leftPointer;
}
public void Swap(int idx1, int idx2)
{
int tmp = array[idx1];
array[idx1] = array[idx2];
array[idx2] = tmp;
}
public void QuickSort(int leftIndex, int rightIndex)
{
if (rightIndex-leftIndex<=0)
{
return;
}
int pivotPos = Partition(leftIndex, rightIndex);
QuickSort(leftIndex, pivotPos - 1);
QuickSort(pivotPos + 1, rightIndex);
}
}
}
버블 정렬 :
- 0th, 1st index 읽기
- 우측이 더 작으면 교환
- 1st, 2nd 읽기 …
- 패스 스루 (1st 2nd 읽기 …)
- O (N^2)
- 이중 for, i=0~n-1, j=0~n-i-1
선택 정렬 :
- 0th 1st 순차적 확인, 최솟값과 그 인덱스 저장
- 최솟값과 0th 교환
- 패스 스루 (1st) 부터 반복)
- O(N^2)이지만 버블 정렬보다는 2배가량 빠름
- 교환 연산이 패스 스루당 한번씩 수행
- 이중 for, i=0~n, j=i+1~n
삽입 정렬 :
- 1st를 따로 저장해둠
- 1st보다 index 값과 비교해 작으면 그 작은 index를 공백에 shift
- 따로 저장해둔 값을 생긴 공백에 채움
- 0th까지 갔는데도 작지 않으면 원래자리에 돌려놓기
- 패스 스루 (2nd 읽기 ..)
- O(N^2)이지만 실제로는 N^2+2N-2번 일어남
- 이미 정렬된 행렬은 삽입정렬 사용하는것이 효율적
퀵 정렬
- 왼쪽 포인터를 배열의 가장 왼쪽, 오른쪽 포인터를 배열의 가장 오른쪽-1에 놓음 (맨 오른쪽 숫자 : pivot)
- while () 왼쪽포인터의 index 값과 피벗값 비교, 피벗보다 더 작으면 이동, 크거나 같으면 멈춤
- while() 오른쪽 포인터의 index 값과 피벗 값 비교, 피벗보다 더 크면 이동, 작거나 같으면 멈춤
- 왼쪽 포인터의 index가 오른쪽 포인터의 index보다 크거나같으면 정렬 멈춤
- 왼쪽 포인터가 현재 가리키고 있는 값을 피벗과 교환
- 새로 구해진 피벗값을 기준으로 좌우 분할, (leftIndex, pivotPos-1) , (pivotPos+1, rightIndex) 왜냐면 pivotPos는 이미 그 위치가 결정되었기 때문.
- 패스 스루 (재귀적으로)
정렬 별 효율성 (빅 오)
- 삽입, 선택 정렬은 최선, 평균, 최악의 경우 모두 O(N^2)
- 퀵 정렬은 최선, 평균의 경우 O(NlogN), 최악의 경우 O(N^2)
This post is licensed under CC BY 4.0 by the author.