트라이
class TrieNode:
de __init__(self):
self.children = {}
단어 저장
nil
값을 저장한다.{
"a": {
"c": {
"e": {
"*": nill
}
}
}
}
별표(asterisk)의 필요성
트라이 검색
def search(self, word):
currentNode = self.root
for char in word:
if currentNode.children.get(char):
currentNode = currentNode/children[char]
else:
return Node
return currentNode
트라이 검색의 효율성
트라이 삽입
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let currentNode = this.root;
for (const char of word) {
if(currentNode.children[char]) {
currentNode = currentNode.children[char];
} else {
let newNode = new TrieNode();
currentNode.children[char] = newNode;
currentNode = newNode;
}
}
currentNode.children["*"] = null;
}
}
자동 완성 개발
단어 수집
collectAllWords(node=null, word="", words=[]) {
let currentNode = node || this.root;
for (let [key, childNode] of Object.entries(currentNode.children)) {
if (key === "*") {
words.push(word);
} else {
this.collectAllWords(childNode, word + key, words)
}
}
return words;
}
트라이 내에 있는 모든 단어를 배열에 담아 리턴하는 변수로 루트 노드에서 word에 해당하는 글자를 추가하면서 재귀 함수가 실행되는 형태로 구현하였다.
자동 완성 기능
autocomplete(prefix){
let currentNode = this.search(prefix)
if (!currentNode) {
return null
}
return this.collectAllWords(curentNode, prefix)
}
search
메서드를 사용하여 현재 단어와 일치하는 노드를 찾고 collectAllWords
로 해당 노드에 포함된 모든 단어를 담은 배열을 출력하면 자동완성 기능이 구현된다.
자동완성 기능은 사용자가 입력할 수 있는 모든 단어를 보여주는 것이 아니라 자주 사용할 만한 단어를 보여주는 것이 일반적이므로 *
의 값으로 null
이 아니라 검색 되는 횟수를 저장하여 점수가 높은 단어를 우선적으로 리턴할 수 있도록 구현할 수 있다.
class Vertex
attr_accessor :value , :adjacent_vertices
def initialize(value)
@value = value
@adjacent_vertices = []
end
def add_adjacent_vertex(vertex)
@adjacent_vertices << vertex
end
end
Vertex 클래스의 value는 한 사람을 나타내고 adjacent_vertices 배열은 그 사람과 연결된 모든 사람을 포함한다. def add_adjacent_vertex(vertex)
return if adjacent_vertices.includes?(vertex)
@adjacent_vertices << vertex
vertex.add_adjacent_vertex(self)
end
end
모든 친구 관계가 상호적인 소셜 네트워크를 위한 무방향 그래프를 만든하면 add_adjacent_vertex 메서드를 위와 같이 수정하여 항상 양방향으로 간선이 추가되게 할 수 있다.그래프 탐색
깊이 우선 탐색(Depth-First Search)
//부트캠프에서 구현한 무방향 그래프의 깊이 우선 탐색
function connectedVertices(edges) {
const maxVertex = Math.max(...edges.flat())
const adjList = {};
for (let i = 0; i <= maxVertex; i++) {
adjList[i] = [];
}
for (let i = 0; i < edges.length; i++) {
adjList[edges[i][0]].push(edges[i][1]);
adjList[edges[i][1]].push(edges[i][0]);
}
const visited = {};
let count = 0;
for (let vertex = 0; vertex <= maxVertex; vertex++) {
if (!visited[vertex]) {
dfs(adjList, vertex, visited);
count++;
}
}
return count;
function dfs(adjList, vertex, visited) {
visited[vertex] = true;
for (let i=0; i<adjList[vertex].length; i++) {
if(!visited[adjList[vertex][i]]) {
dfs(adjList, adjList[vertex][i], visited)
}
}
}
}
너비 우선 탐색(Breadth-First Search)
//부트캠프에서 구현한 무방향 그래프의 너비 우선 탐색
function connectedVertices(edges) {
const maxVertex = Math.max(...edges.flat())
const adjList = {};
for (let i = 0; i <= maxVertex; i++) {
adjList[i] = [];
}
for (let i = 0; i < edges.length; i++) {
adjList[edges[i][0]].push(edges[i][1]);
adjList[edges[i][1]].push(edges[i][0]);
}
const visited = {};
let count = 0;
for (let vertex = 0; vertex <= maxVertex; vertex++) {
if (!visited[vertex]) {
bfs(adjList, vertex, visited);
count++;
}
}
return count;
function bfs(adjList, vertex, visited) {
visited[vertex] = true;
const queue = [vertex]
while (queue.length > 0) {
const cur = queue.shift()
for (let i=0; i<adjList[cur].length; i++) {
if(!visited[adjList[cur][i]]) {
queue.push(adjList[cur][i])
visited[adjList[cur][i]] = true;
}
}
}
}
}
깊이 우선 탐색 대 너비 우선 탐색
그래프 탐색의 효율성
가중 그래프
class WeightedGraphVertex
attr_accessor :value, :adjacent_vertices
def intialize(value)
@value = value
@adjacent_vertices = {}
end
def add_adjacent_vertex(vertex, weight)
@adjacent_vertices[vertex] = weight
end
adjacent_vertices
를 배열이 아닌 해시 테이블로 구현하여 weight
를 저장하므로써 가중 그래프를 구현하였다.데이크스트라의 알고리즘
데이크스트라의 알고리즘은 최단 경로 문제를 해결할 수 있는 알고리즘으로 특정 지역으로 가는 최소 비용 뿐만 아니라 모든 지역으로 가는 최소 비용을 알 수 있다.
데이크스트라 알고리즘 준비
cheapest_prices_table
표를 구현한다.cheapest_privious_stopover_city_table
를 구현한다.데이크스트라의 알고리즘 단계
cheapest_prices_table
의 비용보다 저렴하면(혹은 인접 도시가 아직 cheapest_prices_table
에 없으면),cheapest_prices_table
을 더 저렵한 비용을 업데이트한다.cheapest_privious_stopover_city_table
을 업데이트한다.최단 경로 찾기
cheapest_prices_table
최단 경유를 알고 싶을 때는 cheapest_privious_stopover_city_table
를 통해 알 수 있다.class City
attr_accessor :name, :routes
def initialize(name)
@name = name
@routes = {}
end
def add_route(city, price)
@routes[city] = price
end
end
def dijkstra_shortest_path(starting_city, final_destination)
cheapest_prices_table = {}
cheapest_previous_stopover_city_table = {}
unvisited_cities = []
visited_cities = {}
cheapest_prices_table[starting_city.name] = 0
current_city = starting_city
while current_city
visited_cities[current_city.name] = true
unvisited_cities.delete(current_city)
current_city.routes.each do |adjacent_city, price|
unvisited_cities <<
adjacent_city unless visited_cities[adjacent_city.name]
price_through_current_city =
cheapest_prices_table[current_city.name] + price
if !cheapest_prices_table[adjacent_city.name] ||
price_through_current_city < cheapest_prices_table[adjacent_city.name]
cheapest_prices_table[adjacent_city.name] = price_through_current_city
cheapest_previous_stopover_city_table[adjacent_city.name] =
current_city.name
end
end
current_city = unvisited_cities.min do |city|
cheapest_prices_table[city.name]
end
end
shortest_path = []
current_city_name = final_destination.name
# We loop until we reach our starting city:
while current_city_name != starting_city.name
shortest_path << current_city_name
current_city_name = cheapest_previous_stopover_city_table[current_city_name]
end
shortest_path << starting_city.name
return shortest_path.reverse
end
atlanta = City.new("Atlanta")
boston = City.new("Boston")
chicago = City.new("Chicago")
denver = City.new("Denver")
el_paso = City.new("El Paso")
atlanta.add_route(boston, 100)
atlanta.add_route(denver, 160)
boston.add_route(chicago, 120)
boston.add_route(denver, 180)
chicago.add_route(el_paso, 80)
denver.add_route(chicago, 40)
denver.add_route(el_paso, 140)
p dijkstra_shortest_path(atlanta, el_paso)
데이크스트라의 알고리즘의 효율성
이번 파트에서는 트라이와 그래프에 대해서 학습하고 각 자료구조의 효율성과 추가적인 알고리즘에 대해서 학습하였다. 트라이 같은 경우 관련된 코딩 문제를 풀어본 적이 있으나 트라이라는 이름을 가지고 있었는지 효율성은 어떤지에 대해 알지 못했었고 그래프 역시 BFS, DFS 외에 데이크스트라와 같은 알고리즘을 추가로 알 수 있는 내용이 포함되어 있어서 도움이 되었다.