인접 리스트는 연결된 노드만 리스트에 저장하는 방식이다.
아래와 같이 표현할 수 있다.
A -> B,D
B -> A,C,D
C -> B,D
D -> A,B,C
O(V^2)의 공간을 차지하기 때문에, 노드 개수(V)가 크면 메모리를 과도하게 사용한다.O(V + E)의 공간만 필요하므로, 노드가 많고 간선이 적은 경우(희소 그래프, Sparse Graph)에 훨씬 적은 메모리를 사용하게 된다.DFS(Depth First Search)는 깊이 우선 탐색이라는 의미로 트리나 그래프를 최대한 깊게 들어가서 탐색하는 방법이다.
DFS는 재귀나 스택을 이용해서 구현할 수 있다.
BFS(Breadth First Search)는 너비 우선 탐색이라는 의미로 트리의 같은 레벨에 있는 노드들을 먼저 방문 후에 다음 레벨로 이동해 탐색하는 방식이다.
BFS는 큐 자료구조를 이용해 구현할 수 있다.
예를 들어, 아래와 같은 트리를 BFS로 탐색한다고 가정해보면
A
/ \
B C
/ \ \
D E F
BFS 탐색 순서는 A → B → C → D → E → F 순서이다.
이 순서를 어떻게 큐를 사용해 구현하면
BFS 탐색 과정
[A][B, C] → 탐색: [A][C, D, E] → 탐색: [A, B][D, E, F] → 탐색: [A, B, C][E, F] → 탐색: [A, B, C, D][F] → 탐색: [A, B, C, D, E][] → 탐색: [A, B, C, D, E, F] → 탐색 종료이렇게 큐를 이용하면 너비 기반 탐색과 동일하게 탐색을 할 수 있다.
FIFO(선입선출)이기 때문에 먼저 들어온 노드들이 먼저 처리되기 때문에, 같은 레벨에 있는 노드들(B와 C)가 먼저 탐색되고, 그다음 레벨인 D, E, F가 탐색된다.
B, C)이 가장 가까운 경로를 가지며, 레벨 2에 있는 노드들(D, E, F)는 그보다 더 먼 경로를 가지고 있다커맨드 패턴은 자신의 행동을 실행하고 Undo로 돌아갈 수 있는(혹은 그와 같은 효과를 내는) 패턴이다.
포토샵 같은 프로그램 문서 툴 등등에 쓰이는 대표적인 패턴 중 하나다.
내가 알기론 행동을 class같은 데이터로 만들어 일정량을 Stack 같은 곳에 저장하고 하나씩 빼면서 Undo할 수 있는 걸로 안다.
class ICommand
{
public:
virtual void Execute() = 0;
virtual void Undo() = 0;
virtual ~ICommand() {}
};
class Light
{
public:
void TurnOn()
{
std::cout << "불을 켭니다." << std::endl;
}
void TurnOff()
{
std::cout << "불을 끕니다." << std::endl;
}
};
class LightOnCommand : public ICommand
{
private:
Light* light;
public:
LightOnCommand(Light* l) : light(l) {}
void Execute() override
{
light->TurnOn();
}
void Undo() override
{
light->TurnOff();
}
};
class LightOffCommand : public ICommand
{
private:
Light* light;
public:
LightOffCommand(Light* l) : light(l) {}
void Execute() override
{
light->TurnOff();
}
void Undo() override
{
light->TurnOn();
}
};
class RemoteControl
{
private:
ICommand* command;
public:
void setCommand(ICommand* c)
{
command = c;
}
void pressButton()
{
command->Execute();
}
void pressUndo()
{
command->Undo();
}
};
int main()
{
Light livingRoomLight;
LightOnCommand lightOn(&livingRoomLight);
LightOffCommand lightOff(&livingRoomLight);
RemoteControl remote;
// 불 켜기 명령 실행
remote.setCommand(&lightOn);
remote.pressButton();
// 불 끄기 명령 실행
remote.setCommand(&lightOff);
remote.pressButton();
// 직전 명령 취소 (불 켜기)
remote.pressUndo();
return 0;
}
https://school.programmers.co.kr/learn/courses/30/lessons/43164
bool Travel(int index, vector<vector<string>>& leftTickets, vector<string>& travels)
{
vector<string> ticket = leftTickets[index];
string now = ticket[1];
travels.push_back(now);
leftTickets.erase(leftTickets.begin() + index);
bool result = false;
for (int i = 0; i < leftTickets.size(); i++)
if (now == leftTickets[i][0])
result |= Travel(i, leftTickets, travels);
//중간에 이거 안되는데? 하면 다시 leftTrickets를 채워서 뒤로 보내야돼
//완료면 그 계산은 거기서 끝
if (result)
return true;
else
{
if (leftTickets.size() == 0)
return true;
else
{
if (index < leftTickets.size())
leftTickets.insert(leftTickets.begin() + index, ticket);
else
leftTickets.push_back(ticket);
travels.pop_back();
return false;
}
}
}
vector<string> solution(vector<vector<string>> tickets)
{
for (int i = 0; i < tickets.size() - 1; i++)
for (int j = 0; j < tickets.size() - 1; j++)
if (tickets[j][0] > tickets[j + 1][0] || tickets[j][1] > tickets[j + 1][1])
swap(tickets[j], tickets[j+1]);
vector<string> travels;
for (int i = 0; i < tickets.size(); i++)
if (tickets[i][0] == "ICN")
{
travels.push_back("ICN");
vector<vector<string>> clone(tickets);
Travel(i, clone, travels);
if (clone.size() == 0)
break;
//실패했으면 초기화
travels.clear();
}
return travels;
}
Graph 혹은 map을 이용해 푸는게 훨씬 편하고 빨랐을 것 같다.
데이터를 정리하고 DFS를 하는게 훨씬 맞는 방식이었던 것 같다.
중간에 sort(tickets.begin(), tickets.end(), [](vector<string>& first, vector<string>& second))를 이용해 sort를 하려고 했는데 계속 assert가 떠서 원인을 모르겠어서 그냥 직접 정렬했다.
무조건 int를 쓰지 말고 int32를 써라
레퍼런스 타입으로 다른 곳에 공개할 때 Get이 아닌 Acess를 붙여 공개하는게 좋아보임
예)
const FString& GetName();
FString& AcessName();