데이터는 실생활을 구성하고 있는 모든 값. 이 데이터를 효율적으로 다룰 수 있는 방법
자료구조 시각화 자료
import java.util.*;
public class BracketCheker {
public static void main(String[] args) {
String s1 = "([])(){}";
String s2 = "([)]";
String s3 = "([](){([])})";
String s4 = "";
System.out.println(s1 + " is balanced? " + isBalanced(s1)); // ([])(){} is balanced? true
System.out.println(s2 + " is balanced? " + isBalanced(s2)); // ([)] is balanced? false
System.out.println(s3 + " is balanced? " + isBalanced(s3)); // ([](){([])}) is balanced? true
System.out.println(s4 + " is balanced? " + isBalanced(s4)); // is balanced? true
}
public static boolean isBalanced(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(' || c == '{' || c == '[') {
stack.push(c);
}
else if (c == ')' || c == '}' || c == ']') {
if (stack.isEmpty()) return false;
char top = stack.pop();
if (c == ')' && top != '(' || c == '}' && top != '{' || c == ']' && top != '[')
return false;
}
}
return stack.isEmpty();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
public class StringTransformation {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
System.out.println(transform(str));
}
static String transform (String str) {
StringBuilder sb = new StringBuilder();
Queue<Character> que = new LinkedList<>();
for (char c : str.toCharArray()) {
que.offer(c);
}
while (!que.isEmpty()) {
sb.append(que.poll());
if (que.isEmpty()) break;
que.offer(que.poll());
}
return sb.toString();
}
}
*크기가 9, 높이가 3인 이진 트리
자식 노드가 최대 두 개인 노드들로 구성된 트리
왼쪽 자식 노드와 오른쪽 자식 노드
이진 탐색의 속성이 이진 트리에 적용된 형태의 이진 트리
이진 탐색이란
정렬된 데이터 중 특정한 값을 찾기 귀한 탐색 알고리즘 중 하나.
오름차순으로 정렬된 데이터를 같은 크기의 두 부분으로 나눈 후 두 부분 중 탐색이 필요한 부분에서만 탐색범위를 제한하여 원하는 값을 찾음
1. 전체 데이터에서 중간(root)에 찾고자 하는 값이 있는지 확인
2. 중간값이 찾고자 하는 값이 아닐 경우, 오름차순으로 정렬된 데이터에서 중간값보다 큰값인지 작은값인지 판탄
3. 찾고자 하는 값이 중간값보다 작을 경우 데이터의 맨 앞에서 중간값 전까지의 범위를 반복 탐색
4. 찾고자 하는 값이 중간값보다 클 경우, 데이터의 중간값의 다음 값부터 맨 마지막까지 반복 탐색
import java.util.ArrayList;
public class Tree_traverse {
public static void main(String[] args) {
}
// 전위 순회(preorder traverse)
public ArrayList<String> preOrder(Node node, ArrayList<String> list) {
if (node != null) {
list.add(node.getData());
list = preOrder(node.getLeft(), list);
list = preOrder(node.getRight(), list);
}
return list;
// 탐색 종료 시 list의 값 -> ["A", "B", "D", "H", "I", "E", "J", "K", "C", "F", "L", "M", "G", "N", "O"]
}
// 중위 순회(inorder traverser)
public ArrayList<String> inOrder(Node node, ArrayList<String> list) {
if (node != null) {
list = inOrder(node.getLeft(), list);
list.add(node.getData());
list = inOrder(node.getRight(), list);
}
return list;
// 탐색 종료 시 list의 값 -> ["H", "D", "I", "B", "E", "J", "K", "A", "L", "F", "M", "C", "N", "G", "O"]
}
// 후위 순회(postorder traverse)
public ArrayList<String> postOrder(Node node, ArrayList<String> list) {
if (node != null) {
list = postOrder(node.getLeft(), list);
list = postOrder(node.getRight(), list);
list.add(node.getData());
}
return list;
// 탐색 종료 시 list의 값 -> ["H", "I", "D", "J", "K", "E", "B", "L", "M", "F", "N", "O", "G", "C", "A"]
}
}
class Node {
String data;
Node left;
Node right;
public Node(String data) {
this.data = data;
}
public Node getLeft() {
return left;
}
public String getData() {
return data;
}
public Node getRight() {
return right;
}
}
*3개의 정점, 3개의 간선으로 이루어진 그래프
rmfladsafsdfadsfadsfasdfasdf
// 인접 행렬 // 가중치 그래프라면 1대신 의미있는 값으로
int[][] matrix = new int[][] {
{0, 1, 0},
{0, 0, 1},
{1, 1, 0}
};
// 인접 리스트 -> 인접 행렬보다 상대적으로 메모리를 덜 차지
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
graph.add(new ArrayList<>(Arrays.asList(1, null)));
graph.add(new ArrayList<>(Arrays.asList(2, null)));
graph.add(new ArrayList<>(Arrays.asList(0, 1, null)));
}
너비 우선 탐색 / 큐를 활용하여 구현
1. 하나의 정점을 기준으로 해당 정점과 인접한 정점을 모두 방문
2. 기준점을 방문한 정점으로 변경하여 해당 정점에 인접한 정점을 방문 반복
최단 경로 탐색에 유리 // 인접한 정점들을 먼저 방문하기 때문
Queue 자료 구조를 사용하여 방문한 정점들을 큐에 저장, 그래프의 크기가 크면 많은 메모리를 사용
시작 정점에서 도달할 있는 정점만 탐색
방문 여부를 체크하는 자료구조를 사용해야 함
활용 시 주의 사항
BFS 예제 템플릿
/**
* BFS 인접행렬 : 너비 우선 탐색을 구현한 템플릿 예제입니다.
*
* @param array : 각 정점을 행/열로 하고, 연결된 정점은 1로, 연결되지 않은 정점은 0으로 표기하는 2차원 배열
* @param visited : 방문여부 확인을 위한 배열
* @param src : 방문할 정점
* @param result : 방문했던 정점 리스트를 저장한 List
*
**/
public ArrayList<Integer> bfs_array(int[][] array, boolean[] visited, int src, ArrayList<Integer> result) {
//bfs의 경우 큐를 사용합니다.
Queue<Integer> queue = new LinkedList<>();
//시작 지점을 큐에 넣어주고, 해당 버택스의 방문 여부를 변경합니다.
queue.offer(src);
visited[src] = true;
//큐에 더이상 방문할 요소가 없을 경우까지 반복합니다.
while (!queue.isEmpty()) {
//현재 위치를 큐에서 꺼낸 후
int cur = queue.poll();
// 현재 방문한 정점을 result에 삽입합니다.
result.add(cur);
//전체 배열에서 현재 버택스의 행만 확인합니다.
for (int i = 0; i < array[cur].length; i++) {
//길이 존재하고, 아직 방문하지 않았을 경우
if(array[cur][i] == 1 && !visited[i]) {
//큐에 해당 버택스의 위치를 넣어준 이후
queue.offer(i);
//방문 여부를 체크합니다.
visited[i] = true;
}
}
}
//이어진 모든 길을 순회한 후 방문 여부가 담긴 ArrayList를 반환합니다.
return result;
}
public class BFS { // 두 노드 사이의 최단 경로 찾기
public static void main(String[] args) {
int [][] graph = {
{0, 1, 1, 0, 0, 0},
{1, 0, 0, 1, 1, 0},
{1, 0, 0, 0, 0, 1},
{0, 1, 0, 0, 0, 0},
{0, 1, 0, 0, 0, 1},
{0, 0, 1, 0, 1, 0}
};
int start = 0;
int end = 5;
System.out.print(shortestPath(graph, start, end));
}
static ArrayList<Integer> shortestPath(int [][] graph, int start, int end) {
boolean[] visited = new boolean[graph.length]; // 방문 여부 체크를 위한 배열
return bfs(graph, start, end, visited);
}
static ArrayList<Integer> bfs (int[][] graph, int start, int end, boolean[] visited) {
Queue<Integer> que = new LinkedList<>();
int[] parent = new int[graph.length];
que.offer(start); // 현재 정점 삽입
visited[start] = true;
parent[start] = -1; // 시작 지점 표시
while (!que.isEmpty()) {
int node = que.poll();
if (node == end) { // 도착점 도달했을 때
ArrayList<Integer> path = new ArrayList<>();
while (node != -1) {
path.add(node);
node = parent[node];
}
Collections.reverse(path);
return path;
}
for (int i = 0; i < graph.length; i++) { // 도착점 도달 전
if (graph[node][i] == 1 && !visited[i]) { // 현재 정점의 인접한 정점 중 방문하지 않은 정점을 큐에 삽입
que.add(i);
visited[i] = true;
parent[i] = node;
}
}
}
return null;
}
}
깊이 우선 탐색 / 스텍, 재귀를 이용하여 구현
한 분기를 탐색 후 다음 분기로 넘어가기 전 해당 분기를 완전히 탐색
탐색 불가능한 상태가 되면 이전 분기로 돌아와 다음 분기 탐색
순환구조 고려 방문 정점을 다시 방문하지 않도록 해야함
/**
* DFS 인접행렬 : 깊이 우선 탐색을 구현한 템플릿 예제입니다.
*
* @param array : 각 정점을 행/열로 하고, 연결된 정점은 1로, 연결되지 않은 정점은 0으로 표기하는 2차원 배열
* @param visited : 방문 여부 확인을 위한 배열
* @param src : 방문할 정점
* @param result : 방문했던 정점 리스트를 저장한 List
*
**/
public ArrayList<Integer> dfs(int[][] array, boolean[] visited, int src, ArrayList<Integer> result) {
// 이미 방문했다면
if (visited[src] == true) {
result.add(src); // 방문한 정점을 저장
return result; // 저장한 데이터를 반환하며, 재귀호출을 종료
}
// 아직 방문하지 않았다면
visited[src] = true; // 방문한 정점을 표기
// 현재 정점에서 이동할 수 있는 정점을 순회하며 재귀 호출
for (int index = 0; index < array.length; index++) {
if (array[src][index] == 1) {
// 재귀 호출을 통해, 방문 여부를 담은 데이터를 반환과 동시에 할당
result = dfs(array, visited, index, result);
}
}
return result;
}
public class getDirections_dfs {
public static void main(String[] args) {
int[][] matrix = new int[][] { // 인접 행렬이 담긴 2차원 배열
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},
{0, 1, 0, 0},
};
System.out.print(getDirections(matrix, 1, 4));
// 주어진 정점으로 다른 정점으로 이어지는 길이 존재하는지 여부를 반환
}
static boolean getDirections(int[][] matrix, int from, int to) {
// from, to -> 행
boolean[] visited = new boolean[matrix.length];
return dfs(matrix, from, to, visited);
}
static boolean dfs (int[][] matrix, int cur, int to, boolean[] visited) {
if (cur == to) return true; // 현재 노드의 갚이 원하는 값일때 리턴
visited[cur] = true; // 현재 방문한 곳을 true로
for (int i = 0; i < matrix.length; i++) {
if (matrix[cur][i] == 1 && !visited[i]) { // 연결된 노드가 있을때 해당 노드로 재귀 진입
if (dfs(matrix, i, to, visited)) return true;
}
}
return false; // 연결된 노드를 확인했는데 원하는 값이 없이 끝났을때 false 리턴
}
}
import java.util.*;
public class ConnectedVertices_dfs {
public static void main(String[] args) {
int[][] edges = new int[][]{ // 시작과 도착 정점이 담겨있는 배열 {시작 정점, 도착 정점} 간선으는 무향
{0, 1},
{2, 3},
{4, 5}
};
System.out.print(connectedVertices(edges));
}
static int connectedVertices(int[][] edges) {
int rc = Arrays.stream(edges).flatMapToInt(Arrays::stream).max().getAsInt() + 1;
int[][] graph = new int[rc][rc]; // 인자로 받은 배열의 요소중 가장 큰 정점을 기준으로 1칸 더 큰 정사각형의 행렬을 생성
for (int i = 0; i < edges.length; i++) { //생성한 graph 배열에 인자로 전달받은 edges의 연결된 정점들의 값에 1을 할당
graph[edges[i][0]][edges[i][1]] = 1;
graph[edges[i][1]][edges[i][0]] = 1;
}
int count = 0;
boolean[] visited = new boolean[rc]; // 해당 행을 방문했는지 체크하기 위한 boolean 배열
for (int i = 0; i < rc; i++) {
if (!visited[i]) {
dfs(graph, i, visited); // 재귀 함수 탈출 후 count + 1
count++;
}
}
return count;
}
static void dfs(int[][] graph, int row, boolean[] visited) {
visited[row] = true;
for (int i = 0; i < graph.length; i++) { // 인자로 받은 행의 열을 돌며 연결된 노드가 있고 해당 노드의
if (graph[row][i] == 1 && !visited[i]) // 행을 다녀온 적이 없으면 해당 열에 해당하는 행으로 재귀
dfs(graph, i, visited);
}
}
}