maeng0830
뇌 채우기 운동
maeng0830
전체 방문자
오늘
어제
  • maeng0830-note (85)
    • 자바 (3)
    • 스프링 (39)
      • Core (21)
      • DB (16)
      • Security (2)
      • Test (0)
    • 자료구조 & 알고리즘 (19)
      • 자료구조 (12)
      • 알고리즘 (7)
    • 다른 개발 도구들 (4)
      • Git&Github (1)
      • Redis (3)
    • 프로젝트 (9)
      • Album (9)
    • CS (10)
      • 운영체제 (5)
      • 데이터베이스 (5)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • spring security
  • 트랜잭션
  • 자료구조
  • JPQL
  • JPA

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
maeng0830

뇌 채우기 운동

자료구조 & 알고리즘/알고리즘

BFS

2023. 9. 18. 18:50

BFS

BFS(Breadth-First Search)는 루트 노드 또는 임의 노드에서 시작해서 가까운 거리에 있는 노드부터 먼저 탐색하는 완전 탐색 방법의 하나이다.

 

BFS의 특징

  • 선입선출의 자료구조인 큐를 통해 구현된다.
  • 그래프에 대한 BFS를 구현할 경우 어떤 정점을 방문했는 지 여부를 반드시 검사해야 무한 루프를 방지할 수 있다.

 

시간 복잡도

그래프의 구현 방법에 따라 시간 복잡도에 차이가 있다. N은 정점의 수, E는 간선의 수이다.

  • 인접 리스트로 표현된 그래프: O(N+E)
  • 인접 행렬로 표현된 그래프: O(N^2)

 

BFS를 사용하기 좋은 상황

  • 그래프의 모든 정점을 방문하는 것이 목적인 경우
  • 두 정점간의 최단 거리와 관련된 문제를 해결하는 것이 목적인 경우

 

BFS의 그래프 탐색 과정

  • 시작 정점을 방문한 후, 깊이가 작은 정점들에서부터 큰 정점들까지 차례로 방문한다.
  • 각 정점을 방문할 때는 방문 여부 체크를 한다.

 

BFS 구현

인접 행렬과 인접 리스트에 대해 BFS를 구현하였다.

 

인접 행렬 BFS

public class AdjMatrix_bfs {

	private int[][] adjMatrix;

	public AdjMatrix_bfs(int size) {
		if (size <= 0) {
			System.out.println("그래프는 정점이 1개 이상이어야 합니다.");
			return;
		}

		adjMatrix = new int[size + 1][size + 1]; // vertex의 인덱스를 1부터 시작하기 위함.
	}

	// 무향 간선 - 가중치 X
	public void insertBiDir(int vertex1, int vertex2) {
		if (valid(vertex1, vertex2)) {
			return;
		}

		adjMatrix[vertex1][vertex2] = 1;
		adjMatrix[vertex2][vertex1] = 1;
	}

	// 무향 간선 - 가중치 O
	public void insertBiDir(int vertex1, int vertex2, int weight) {
		if (valid(vertex1, vertex2)) {
			return;
		}

		adjMatrix[vertex1][vertex2] = weight;
		adjMatrix[vertex2][vertex1] = weight;
	}

	// 방향 간선 - 가중치 X
	public void insertSingleDir(int vertex1, int vertex2) {
		if (valid(vertex1, vertex2)) {
			return;
		}

		adjMatrix[vertex1][vertex2] = 1;
	}

	// 방향 간선 - 가중치 O
	public void insertSingleDir(int vertex1, int vertex2, int weight) {
		if (valid(vertex1, vertex2)) {
			return;
		}

		adjMatrix[vertex1][vertex2] = weight;
	}

	public void bfs(int index) {
		if (valid(index, index)) {
			return;
		}

		// 방문 여부 체크를 위한 배열
		boolean[] visited = new boolean[adjMatrix.length];
		Queue<Integer> queue = new LinkedList<>();

		visited[index] = true;
		queue.add(index);

		while (queue.size() != 0) {
			// 방문한 정점을 큐에서 추출
			index = queue.poll();
			System.out.print(index + " ");

			// 모든 정점 중 방문한 정점과 인접한 정점을 확인한다.
			int[] matrix = adjMatrix[index];
			for (int i = 1; i < matrix.length; i++) {
				// 인접 정점 중 아직 방문하지 않은 정점이면 방문한 것으로 표시하고 큐에 삽입
				if (matrix[i] != 0 && !visited[i]) {
					visited[i] = true;
					queue.add(i);
				}
			}
		}
	}

	private boolean valid(int vertex1, int vertex2) {
		if (adjMatrix.length <= 1) {
			return true;
		}

		if (vertex1 < 1 || vertex1 >= adjMatrix.length || vertex2 < 1 || vertex2 >= adjMatrix.length) {
			return true;
		}

		return false;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();

		for (int i = 1; i < adjMatrix.length; i++) {
			for (int j = 1; j < adjMatrix[i].length; j++) {
				sb.append(adjMatrix[i][j]).append(" ");
			}
			sb.append("\n");
		}

		return sb.toString();
	}
}

 

인접 리스트 BFS

public class Edge {
	private int destination;
	private int weight;

	public Edge(int destination, int weight) {
		this.destination = destination;
		this.weight = weight;
	}

	public Edge(int destination) {
		this.destination = destination;
		this.weight = 1;
	}

	public int getDestination() {
		return destination;
	}

	public int getWeight() {
		return weight;
	}
}


public class AdjList_bfs<T> {
	private int size;
	private List<List<Edge>> adjList = new ArrayList<>(); // 정점들의 목록
	private List<T> dataList = new ArrayList<>(); // 정점들의 데이터를 저장한다.

	// 정점 추가
	public void addVertex(T data) {
		// 인덱스를 1부터 시작하기 위해 첫 번쨰 정점 추가 전에 의미없는 정점을 추가.
		if (size == 0) {
			adjList.add(new ArrayList<>());
			dataList.add(null);
			size++;
		}

		adjList.add(new ArrayList<>()); // 정점들의 목록을 추가한다.
		dataList.add(data); // 정점들의 데이터를 저장한다. 동일한 정점에 대한 adjList와 dataList의 인덱스는 동일하다.
		size++; // 정점 개수 증가
	}

	public void bfs(int index) {
		if (valid(index)) {
			return;
		}

		// 방문 여부 체크를 위한 배열
		boolean[] visited = new boolean[size];
		Queue<Integer> queue = new LinkedList<>();

		visited[index] = true;
		queue.add(index);

		while (queue.size() != 0) {
			// 방문한 정점을 큐에서 추출
			index = queue.poll();
			System.out.print(index + " ");

			// 방문한 정점의 인접 정점을 가져오고 순회한다.
			ListIterator<Edge> listIterator = adjList.get(index).listIterator();
			while (listIterator.hasNext()) {
				// 인접한 정점
				int n = listIterator.next().getDestination();

				// 방문하지 않은 노드면 방문한 것으로 표시하고 큐에 삽입
				if (!visited[n]) {
					visited[n] = true;
					queue.add(n);
				}
			}
		}
	}

	// 간선을 추가한다 - 가중치 지정 O
	public void addEdge(int source, int destination, int weight) {
		if (valid(source) || valid(destination)) {
			return;
		}

		adjList.get(source).add(new Edge(destination, weight));
	}

	// 간선을 추가한다 - 가중치 지정 X
	public void addEdge(int source, int destination) {
		if (valid(source) || valid(destination)) {
			return;
		}

		adjList.get(source).add(new Edge(destination));
	}

	// 주어진 인덱스에 해당하는 정점에서 출발하는 간선 정보를 반환한다.
	public List<Edge> getAdjVertices(int index) {
		return adjList.get(index);
	}

	// 주어진 인덱스에 해당하는 정점의 데이터를 반환한다.
	public T getData(int index) {
		return dataList.get(index);
	}

	private boolean valid(int index) {
		if (size <= 1) {
			System.out.println("정점이 없습니다.");
			return true;
		}

		if (index < 1 || index >= size) {
			System.out.println("유효한 정점 인덱스가 아닙니다.");
			return true;
		}
		return false;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();

		for (int i = 0; i < adjList.size(); i++) {
			sb.append("adjVertices to " + i + ": ");
			List<Edge> adjVertices = getAdjVertices(i);

			for (Edge adjVertex : adjVertices) {
				sb.append(adjVertex.getDestination() + "(" + adjVertex.getWeight() + ")" + " ");
			}

			sb.append("\n");
		}

		return sb.toString();
	}
}

 

실제 사용

public class Main {

	public static void main(String[] args) {
		///////////////////// 인접 리스트의 BFS /////////////////////
		AdjList_bfs<Integer> adjList_bfs = new AdjList_bfs<>();

		for (int i = 1; i <= 5; i++) {
			adjList_bfs.addVertex(i);
		}

		adjList_bfs.addEdge(1, 2);
		adjList_bfs.addEdge(1, 4);
		adjList_bfs.addEdge(2, 3);
		adjList_bfs.addEdge(2, 4);
		adjList_bfs.addEdge(2, 5);
		adjList_bfs.addEdge(3, 2);
		adjList_bfs.addEdge(4, 5);
		adjList_bfs.addEdge(4, 1);
		adjList_bfs.addEdge(5, 1);

		for (int i = 1; i <= 5; i++) {
			adjList_bfs.bfs(i);
			System.out.println();
		}
		/*1 2 4 3 5
		  2 3 4 5 1
		  3 2 4 5 1
		  4 5 1 2 3
		  5 1 2 4 3 */

		System.out.println("/////////////////////////////////////");

		///////////////////// 인접 행렬의 BFS /////////////////////
		AdjMatrix_bfs adjMatrix_bfs = new AdjMatrix_bfs(5);

		adjMatrix_bfs.insertSingleDir(1, 2);
		adjMatrix_bfs.insertSingleDir(1, 4);
		adjMatrix_bfs.insertSingleDir(2, 3);
		adjMatrix_bfs.insertSingleDir(2, 4);
		adjMatrix_bfs.insertSingleDir(2, 5);
		adjMatrix_bfs.insertSingleDir(3, 2);
		adjMatrix_bfs.insertSingleDir(4, 5);
		adjMatrix_bfs.insertSingleDir(4, 1);
		adjMatrix_bfs.insertSingleDir(5, 1);

		for (int i = 1; i <= 5; i++) {
			adjMatrix_bfs.bfs(i);
			System.out.println();
		}
		/*1 2 4 3 5
		  2 3 4 5 1
		  3 2 4 5 1
		  4 5 1 2 3
		  5 1 2 4 3 */
	}
}
    '자료구조 & 알고리즘/알고리즘' 카테고리의 다른 글
    • 서로소 집합(Disjoint Set)-Union-Find
    • 최단 경로(Shortest Path)-Dijkstra
    • 그리디(Greedy)
    • DFS
    maeng0830
    maeng0830

    티스토리툴바