C#/C# 교과서
[C# 교과서] 31~32. 알고리즘과 절차 지향 프로그래밍, 개체 만들기
서니션
2023. 1. 9. 14:48
알고리즘
- 문제를 해결하는 일련의 절차나 방법을 공식으로 표현한 풀이법
- 프로그래밍을 할 때 생긴 문제의 해결 방법을 체계적으로 정리한 것
- ‘문제 해결 능력’
- 입력(input) → 처리(process) → 출력(output) 중 처리 단계가 알고리즘 단계
- 입력 : 자료구조에서 담당하는 영역. 간단히 변수 및 배열의 데이터를 사용하고 나아가서는 컬렉션, 파일, 데이터베이스의 데이터를 사용하는 영역
- 처리 : 알고리즘 처리 영역
- 출력 : 화면에 보이는 UI를 담당하는 영역
근삿값 알고리즘
using System;
using System.Linq;
using static System.Console;
class Program2
{
static void Main()
{
// 절댓값 구하기 로컬 함수 : Math.Abs() 함수와 동일한 기능을 구현
int Abs(int num) => (num < 0) ? -num : num;
// 1. 초기화
int min = int.MaxValue; // 차이 값의 절댓값 중 최솟값이 담길 그릇
// 2. 입력 : 이진수와 16진수로 표현
int[] num = {0b1010,0x14,0b11110,0x1B,0b10001}; // {10,20,30,27,17}
int target = 25; // target과 가까운 값
int near = default; // 가까운 값 : 27
// 3. 처리 : NEAR
for (int i=0; i<num.Length; i++)
{
int abs = Abs(num[i] - target); // 차이 값의 절댓값
if (abs < min)
{
min = abs; // MIN : 최솟값 알고리즘
near = num[i]; // NEAR : 차이 값의 절댓값 중 최솟값일 때 값
}
}
// 4. 출력
var minimum = num.Min(m=>Math.Abs(m-target));
var closest = num.First(c=>Math.Abs(target-c)==minimum);
WriteLine($"{target}와/과 가장 가까운 값(식):{closest}(차이:{minimum})");
WriteLine($"{target}와/과 가장 가까운 값(문):{near}(차이:{min})");
}
}
가까운 값 모두 구하기
using System;
using System.Collections.Generic;
using static System.Console;
class Program2
{
static void Main()
{
int[] data = {10,20,23,27,17};
int target = 25;
List<int> nears = new List<int>(); // 가까운 값들
int min = Int32.MaxValue;
// 1. MIN 알고리즘 : 차이의 최솟값 구하기
for (int i=0; i<data.Length; i++)
{
if (Math.Abs(data[i] - target) < min)
{
min = Math.Abs(data[i]-target);
}
}
WriteLine($"차이의 최솟값 : {min}");
// 2. NEAR 알고리즘 : 차이의 최솟값이 min인 값들을 다시 한 번 비교
for (int i = 0; i < data.Length; i++)
{
if (Math.Abs(data[i]-target) == min)
{
nears.Add(data[i]); // 가까운 값을 모두 저장
}
}
// 가까운 값 출력
foreach (var n in nears)
{
WriteLine(n);
}
}
}
25와 가까운 데이터인 23과 27을 모두 구해서 리스트에 넣은 후 출력하는 예제
선택 정렬 알고리즘
- 데이터 하나를 기준으로 나머지 데이터와 비교하여 가장 작거나 큰 데이터와 자리를 바꾸는 식으로 반복해서 비교하는 정렬 방법
- 선택 정렬은 데이터 개수가 n개이면 전체 회전수는 n-1회
using System;
using static System.Console;
class Program2
{
static void Main()
{
int[] data = {3,2,1,5,4};
int N = data.Length;
for (int i = 0; i < N-1; i++)
{
for (int j = i+1; j < N; j++)
{
if (data[i] > data[j]) // > 오름차순, < 내림차순
{
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
}
for (int i = 0; i < N; i++)
{
Write($"{data[i]}\\t");
}
WriteLine();
}
}
이진 검색 알고리즘
- 주어진 데이터가 오름차순으로 정렬되어 있다고 가정
- 의미 그대로 데이터를 절반으로 나누어서 검색하여 순차 검색보다 효율이 좋음
using System;
using static System.Console;
class Program2
{
static void Main()
{
// 1. 입력
int[] data = {1,3,5,7,9}; // 오름차순으로 정렬되었다고 가정
int N = data.Length;
int search = 3; // 검색할 데이터
bool flag = false; // 플래그 변수
int index = -1; // 인덱스 변수 : 찾은 위치
// 2. 처리
int low =0; // min : 낮은 인덱스
int high = N-1; // max : 높은 인덱스
while (low <= high)
{
int mid = (low + high) / 2; // 중간 인덱스 구하기
if (data[mid]==search)
{
flag = true;
index = mid;
break; // 찾으면 플래그, 인덱스 저장 후 종료
}
if (data[mid]>search)
{
high = mid - 1; // 찾을 데이터가 작으면 왼쪽 영역으로 이동
}
else
{
low = mid + 1; // 찾을 데이터가 크면 오른쪽 영역으로 이동
}
}
// 3. 출력
if(flag)
{
WriteLine($"{search}을(를) {index} 위치에서 찾았습니다.");
}
else
{
WriteLine("찾지 못했습니다.");
}
}
}
병합 알고리즘
- 배열 2개를 합쳐 하나로 만듦
using System;
using static System.Console;
class Program2
{
static void Main()
{
// 1. 입력
int[] first = {1,3,5};
int[] second = {2,4};
int M = first.Length; int N = second.Length; // M:N 관행
int[] merge = new int[M+N]; // 병합된 배열
int i=0; int j=0; int k=0; // i,j,k 관행
// 2. 처리 (merge)
while (i<M && j<N) // 둘 중 하나라도 배열 끝에 도달할 때까지
{
if (first[i] <= second[j]) // 작은 값을 merge 배열에 저장
{
merge[k++] = first[i++];
}
else
{
merge[k++] = second[j++];
}
}
while (i < M) // 첫 번째 배열이 끝에 도달할 때까지
{
merge[k++] = first[i++];
}
while (j < N) // 두 번째 배열이 끝에 도달할 때까지
{
merge[k++] = second[j++];
}
// 3. 출력
foreach (var m in merge)
{
Write($"{m}\\t");
}
WriteLine();
}
}
최빈값 구하기 : MODE 알고리즘
- 최빈값은 데이터 중에서 가장 많이 나타내는 값
- 다른 알고리즘과 또 다른 모양으로 데이터 자체를 배열의 인덱스로 보고, 인덱스 개수 알고리즘을 적용하는 형태
그룹 알고리즘
- 반별 총점이나 평균, 제품별 판매금액의 합 같은 그룹별로 구분되는 데이터의 통계를 산출할 때 사용
클래스와 개체
- class 키워드로 생성한 것을 클래스
- 클래스를 new 키워드로 사용하여 새로운 이름으로 만든 것을 개체
- 모든 개체는 GetHashCode() 메서드를 호출하여 고유의 키 값을 제공받을 수 있음
- 이 클래스로 만든 개체들은 서로 다른 인스턴스
인스턴스 메서드
- 클래스 내에 선언된 메서드 중에서 static 키워드가 붙지 않은 메서드를 인스턴스 메서드라고 함
- 인스턴스 메서드를 호출하려면 클래스의 인스턴스를 생성하여 개체를 만들어야 함
- new 키워드를 사용
- 인스턴스 메서드를 호출하려면 개체.인스턴스메서드이름();
Car 클래스의 인스턴스 생성 → Car car = new Car();
Car 클래스의 Go() 인스턴스 메서드 호출 → car.Go();
익명 형식
- 클래스를 선언하지 않고 개체를 만드는 익명 형식도 있음
- 특정 클래스 없이 이름 하나로 여러 속성을 모아 관리할 때 유용
정적 멤버와 인스턴스 멤버
- static 키워드가 붙은 멤버에 접근할 때는 클래스 이름.멤버이름; 형태로 접근
- static 키워드가 붙지 않은 멤버에 접근할 때는 클래스의 인스턴스를 생성하고 생성된 개체이름.멤버이름; 형태로 접근
- static 키워드가 붙은 변수를 클래스 변수, 공유 개념
- static이 붙지 않은 변수를 인스턴스 변수라고 함
ToString() 메서드 오버라이드
개체에 대한 문자열을 재정의
클래스 배열
클래스도 데이터 형식의 하나이므로 배열처럼 사용 가능
using System;
public class CategoryClass
{
public void Print(int i) => Console.WriteLine($"카테고리 {i}");
}
class ClassArray
{
static void Main()
{
CategoryClass[] categories = new CategoryClass[3];
categories[0] = new CategoryClass();
categories[1] = new CategoryClass();
categories[2] = new CategoryClass();
for (int i = 0; i < categories.Length; i++)
{
categories[i].Print(i);
}
}
}
var 키워드를 사용하여 클래스의 인스턴스 생성
var 키워드를 사용하면 코드가 살짝 짧아짐
public class ExamClass{}
ExamClass exam1 = new ExamClass();
var exam2 = new ExamClass();
긴 클래스 이름을 사용 할때는 간단히 var로 줄여 표현하는 것도 나쁘지 않음
- 정적 멤버 호출은 가내수공업으로 필요할 때 바로 호출해서 사용하는 개념
- 인스턴스 멤버 호출은 대기업 기성품처럼 설계도를 바탕으로 개체를 대량으로 만들어 사용
- 프로그램 내에서 한두 번 호출하는 경우에 정적 멤버 사용
- 여러 번 반복해서 사용하는 경우에 인스턴스 멤버 사용