정렬 알고리즘이란?
주어진 데이터 세트를 특정 순서(대개는 숫자의 오름차순 또는 내림차순, 문자열의 사전식 순서)로 배열하는 방법을 제공하는 것이다.
1. 선택 정렬 (Selection Sort)
<예시> : 선택 정렬 구현 예제
int[] arr = new arr[] { 5, 2, 4, 6, 1, 3 }; for (int i = 0; i < arr.Length - 1; i++) { int minIndex = i; for (int j = i + 1; j < arr.Length; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } int temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } foreach (int num in arr) { Console.WriteLine(num); }
2. 삽입 정렬 (Insertion Sort)
<예시> : 삽입 정렬 구현 예제
int[] arr = new arr[] { 5, 2, 4, 6, 1, 3 }; for (int i = 1; i < arr.Length; i++) { int j = i - 1; int key = arr[i]; while(j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = key; } foreach (int num in arr) { Console.WriteLine(num); }
3. 퀵 정렬 (Quick Sort)
<예시> : 퀵 정렬 구현 예제
void QuickSort(int[] arr, int left, int right) { if (left < right) { int pivot = Partition(arr, left, right); QuickSort(arr, left, pivot - 1); QuickSort(arr, pivot + 1, right); } } int Partition(int[] arr, int left, int right) { int pivot = arr[right]; int i = left - 1; for (int j = left; j < right; j++) { if (arr[j] < pivot) { i++; Swap(arr, i, j); } } Swap(arr, i + 1, right); return i + 1; } void Swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } int[] arr = new int[] { 5, 2, 4, 6, 1, 3 }; QuickSort(arr, 0, arr.Length - 1); foreach (int num in arr) { Console.WriteLine(num); }
4. 병합 정렬 (Merge Sort)
<예시> : 병합 정렬 구현 예제
void MergeSort(int[] arr, int left, int right) { if (left < right) { int mid = (left + right) / 2; MergeSort(arr, left, mid); MergeSort(arr, mid + 1, right); Merge(arr, left, mid, right); } } void Merge(int[] arr, int left, int mid, int right) { int[] temp = new int[arr.Length]; int i = left; int j = mid + 1; int k = left; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } while (i <= mid) { temp[k++] = arr[i++]; } while (j <= right) { temp[k++] = arr[j++]; } for (int l = left; l <= right; l++) { arr[l] = temp[l]; } } int[] arr = new int[] { 5, 2, 4, 6, 1, 3 }; MergeSort(arr, 0, arr.Length - 1); foreach (int num in arr) { Console.WriteLine(num); }
탐색 알고리즘이란?
탐색 알고리즘은 주어진 데이터 집합에서 특정 항목을 찾는 방법을 제공한다. 여기서는 가장 일반적인 탐색 알고리즘 몇 가지를 살펴본다.
1. 선형 탐색 (Linear Search)
<예시> : 선형 탐색 구현 예제
int SequentialSearch(int[] arr, int target) { for (int i = 0; i < arr.Length; i++) { if (arr[i] == target) { return i; } } return -1; }
2. 이진 탐색 (Linear Search)
<예시> : 이진 탐색 구현 예제
int BinarySearch(int[] arr, int target) { int left = 0; int right = arr.Length - 1; while(left <= right) { int mid = (left + right) / 2; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; }
그래프란?
정점(Vertex)과 간선(Edge)으로 이루어진 자료 구조
방향 그래프(Directed Graph)와 무방향 그래프(Undirected Graph)로 나뉜다.
가중치 그래프 (Weighted Graph)는 간선에 가중치가 있음
<그래프 탐색 방법>
깊이 우선 탐색 (Depth-First Search, DFS)
DFS는 트리나 그래프를 탐색하는 알고리즘 중 하나로, 루트에서 시작하여 가능한 한 깊이 들어가서 노드를 탐색하고, 더 이상 방문할 노드가 없으면 이전 노드로 돌아가는 방식.
너비 우선 탐색 (Breadth-First Search, BFS)
BFS는 트리나 그래프를 탐색하는 알고리즘 중 하나로, 루트에서 시작하여 가까운 노드부터 방문하고, 그 다음 레벨의 노드를 방문하는 방식이다.
<예시> : DFS, BFS 활용 예제
`
using System;
using System.Collections.Generic;public class Graph
{
private int V; // 그래프의 정점 개수
private List[] adj; // 인접 리스트public Graph(int v) { V = v; adj = new List<int>[V]; for (int i = 0; i < V; i++) { adj[i] = new List<int>(); } } public void AddEdge(int v, int w) { adj[v].Add(w); } public void DFS(int v) { bool[] visited = new bool[V]; DFSUtil(v, visited); } private void DFSUtil(int v, bool[] visited) { visited[v] = true; Console.Write($"{v} "); foreach (int n in adj[v]) { if (!visited[n]) { DFSUtil(n, visited); } } } public void BFS(int v) { bool[] visited = new bool[V]; Queue<int> queue = new Queue<int>(); visited[v] = true; queue.Enqueue(v); while (queue.Count > 0) { int n = queue.Dequeue(); Console.Write($"{n} "); foreach (int m in adj[n]) { if (!visited[m]) { visited[m] = true; queue.Enqueue(m); } } } }}
public class Program
{
public static void Main()
{
Graph graph = new Graph(6);graph.AddEdge(0, 1); graph.AddEdge(0, 2); graph.AddEdge(1, 3); graph.AddEdge(2, 3); graph.AddEdge(2, 4); graph.AddEdge(3, 4); graph.AddEdge(3, 5); graph.AddEdge(4, 5); Console.WriteLine("DFS traversal:"); graph.DFS(0); Console.WriteLine(); Console.WriteLine("BFS traversal:"); graph.BFS(0); Console.WriteLine(); }}
<예시> : 다익스트라 알고리즘 예제
using System; class DijkstraExample { static int V = 6; // 정점의 수 // 주어진 그래프의 최단 경로를 찾는 다익스트라 알고리즘 static void Dijkstra(int[,] graph, int start) { int[] distance = new int[V]; // 시작 정점으로부터의 거리 배열 bool[] visited = new bool[V]; // 방문 여부 배열 // 거리 배열 초기화 for (int i = 0; i < V; i++) { distance[i] = int.MaxValue; } distance[start] = 0; // 시작 정점의 거리는 0 // 모든 정점을 방문할 때까지 반복 for (int count = 0; count < V - 1; count++) { // 현재 방문하지 않은 정점들 중에서 최소 거리를 가진 정점을 찾음 int minDistance = int.MaxValue; int minIndex = -1; for (int v = 0; v < V; v++) { if (!visited[v] && distance[v] <= minDistance) { minDistance = distance[v]; minIndex = v; } } // 최소 거리를 가진 정점을 방문 처리 visited[minIndex] = true; // 최소 거리를 가진 정점과 인접한 정점들의 거리 업데이트 for (int v = 0; v < V; v++) { if (!visited[v] && graph[minIndex, v] != 0 && distance[minIndex] != int.MaxValue && distance[minIndex] + graph[minIndex, v] < distance[v]) { distance[v] = distance[minIndex] + graph[minIndex, v]; } } } // 최단 경로 출력 Console.WriteLine("정점\t거리"); for (int i = 0; i < V; i++) { Console.WriteLine($"{i}\t{distance[i]}"); } } static void Main(string[] args) { int[,] graph = { { 0, 4, 0, 0, 0, 0 }, { 4, 0, 8, 0, 0, 0 }, { 0, 8, 0, 7, 0, 4 }, { 0, 0, 7, 0, 9, 14 }, { 0, 0, 0, 9, 0, 10 }, { 0, 0, 4, 14, 10, 0 } }; int start = 0; // 시작 정점 Dijkstra(graph, start); } }
동적 프로그래밍이란?
<예시> : DP로 피보나치 수열 구현
public int Fibonacci(int n) { int[] dp = new int[n+1]; dp[0] = 0; dp[1] = 1; for (int i = 2; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; }
그리디 알고리즘이란?
<예시> : 주어진 동전들로 특정 금액을 만드는데 필요한 최소 동전 수를 구하는 함수
public int MinCoins(int[] coins, int amount) { Array.Sort(coins); int count = 0; for (int i = conis.Length-1; i >= 0; i--) { while (amount >= coins[i]) { amount -= conis[i]; count++; } } if (amount > 0) { return -1; } return count; }
분할 정복이란?
<예시> : 퀵 정렬을 통해 주어진 배열을 정렬하는 함수
public void QuickSort (int[] arr, int low, int high) { if (low < high) { int pi = Partition(arr, low, high); QuickSort(arr, low, pi - 1); QuickSort(arr, pi + 1, high); } } int Partition (int[] arr, int low, int high) { int pivot = arr[high]; int i = (low - 1); for (int j = low; j <= high - 1; j++) { if (arr[j] < pivot) { i++; Swap(arr, i, j); } } Swap(arr, i+1, high); return (i + 1); } void Swap (int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; }
<문제>
Given an array of integers heights representing the histogram's bar height where the width of each bar is 1, return the area of the largest rectangle in the histogram.
<번역>
heights 히스토그램의 막대 높이를 나타내는 정수 배열이 주어지고 각 막대의 너비가 인 경우 히스토그램에서 가장 큰 사각형의 면적을1 반환합니다.

입력 : [2, 1, 5, 6, 2, 3]
출력 : 10
설명 : The above is a histogram where width of each bar is 1.
The largest rectangle is shown in the red area, which has an area = 10 units.
public class Solution { public int LargestRectangleArea(int[] heights) { Stack<int> st = new Stack<int>(); int result = 0; for(int i = 0; i < heights.Length; i++) { while(st.Count() != 0 && heights[i] < heights[st.Peek()]) { int h = heights[st.Peek()]; st.Pop(); int w; if(st.Count == 0){ w = i; } else{ w = i - st.Peek() - 1; } result = Math.Max(result, h * w); } st.Push(i); } while(st.Count != 0) { int h = heights[st.Peek()]; st.Pop(); int w; if(st.Count == 0){ w = heights.Length; } else{ w = heights.Length - st.Peek() - 1; } result = Math.Max(result, h * w); } return result; } }
<풀이>
힌트를 보고 스택을 이용해서 풀면 되겠다고 생각하여 스택을 활용해서 해결했다.
스택에 막대의 주소(i)를 저장하며 순회하면서, 각 바에 대해서 아래와 같이 수행하면 된다.
1. 스택이 비어 있지 않고, 현재 바의 높이가 스택 top의 막대 높이보다 낮을 경우
현재 막대는 제외하고 스택 top의 바를 기준으로 만들 수 있는 가장 큰 직사각형을 구하고 최대 넓이를 갱신한다. 높이(h)는 스택 top의 막대 높이이고, 너비(w)는 새로운 top(이전에 pop을 한번 진행하였으므로 스택의 2번째 인덱스라고 할 수 있음)에 존재하는 막대와 현재 막대 사이에 존재하는 막대의 너비이다.
2. 스택이 비어 있고, 현재 바의 높이가 스택의 top 막대 높이보다 낮을 경우
스택top의 막대를 pop했을 때, 해당 막대가 1번째 막대거나 전에 push했던 막대들이 전부 해당 막대보다 높은 경우이므로 너비(w)는 i가 된다. 나머지 처리는 1번과 같다.
3. 순회가 끝난 후 스택에 막대가 남아 있는 경우
이들도 위와 같은 처리를 해주면서 최대 넓이를 갱신해주면 된다.
<문제>
You are given an image represented by an m x n grid of integers image, where image[i][j] represents the pixel value of the image. You are also given three integers sr, sc, and color. Your task is to perform a flood fill on the image starting from the pixel image[sr][sc].
To perform a flood fill:
Begin with the starting pixel and change its color to color.
Perform the same process for each pixel that is directly adjacent (pixels that share a side with the original pixel, either horizontally or vertically) and shares the same color as the starting pixel.
Keep repeating this process by checking neighboring pixels of the updated pixels and modifying their color if it matches the original color of the starting pixel.
The process stops when there are no more adjacent pixels of the original color to update.
Return the modified image after performing the flood fill.
<번역>
이미지는 정수 이미지의 m x n 격자로 표시되며, 여기서 image[i][j]는 이미지의 픽셀 값을 나타냅니다. 또한 세 개의 정수 sr, sc, color가 주어집니다. 당신의 임무는 픽셀 이미지[sr][sc]에서 시작하여 이미지에 플러드 채우기를 수행하는 것입니다.
플러드 채우기 수행하기:
시작 픽셀부터 시작하여 색상을 color로 변경합니다.
각 픽셀에 대해 동일한 프로세스를 수행합니다(원래 픽셀과 한 면을 수평 또는 수직으로 공유하는 픽셀). 시작 픽셀과 동일한 색상을 공유합니다.
업데이트된 픽셀의 인접 픽셀을 확인하고 시작 픽셀의 원래 색상과 일치하는지 색상을 수정하여 이 과정을 계속 반복합니다.
원래 색상의 인접 픽셀이 더 이상 업데이트되지 않으면 프로세스가 중지됩니다.
플러드 채우기를 수행한 후 수정된 이미지를 반환합니다.

입력 : [[1, 1, 1], [1, 1, 0], [1, 0, 1]], sr = 1, sc = 1, color = 2
출력 : [[2, 2, 2], [2, 2, 0], [2, 0, 1]]
설명 : From the center of the image with position (sr, sc) = (1, 1) (i.e., the red pixel), all pixels connected by a path of the same color as the starting pixel (i.e., the blue pixels) are colored with the new color.
Note the bottom corner is not colored 2, because it is not horizontally or vertically connected to the starting pixel.
public class Solution { int[] dx = new int[] {1,0,-1,0}; int[] dy = new int[] {0,1,0,-1}; public void DFS(int x, int y, int[][] image, int color, int startColor){ if (image[x][y] != startColor) { return; } image[x][y] = color; for(int i=0;i<4;i++){ int next_x = x + dx[i]; int next_y = y + dy[i]; if (next_x >= 0 && next_x < image.Length && next_y >= 0 && next_y < image[0].Length) { DFS(next_x,next_y, image, color, startColor); } } } public int[][] FloodFill(int[][] image, int sr, int sc, int color) { if (image[sr][sc] == color) { return image; } DFS(sr,sc,image,color,image[sr][sc]); return image; } }
<풀이>
해당 문제는 DFS를 활용하여 해결할 수 있다.
시작 위치의 색이 바꿀 색상과 같으면 색변경이 필요 없으므로 바로 image를 return 한다.
DFS를 통해 시작위치와 색이 같으며 인접한(상하좌우로 연결되어 있으면 인접해 있다고 정의한다) 위치에 존재하는 픽셀을 새로운 색으로 바꾼다. 이때, DFS 함수를 활용해서 재귀적으로 호출하면 인접한 픽셀의 색을 모두 바꿀 수 있다.
<문제>
Given an integer array nums, return the length of the longest strictly increasing subsequence.
<번역>
주어진 정수 배열에서, 최장 증가 부분 수열을 반환하라.
입력 : nums = [10, 9, 2, 5, 3, 7, 101, 18]
출력 : 4
설명 : The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
public class Solution { public int LengthOfLIS(int[] nums) { int[] dp = new int[nums.Length]; for(int i=0;i<nums.Length;i++){ dp[i] = 1; } int result = 0; for(int i=0;i<nums.Length;i++){ for(int j=0;j<i;j++){ if(nums[i] > nums[j]){ dp[i] = Math.Max(dp[i], dp[j] + 1); } } result = Math.Max(result, dp[i]); } return result; } }
<풀이>
여러가지 방법이 있겠지만, 나는 DP를 이용해서 해결했다.
간단하게 설명하면 배열을 순회하며 각 부분까지의 최장 증가 부분 수열의 길이를 구해 dp배열에 저장하고, 최대값을 갱신하는 식으로 문제를 해결했다.