순차 탐색 | 리스트 안에 있는 특정한 데이터를 찾기 위해 앞에서부터 데이터를 하나씩 확인하는 방법 |
이진 탐색 | 정렬되어 있는 리스트에서 탐색 범위를 절반씩 좁혀가며 데이터를 탐색하는 방법 (이진탐색은 시작점, 끝점, 중간점을 이용해 탐색 범위를 설정한다.) |
단계마다 탐색 범위를 2로 나누는 것과 동일하기 때문에 연산횟수는 log2N에 비례하고, 시간 복잡도는 O(logN)을 보장한다.
// 이진 탐색 소스코드 구현(반복문)
public static int binarySearch(int[] arr, int target, int start, int end) {
while(start<=end) { // start가 end보다 크면 배열에 데이터가 없다는 뜻!
int mid = (start+end) / 2;
if(arr[mid]==target) return mid; // 원하는 값을 찾은 경우 중간점 인덱스 반환
else if (arr[mid]<target) end = mid - 1; // 중간점의 값보다 찾는 값이 작을 때 왼쪽 확인
else end = mid + 1; // 중간점의 값보다 찾는 값이 클 때 오른쪽 확인
}
return -1; // 해당 값을 찾지 못했다면 -1 반환
}
1. 이진 트리(binary tree)
- 노드가 왼쪽 자식과 오른쪽 자식을 갖는 트리, 각 노드의 자식은 2명 이하만 유지해야 한다.
2. 완전이진트리(complete binary tree)
- 루트부터 노드가 채워져 있으면서 같은 레벨에서는 왼쪽에서 오른쪽으로 노드가 채워져 있는 이진트리
마지막 레벨을 제외한 레벨은 노드를 가득 채운다. | 마지막레벨은 왼쪽부터 오른쪽 방향으로 노드를 채우되 반드시 끝까지 채울 필요는 없다. (하지만 왼쪽은 꼭) |
3. 이진 검색 트리(binary search tree)
이진검색트리의 조건
- 어떤 노드 N을 기준으로 왼쪽 서브 트리 노드의 모든 키 값은 노드 N의 키 값보다 작아야 한다. - 오른쪽 서브 트리 노드의 키 값은 노드 N의 키 값보다 커야 한다. - 같은 키 값을 갖는 노드는 없다.(중복 xx) |
이진 검색 트리 만들기
1) 노드 클래스 Node<K,V>
- 이진 검색트리의 개별 노드를 나타내는 것이 클래스 Node<K,V>이며, 4개의 필드로 구성된다.
key | 키 값(탐색을 위해서 찾을 자료를 식별할 수 있는 유일한 값) |
data | 데이터 |
left | 왼쪽 자식 노드에 대한 참조(왼쪽 포인터) |
right | 오른쪽 자식 노드에 대한 참조(오른쪽 포인터) |
static class Node<K, V> { // <Key, Value>
// data를 저장할 변수, key를 저장할 변수, 왼쪽 노드를 저장할 변수, 오른쪽 노드를 저장할 변수
K key;
V data;
Node<K, V> left;
Node<K, V> right;
}
노드 클래스 Node<K,V>에는 1개의 생성자와 3개의 메서드가 있다.
생성자 | 각 필드에 넣어둘 4개의 값을 전달받아 그대로 설정 |
getKey 메서드 | 키 값을 그대로 반환 |
getValue 메서드 | 데이터 data 그대로 반환 |
print 메서드 | 데이터 data를 출력하는 메서드 |
// 객체 1개를 저장할 노드클래스
static class Node<K, V> {
// 외부에서 접근하지 못하도록 접근제어자를 private으로 설정
private K key;
private V data;
private Node<K, V> left;
private Node<K, V> right;
// 생성자
Node(K key, V data, Node<K, V> left, Node<K, V> right) {
this.key = key;
this.data = data;
this.left = left;
this.right = right;
}
// 데이터가 들어올 땐 생성자 메서드로 멤버변수가 캡슐화가 되어있어서 get으로 불러와야 함
// 키 값을 반환
K getKey() {
return key;
}
// 데이터 값을 반환
V getValue() { // 여러 데이터가 들어옴
return data;
}
// 출력
void print() { // 데이터를 출력
System.out.println(data);
}
}// Node
2) 이진검색트리 클래스 BinTree<K,V>
root | 루트에 대한 참조를 보존, 유지하는 필드 |
Comparable | 키 값의 대소 관계를 비교하는 비교자( comparable은 compareTo 메서드로 구현) (원래는 compareTo는 별도의 대상기준이 필요없지만 밑의 코드에는 한 개의 매개변수를 Comparable로 형변환하고 사용) |
private Node<K, V> root; // 루트 노드 (전역변수 root)
public BinaryTree() {
root = null; // 루트노드 초기화
}
public int comp(K key1, K key2) { // key1과 key2를 비교(key1 - key2)
// comparable의 compareTo를 사용해서 구현함
return ((Comparable<K>)key1).compareTo(key2);
}
BinaryTree( )
루트에 대한 참조인 root를 null로 하여 노드가 하나도 없는(비어 있는) 이진 검색트리를 생성하는 생성자
comp( ) 두 키값을 비교하는 comp 메서드
- 이 메서드는 두 키 값 key1과 key2를 비교해 아래 값을 반환(key1 - key2)
- 생성자 BinaryTree( )로 이진트리검색을 생성한 경우 root 값은 null이고, 비교자는 설정되어 있지 않아서 key1을 Comparable<K>인터페이스형으로 형변환 하고 compareTo 메서드 호출
key1 > key2면 양수 | key1 < key2 음수 | key 1 == key2 0 |
- 큰 값에서 작은 값을 빼니까 양수, 작은 값에서 큰 값을 빼니까 음수, 같으면 0
키 값으로 검색하는 search메서드
- 탐색은 항상 루트부터 선택하여 검색을 진행한다. 키 값 p와 루트 노드의 키 값을 비교한다.
- 이진검색트리에서 원하는 값을 찾으려면, 루트부터 시작해 현재 선택한 노드의 키 값과 목표하는 값을 비교하면서 왼쪽, 오른쪽 검색을 진행하면 된다.
1. 루트부터 선택해 검색을 진행한다. 여기서 선택한 노드는 P이다. 2. P가 null이면 검색에 실패한다. 3. 검색하는 값 key와 선택한 노드 p의 키 값을 비교하여 3.1 값이 같으면 검색에 성공(검색 종료) 3.2 key가 작으면 선택한 노드에 왼쪽 자식 노드를 대입한다.(왼쪽으로 검색 진행) 3.3 key가 크면 선택한 노드에 오른쪽 자식 노드를 대입한다.(오른쪽으로 검색 진행) 4. 일치하는 값을 찾을 때까지 절차를 반복하고 검색하는 값이 없으면 null 반환 |
// 노드 검색
public V search(K key) {
Node<K, V> point = root; // 현재 point 노드는 root
while(true) {
if(point == null) {
return null; // 노드가 없을 때, 검색 실패
}
// 노드 point의 키과 매개변수 key를 비교한 값을 result 변수에 저장(0, +, -)
int result = comp(point.getKey(), key);
if(result==0) { // key와 같은 정보를 가진 Data검색됨(같으면)
return point.getValue(); 검색 성공!
}else if(result>0) { // 양수이면 point.getKey()가 크고, 매개변수 key가 작다.
point = point.left; // 왼쪽 서브트리에서 검색
}else { // 음수이면 point.getKey()가 작고, 매개변수 key가 크다
point = point.right; // 오른쪽 서브트리에서 검색
}
}
}
키 값이 key인 노드를 검색하고 검색에 성공하면 그 노드의 데이터에 대한 참조를 반환
노드를 삽입하는 add 메서드
- 원소를 삽입하려면 먼저 이진 탐색 트리에 같은 원소가 있는지를 확인하기 위해 탐색을 해야한다.
- 이진검색트리에서 원하는 값을 찾으려면, 루트부터 시작해 현재 선택한 노드의 키 값과 목표하는 값을 비교하면서 왼쪽, 오른쪽 검색을 진행한다.
1. 루트부터 선택한다. 여기서 선택한 노드는 node이다. 2. 삽입할 키 key와 선택 노드 node의 키 값을 비교한다. 값이 같다면 삽입에 실패한다. 2.1 값이 같지 않을 때 key값이 삽입할 값보다 작으면 왼쪽 자식이 없는 경우에 노드를 삽입한다.( 노드를 삽입하고 종료) 왼쪽 자식 노드가 있는 경우에 선택한 노드룰 왼쪽 자식노드로 한다.(기준점이 왼쪽서브트리의 자식노드로) 2.2 key값이 삽입할 값보다 크면 오른쪽 자식 노드가 없는 경우에 노드를 삽입한다.(노드를 삽입하고 종료) 오른쪽 자식 노드가 있는 경우에 선택한 노드를 오른쪽 자식 노드로 한다.(기준점이 오른쪽서브트리의 오른쪽노드로) 3. 일치하는 값을 찾을 때까지 2번 절차를 반복한다. |
// node를 루트로 하는 서브 트리에 노드 <K, V>를 삽입 (위치를 찾아서 노드추가)
public void addNode(Node<K,V> node, K key, V data) {
int result = comp(key, node.getKey()); // 삽입하려는 key값이 더 크면 node의 오른쪽, 아니면 왼쪽
if(result == 0) {// key가 이진검색트리에 이미 있는 키값일때
return;
}else if(result<0) { // node의 있는 값보다 작아서 왼쪽 노드 검색
if(node.left==null) { // node의 왼쪽이 비어있으면 새로운 노드를 왼쪽에 추가
node.left = new Node<K,V>(key, data, null, null);
}else {
addNode(node.left, key, data); // 왼쪽 서브 트리를 중심으로 다시 비교
}
}else { // node의 있는 값보다 커서 오른쪽 노드 검색
if(node.right==null) { // node의 오른쪽이 비었으면 새로운 노드를 오른쪽에 추가
node.right = new Node<K,V>(key, data, null, null);
}else {
addNode(node.right, key, data); // 오른쪽 서브 트리를 중심으로 다시 비교
}
}
}
// 노드를 삽입(노드가 하나도 없는 경우)
public void add(K key, V data) {
if(root==null) { // 루트가 비어있으면 노드를 만들어 data를 root에 대입한다.
root = new Node<K,V>(key, data, null, null);
}else { // 루트가 비어있지 않으면 key를 이용해 위치를 찾아서 추가
addNode(root, key, data);
}
}
new Node<K,V>(key, data, null, null)
- 키 값이 key, 데이터가 data, 왼쪽 포인터와 오른쪽 포인터가 모두 null인 노드를 생성하고, root가 그것을 참조하도록 한다. (root는 '루트 자체'가 아니라 루트에 대한 참조)
addNode(root, key, data);
- 트리가 비어있지 않은 상태이므로 메서드 addNode( )를 호출해 노드를 삽입한다.
- addNode는 node를 루트로 하는 서브트리에 키 값이 key이고 데이터가 data인 노드를 삽입하는 메서드이다.
노드를 삭제하는 remove 메서드
- 원소를 삭제하는 경우 자식 노드의 수에 따라 세 가지 경우가 있다. 각각의 상황에 알맞은 처리를 해야한다.
- 노드를 삭제한 후에도 이진 탐색트리를 유지해야 한다.
1) 삭제할 노드가 리프 노드일 경우(자식 노드가 없는 노드를 삭제하는 경우)
- 삭제할 노드가 부모 노드의 왼쪽 자식이면 부모의 왼쪽 포인터를 null로 한다.
- 삭제할 노드가 부모 노드의 오른쪽 자식이면 부모의 오른쪽 포인터를 null로 한다.
2) 삭제할 노드가 한 개의 자식 노드를 가진 경우 (자식 노드가 1개인 노드를 삭제하는 경우)
- 삭제 대상 노드가 부모 노드의 왼쪽 자식이면 부모의 왼쪽 포인터가 삭제 대상 노드의 자식을 가리키도록 한다.
- 삭제 대상 노드가 부모 노드의 오른쪽 자식이면 부모의 오른쪽 포인터가 삭제 대상 노드의 자식을 가리키도록 한다.
3) 삭제할 노드가 두 개의 자식 노드를 가진 경우 (자식 노드가 2개인 노드를 삭제하는 경우)
- 노드를 삭제하고 나면 부모노드의 자리를 자식노드에게 물려줄 때 왼쪽, 오른쪽 중 어느쪽에 물려줄지 생각해야한다.
- 왼쪽 서브트리에서 가장 큰 자손 노드 또는 오른쪽 서브트리에서 가장 작은 자손 노드가 삭제할 노드의 자리에 올 수 있다.
1. 삭제할 노드의 왼쪽 서브 트리에서 키 값이 가장 큰 노드를 검색한다. 2. 검색한 노드를 삭제 위치로 옮긴다.(검색한 노드의 데이터를 삭제 대상 노드의 위치로 복사한다.) 3. 옮긴 노드를 삭제한다. 이 때 3.1 옮긴 노드에 자식이 없으면 '자식 노드가 없는 노드의 삭제 순서(1번)'에 따라 노드를 삭제한다. 3.2 옮긴 노드에 자식이 1개만 있으면 '자식 노드가 1개 있는 노드의 삭제 순서(2번)'에 따라 노드를 삭제한다. |
1. 삭제할 노드의 오른쪽 서브 트리에서 키 값이 가장 작은 노드를 검색한다. 2. 검색한 노드를 삭제 위치로 옮긴다.(검색한 노드의 데이터를 삭제 대상 노드의 위치로 복사한다.) 3. 옮긴 노드를 삭제한다. 이 때 3.1 옮긴 노드에 자식이 없으면 '자식 노드가 없는 노드의 삭제 순서(1번)'에 따라 노드를 삭제한다. 3.2 옮긴 노드에 자식이 1개만 있으면 '자식 노드가 1개 있는 노드의 삭제 순서(2번)'에 따라 노드를 삭제한다. |
예제) 키 값이 key인 노드를 삭제한다. 이 예제에서는 왼쪽 서브트리의 가장 큰 값을 선택한다.
* 삭제할 키를 검색한다. 검색에 성공하면 point는 찾은 노드를 참조하고, parent는 찾은 노드의 부모 노드를 참조한다. *
public boolean remove(K key) {
Node<K,V> point = root; // 현재 탐색하는 point(root부터 탐색)
Node<K,V> parent = null; // 스캔중인 노드의 부모노드(부모노드 저장용)
boolean isLeftChild = true; // 왼쪽, 오른쪽노드인지의 정보가 필요함, 왼쪽 자식 노드이면 true, 오른쪽 자식 노드이면 falsee
//삭제할 키를 검색
while(true) {
if(point==null) { // point가 null이면 노드가 존재하지 않는다.
return false; // 키 값이 없다.
}
// 삭제할 노드 찾기( 매개변수 key와 point k의 키 값을 비교
int cond = comp(key, point.getKey()); // 양수 -> 오른쪽 검색(key가 더 크다), 음수 -> 왼쪽 검색(key가 더 작다)
if(cond==0) { // key와 노드 point의 key값이 같으면 검색 성공!
break;
}else {
parent = point; // 가지로 내려가기 전에 부모노드로 설정
if(cond<0) { // key가 더 작으면 왼쪽 서브 트리에서 검색
point = point.left; // 왼쪽 값을 point에 저장
isLeftChild = true;
}else { // key가 더 크면 오른쪽 서브 트리에서 검색
point = point.right; // 다음노드(오른쪽의 다음노드)
isLeftChild = false;
}
}
}
* 삭제할 노드의 자식이 없을 때와 자식 노드가 1개인 노드를 삭제하는 경우 *
- 삭제할 노드의 왼쪽 자식이 없을때 - 삭제할 노드의 오른쪽 자식이 없을 때
부모노드(parent): 찾는 노드가 연결되어 있는 부모 노드 자식노드(point.right와 point.left) : 찾는 노드가 연결되어 있는 자식 노드 (삭제할 노드의 왼쪽 자식이 없을 때와 오른쪽 자식이 없을 때로 나뉨) |
// point 삭제할 노드(찾은 노드), parent는 point의 부모노드
// left자식 노드가 없을 때 삭제할 노드의 right를 부모노드의 left에 대입한다.
if(point.left == null && point.right == null) { // 삭제할 노드의 자식이 없을 때
if(point == root) root = null;
else if(isLeftChild) {
parent.left = null;
}else {
parent.right = null;
}
}else if(point.left == null) { // 삭제할 노드(찾은 노드)의 왼쪽자식이 없다.
if(point == root) { // 찾은 노드가 root를 가리키고 있다면
root = point.right; // 삭제할 노드의 오른쪽 자식노드를 루트로
}else if(isLeftChild) { // 삭제할 노드가 부모노드의 left이면
parent.left = point.right; // 삭제할 노드의 오른쪽 자식을 부모의 왼쪽 포인터와 연결
}else { // 삭제할 노드가 부모노드의 right이면
parent.right = point.right; // 삭제할 노드의 오른쪽 자식을 부모의 오른쪽 포인터와 연결
}
}else if(point.right == null) { // 삭제한 노드(찾은 노드)의 오른쪽자식이 없다.
if(point == root) { // 찾은 노드가 root를 가리키고 있다면
root = point.left; // 삭제할 노드의 왼쪽 자식을 루트와 연결
}else if(isLeftChild) { // 삭제할 노드가 부모노드의 left이면
parent.left = point.left; // 삭제할 노드의 왼쪽 자식을 부모의 왼쪽 포인터와 연결
}else { // 삭제할 노드가 부모노드의 right이면
parent.right = point.left; // 삭제할 노드의 왼쪽 자식을 부모의 오른쪽 포인터와 연결
}
parent.left = point.right : 부모의 왼쪽 포인터가 삭제할 노드의 오른쪽 자식을 가리킴
parent.right = point.right : 부모의 오른쪽 포인터가 삭제할 노드의 오른쪽 자식을 가리킴
parent.left = point.left : 부모의 왼쪽 포인터가 왼쪽 자식을 가리킴
parent.right = point.left : 부모의 오른쪽 포인터가 왼쪽 자식을 가리킴
* 삭제할 노드(찾은 노드)의 왼쪽 자식, 오른쪽 자식이 둘 다 있는 경우 *
}else { // 삭제할 노드(찾은 노드)의 왼쪽 자식과 오른쪽 자식이 둘 다 있다.
// point의 자식들 중 왼쪽 서브트리에서 가장 큰 값을 찾기
parent = point; // 삭제하려는 노드(찾은 노드)를 parent와 연결
Node<K,V> maxNode = point.left; // 왼쪽 서브 트리를 maxNode로 연결(왼쪿서브트리에서 큰값을 찾아야 해서)
isLeftChild = true;
// 삭제할 노드의 왼쪽 서브트리에서 키 값 가장 큰 노드 maxNode를 검색
// 키 값이 큰 노드를 선택하려면 트리의 구조의 조건에 의해서 오른쪽 노드를 검색한다.
while(maxNode.right!=null) {// 왼쪽 서브트리의 오른쪽의 리프 노드까지 가면 while문 빠져나옴
parent = maxNode; // while문이 끝나면 parent에 maxNode.right값이 들어가게 된다.
maxNode = maxNode.right;
isLeftChild = false;
}
// 후계 노드 값 복사(삭제할 노드의 값을 후계 노드의 값으로 변경)
// 후계 노드 값은 왼쪽 서브트리에서 가장 큰 값(maxNode)
point.key = maxNode.key; // maxNode의 키 값을 삭제 대상 노드 위치로 복사
point.data = maxNode.data; // maxNode의 데이터를 삭제 대상 노드 위치로 복사
// 옮긴 노드(maxNode)에 자식이 1개 있다면
if(isLeftChild) { // 삭제할 노드가 maxNode의 left이면
parent.left = maxNode.left; // 기존의 maxNode의 왼쪽 자식을 자신의 왼쪽 포인터와 연결
}else { // 삭제할 노드가 maxNode의 right이면
parent.right = maxNode.left; // 기존의 maxNode의 왼쪽 자식을 자신의 오른쪽 포인터와 연결
}
}
return true;
}
모든 노드를 출력하는 print 메서드
- 모든 노드의 키 값을 오름차순으로 출력하는 메서드
- 오름차순으로 출력하기 위해 중위 순회 방법으로 트리를 검색
// node를 루트로 하는 서브 트리의 노드를 키 값의 오름차순으로 출력
public void subNodePrint(Node node) {
if(node != null) {
subNodePrint(node.left); // 왼쪽 서브트리를 루트로 해 키 값의 오름차순으로 출력
System.out.println(node.getKey()+ " " +node.data); // node를 출력
subNodePrint(node.right); // 오른쪽 서브트리를 루트로 해 값의 오름차순으로 출력
}
}
// 모든 노드를 키 값의 오름차순으로 출력
public void print() {
if(root==null) { // 노드가 비어있을 때
System.out.println("등록된 회원이 없습니다.");
}else {
subNodePrint(root); // root를 매개변수로 해 printSubTree 메서드 호출
}
}
-> printSubTree 메서드는 node를 루트로 하는 서브 트리의 노드를 키 값의 오름차순으로 출력하기 위한 재귀메서드이다.
-> 이진 검색 트리를 중위 순회(Inorder)하면 키 값의 오름차순으로 노드를 얻을 수 있다.
<전체 코드>
package al05_binary_tree_search;
import java.util.Scanner;
public class BinarySearchTreeTest {
static Scanner s = new Scanner(System.in);
// 데이터를 담을 객체 (데이터가 여러개)
static class Data {
private Integer no; // 입력받은 번호
private String name; // 입력받은 이름
final int NO = 1; // 번호입력받을지 확인 (메서드를 한 개만 가지고 출력해서)
final int NAME = 2; // 이름입력받을지 확인
// 번호를 얻을 수 있는 메서드(입력받은 번호를 키로 사용해서 번호에 관련한 정보를 가져온다)
Integer getKeyCode() {
return no;
}
// 이름을 가져오는 메서드
public String toString() {
return name;
}
// 데이터를 입력받는 메서드
public void inData(String msg, int sw) {
System.out.println(msg + "입력하세요");
if ((sw & NO) == NO) { // 입력받아서 no에 받음
System.out.print("번호 : ");
no = Integer.parseInt(s.nextLine()); // 엔터 문제 떄문에
}
if ((sw & NAME) == NAME) {
System.out.print("이름 : ");
name = s.nextLine();
}
}
} // Data
enum Menu {
ADD("회원추가"),
REMOVE("삭제"),
SEARCH("검색"),
PRINT("출력"),
TERMINATE("종료");
private final String message;
// 생성자
Menu(String msg) {
message = msg;
}
// getter
String getMessage() {
return message;
}
// 사용자가 입력한 메뉴객체를 리턴하는 메서드 (idx는 콘솔에서 입력받은 값)
static Menu menuAt(int idx) {
for(Menu m : Menu.values()) {
if(m.ordinal()==idx) { // 입력받은 값이 열거형의 idx와 같으면 출력
return m;
}
}
return null; // 메뉴를 잘못입력함
}
} // Menu
// 메뉴를 출력하고 사용자에게 메뉴를 입력받은 메서드
// 반환형: 선택한 메뉴 객체
static Menu selectMenu() {
int menuNo;
do { // 메뉴를 잘못입력하면 다른 메뉴를 입력받기 위해서 반복을 실행한다.
for(Menu m: Menu.values()) { // 메뉴출력
System.out.printf("%d.%s ", m.ordinal()+1, m.getMessage());
}
System.out.print("-> ");
menuNo = Integer.parseInt(s.nextLine())-1;
} while(menuNo<Menu.ADD.ordinal() || menuNo>Menu.TERMINATE.ordinal());
return Menu.menuAt(menuNo);
}
public static void main(String[] args) {
Menu m;
BinaryTree<Integer, Data> tree = new BinaryTree<Integer, Data>();
Data data;
// 메뉴 입력 받기
do {
m = selectMenu(); // 사용자 선택한 메뉴
switch(m) {
case ADD: // 번호, 이름을 입력받아 Node -> Tree에 저장
data = new Data();
data.inData("회원정보를", data.NO | data.NAME); // 회원정보와 3이라는 값이 넘어온다.(입력끝)
// 새로운 노드 추가하기
tree.add(data.getKeyCode(), data);
break;
case REMOVE:
data = new Data();
data.inData("삭제할 key", data.NO);
boolean result = tree.remove(data.getKeyCode());
break;
case SEARCH:
data = new Data();
data.inData("검색할 key", data.NO); // key가 저장
//검색한 결과를 리턴받음
Data searchData= tree.search(data.getKeyCode());
if(searchData==null) {
System.out.println("해당키의 회원정보가 없습니다.");
}else if(searchData!=null) { // 검색한 결과가 있을 때
System.out.println("번호: " + searchData.getKeyCode() + ", 이름:" + searchData);
}
break;
case PRINT:
tree.print();
break;
}
} while(m != Menu.TERMINATE); // 프로그램이 종료됨
}
}
2022.01.10 - [멀티캠퍼스 풀스택 과정/Java의 정석] - 자바의 정석8-4 Arrays, Comparator와 Comparable
2022.01.25 - [멀티캠퍼스 풀스택 과정/알고리즘] - 알고리즘1-2. 검색 알고리즘(선형검색, 이진검색)
'멀티캠퍼스 풀스택 과정 > 알고리즘' 카테고리의 다른 글
알고리즘1-3. 스택(Stack) (0) | 2023.03.29 |
---|---|
소수판별 알고리즘 (0) | 2022.01.28 |
알고리즘2-1. 선형리스트(LinkedList 연결리스트) (0) | 2022.01.27 |
알고리즘1-6. 정렬(Sorting): 버블정렬, 선택정렬, 삽입정렬, 퀵정렬 (0) | 2022.01.26 |
알고리즘1-5. 재귀알고리즘 (0) | 2022.01.26 |