1. 힙 (Heap)

  • 데이터에서 최대값과 최소값을 빠르게 찾기 위해 고안된 완전 이진 트리 (Complete Binary Tree)
    • 완전 이진 트리 : 노드를 삽일할 때 최하단 왼쪽 노드부터 차례대로 삽입하는 트리

힙 사용 이유

  • 배열에 데이터를 넣고, 최대값과 최소값을 찾으려면 O(n)이 걸림
  • 이에 반해, 힙에 데이터를 넣고, 최대값과 최소값을 찾으면 O(logn)
  • 우선순위 큐와 같이 최대값 또는 최소값을 빠르게 찾아야 하는 자료구조 및 알고리즘 구현 등에 활용

 

2. 힙 (Heap) 구조

  • 힙은 최대값을 구하기 위한 구조 (최대힙, Max Heap) 와, 최소값을 구하기 위한 구조 (최소힙, Min Heap) 로 분류
  • 힙은 다음과 같은 조건을 가지고 있는 자료구조임
    • 각 노드의 값은 해당 노드의 자식 노드가 가진 값보다 크거나 같다. (최대힙의 경우)
    • 최소힙의 경우는 각 노드의 값은 해당 노드의 자식 노드가 가진 값보다 작거나 같다.
    • 완전 이진 트리 형태를 가짐

3. 힙과 이진 탐색 트리의 공통점과 차이점

공통점

  • 이진 트리임

차이점

  • 힙은 각 노드의 값이 자식 노드보다 크거나 같음 (최대힙의 경우)
  • 이진 탐색 트리는 왼쪽 자식 노드의 값이 가장 작고 그 다음 부모 노드 그 다음 오른쪽 자식 노드 값이 가장 큼
  • 힙은 이진 탐색 트리의 조건이 자식 노드에서 작은 값은 왼쪽, 큰 값은 오른쪽이라는 조건은 없음
  • 힙의 왼쪽 밑 오른쪽 자식 노드의 값은 오른쪽이 클 수도 있고, 왼쪽이 클 수도 있음

이진 탐색 트리는 탐색을 위한 구조, 힙은 최대/최소값 검색을 위한 구조 중 하나로 이해

 

출처 : https://fastcampus.co.kr/courses/210773/clips/

 

4. 힙 (Heap) 구현

  • 배열 이용해서 데이터를 힙 구조에 삽입, 삭제
  • root 노드 인덱스 번호를 1로 지정하면 수월함
    • 부모 노드 인덱스 번호 = 자식 노드 인덱스 번호 / 2
    • 왼쪽 자식 노드 인덱스 번호 = 부모 노드 인덱스 번호 * 2
    • 오른쪽 자식 노드 인덱스 번호 = 부모 노드 인덱스 번호 * 2 + 1
public class Heap {
    private ArrayList<Integer> heap;
    
    public init() {
    	heap = new ArrayList<Integer>();
        heap.add(0); // 0번째 인덱스 사용 안함
    }
    
    public Boolean moveUp(Integer index) {
    	if (index <= 1) {
            return false;
        }
        Integer parentIndex = index / 2;
        if (heap.get(index) > heap.get(parentIndex)) {
            return true;
        } else {
            return false;
        }
    }
    
    public Boolean insert(Integer data) {
    	if (heap.size() == 0) {
            init();
            return true;
        }
        
        heap.add(data);
        
        Integer index = heap.size() - 1;
        while (this.moveUp(index)) {
            Integer parentIndex = index / 2;
            Integer parentData = heap.get(parentIndex); // 부모 노드의 값
            heap.set(parentIndex, data);
            heap.set(index, parentData);
            index = parentIndex;
        }
        return true;
    }
    
    public Boolean moveDown(Integer index) {
    	Integer leftChildIndex = index * 2;
        Integer rightChildIndex = index * 2 + 1;
        
        if (leftChildIndex >= heap.size()) { // 1. 왼쪽 자식 노드도 없을 때
            return false;
        } else if (rightChildIndex >= heap.size()) {  // 2. 오른쪽 자식 노드만 없을 때
            if (heap.get(index) < heap.get(leftChildIndex)) {
            	return true;
            } else {
            	return false;
            }
        } else { // 3. 왼쪽, 오른쪽 자식 노드 모두 있을 때
            if (heap.get(leftChildIndex) > heap.get(rightChildIndex)) {
                if (heap.get(index) < heap.get(leftChildIndex)) {
                    return true;
                } else {
                    return false;
                }
            } else {
            	if (heap.get(index) < heap.get(rightChildIndex)) {
                    return true;
                } else {
                    return false;
                }
            }
        }
    }
    
    public Integer pop() {
    	if (heap.size() <= 1) {
        	return null;
        }
        
        Integer popData = heap.get(1);
        heap.set(1, heap.get(heap.size() - 1);
        heap.remove(heap.size() - 1);
        
        Integer index = 1;
        
        while (this.moveDown(index)) {
            Integer leftChildIndex = index * 2;
            Integer rightChildIndex = index * 2 + 1;
            
            if (rightChildIndex >= heap.size()) { // 2. 오른쪽 자식 노드만 없을 때
            	if (heap.get(index) < heap.get(leftChildIndex)) {
                    Integer leftChildData = heap.get(leftChildIndex);
                    heap.set(leftChildIndex, heap.get(index));
                    heap.set(index, leftChildData);
                }
            } else { // 3. 왼쪽, 오른쪽 자식 노드 모두 있을 때
            	if (heap.get(leftChildIndex) > heap.get(rightChildIndex)) {
                    if (heap.get(index) < heap.get(leftChildIndex)) {
                        Integer leftChildData = heap.get(leftChildIndex);
                        heap.set(leftChildIndex, heap.get(index));
                        heap.set(index, leftChildData);
                    }
            	} else {
                    if (heap.get(index) < heap.get(rightChildIndex)) {
                        Integer rightChildData = heap.get(rightChildIndex);
                        heap.set(rightChildIndex, heap.get(index));
                        heap.set(index, rightChildData);
                    }
                }
            }
        }
        return popData;
    }
    
	public 
}

 

5. 힙 (Heap) 시간 복잡도

  • O(logn)

'Algorithm > 자료구조' 카테고리의 다른 글

트리  (0) 2023.01.16
해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20

1. 트리 (Tree) 구조

트리 : Node와 Branch를 이용해서 사이클을 이루지 않도록 구성한 데이터 구조

실제로 어디에 많이 사용되나? 트리 중 이진 트리 (Binary Tree) 형태의 구조로, 탐색(검색) 알고리즘 구현을 위해 많이 사용됨

 

2. 알아둘 용어

  • Node : 트리에서 데이터를 저장하는 기본 요소 (데이터와 다른 연결된 노드에 대한 Branch 정보 포함)
  • Root Node : 트리 맨 위에 있는 노드
  • Level : 최상위 노드를 Level 0으로 하였을 때, 하위 Branch로 연결된 노드의 길이를 나타냄
  • Parent Node : 어떤 노드의 다음 레벨에 연결된 노드
  • Child Node : 어떤 노드의 상위 레벨에 연결된 노드 
  • Leaf Node (Terminal Node) : Child Node가 하나도 없는 노드
  • Sibling (Brother Node) : 동일한 Parent Node를 가진 노드
  • Depth : 트리에서 Node가 가질 수 있는 최대 Level

출처 :&nbsp;https://fastcampus.co.kr/courses/210773/clips/

3. 이진 트리와 이진 탐색 트리 (Binary Search Tree)

  • 이진 트리 : 노드의 최대 Branch가 2인 트리
  • 이진 탐색 트리 (Binary Search Tree, BST) : 이진 트리에서 다음과 같은 추가적인 조건이 있는 트리
    • 왼쪽 노드는 해당 노드보다 작은 값, 오른쪽 노드는 해당 노드보다 큰 값을 가지고 있음!

 

4. 자료 구조 이진 탐색 트리의 장점과 주요 용도

주요 용도 : 데이터 검색 (탐색)

장점 : 탐색 속도를 개선할 수 있음

단점 : 

 

이진트리와 정렬된 배열간의 탐색 비교

              21              
      14               28      
  11       18       25       32  
5   12   15   19   23   27   30   37

 

5 11 12 14 15 18 19 21 23 25 27 28 30 32 37

 

5. 링크드 리스트 구현하기

public class Node {
    private Integer value;
    public Node left;
    public Node right;
    
    public Node(Integer value) {
    	this.value = value;
        this.left = null;
        this.right = null;
    }
    
    public Node(Integer value, Node right) {
    	this.value = value;
        this.right = right;
    }
    
     public Integer getValue() {
        return this.data;
    }
}

public class NodeMgmt() {
	private Node head; //root Node
    
    public void init(Integer value) {
    	head = new Node(value);
    }
    
    public void insert(Integer value) {
        Node node = head;
        while(true) {
            if (value < node.getValue()) {
                if (node.left != null) {
                    node = node.left;
                } else {
                    node.left = new Node(value);
                    break;
                }
            } else {
            	if (node.right != null) {
                	node = node.right;
                } else {
                	node.right = new Node(value);
                    break;
                }
            }
        }
    }
    
    public Boolean search(Integer value) {
    	Node node = head;
        while (node) {
            if (node.getValue() == value) {
            	return true;
            } else if (value < node.getValue()) {
            	node = node.left;
            } else {
            	node = node.right;
            }
        }
        return false;
    }
    
    public Boolean delete(Integer value) {
    	Boolean searched = false;
    	Node node = head;
        Node parent = head;
        while (node) {
        	if (node.getValue() == value) {
            	searched = true;
                break;
            } else if (value < node.getValue()) {
            	parent = node;
            	node = node.left;
            } else {
            	parent = node;
                node = node.right;
            }
        }
        
        if (searched == false) {
        	return false;
        }
        
        // 1. 삭제할 Node가 Leaf Node인 경우
        if (node.left == null && node.right == null) {
        	if (value < parent.getValue()) {
            	parent.left = null;
            } else {
            	parent.right = null;
            }
        }
        
        // 2. 삭제할 Node가 Child Node를 한 개 가지고 있을 경우
        if (node.left != null && node.right == null) {
        	if (value < parent.getValue()) {
            	parent.left = node.left;
            } else {
            	parent.right = node.left;
            }
        } else if (node.left == null && node.right != null) {
        	if (value < parent.getValue()) {
            	parent.left = node.right;
            } else {
            	parent.right = node.right;
            }
        }
        
        // 3. 삭제할 Node가 Child Node를 두 개 가지고 있을 경우
        if (node.left != null && node.right != null) {
        	if (value < parent.getValue()) {
            	Node changeNode = node.right;
                Node changeNodeParent = node.right;
                while (changeNode.left != null) {
                	changeNodeParent = changeNode;
                	changeNode = changeNode.left;
                }
                if (changeNode.right != null) {
                	changeNodeParent.left = changeNode.right;
                } else {
                	changeNodeParent.left = null;
                }
                parent.left = changeNode;
                changeNode.left = node.left;
                changeNode.right = node.right;
            } else {
            	Node changeNode = node.right;
                Node changeNodeParent = node.right;
                while (changeNode.left != null) {
                	changeNodeParent = changeNode;
                    changeNode = changeNode.left;
                }
                if (changeNode.right != null) {
                	changeNodeParent.left = changeNode.right;
                } else {
                	changeNodeParent.left = null;
                }
                parent.right = changeNode;
                changeNode.left = node.left;
                changeNode.right = node.right;
            }
        }
    	return true;
    }
    
    

}

 

6. 이진 탐색 트리의 시간 복잡도와 단점

6-1 시간 복잡도 (탐색시)

  • depth (트리의 높이) 를 h라고 표기한다면, O(h)
  • n개의 노드를 가진다면, h = log2n에 가까우므로, 시간 복잡도는 O(logn)
    • 빅오 표기법에서 logn에서의 log의 밑은 10이 아니라 2
    • 한번 실행시마다 50%의 실행할 수도 있는 명령을 제거한다는 의미, 즉 50%의 실행시간을 단축시킬 수 있음을 의미

6-2 이진 탐색 트리 단점

  • 평균 시간 복잡도는 O(logn) 이지만 최악의 경우는 링크드 리스트 등과 동일한 성능을 보임 O(n)

'Algorithm > 자료구조' 카테고리의 다른 글

힙 (Heap)  (0) 2023.01.16
해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20

대표적인 데이터 구조6 : 해쉬 테이블 (Hash Table)

 

1. 해쉬 구조

Hash Table : Key에 Value를 저장하는 데이터 구조

Key를 통해 바로 데이터를 받아올 수 있으므로, 속도가 획기적으로 빨라짐

배열로 미리 해쉬 테이블 사이즈만큼 생성 후에 사용 (공간과 탐색 시간을 맞바꾸는 기법)

 

2. 알아둘 용어

  • 해쉬(Hash) : 임의 값을 고정 길이로 변환하는 것
  • 해쉬 테이블(Hash Table) : 키 값의 연산에 의해 직접 접근이 가능한 데이터 구조
  • 해싱 함수(Hashing Function) : Key에 대해 산술 연산을 이용해 데이터 위치를 찾을 수 있는 함수
  • 해쉬 값(Hash Value) 또는 해쉬 주소(Hash Address) : Key를 해싱 함수로 연산해서 해쉬 값을 알아내고 이를 기반으로 해쉬 테이블에서 해당 Key에 대한 데이터 위치를 일관성 있게 찾을 수 있음
  • 슬록(Slot) : 한 개의 데이터를 저장할 수 있는 공간
  • 저장할 데이터에 대해 Key를 추출할 수 있는 별도 함수도 존재할 수 있음 

 

3. 해쉬 테이블의 장단점과 주요 용도

장점

  • 데이터 저장/읽기 속도가 빠름 (검색 속도가 빠름)
  • 해쉬는 키에 대한 데이터가 있는지(중복) 확인이 쉬움

단점

  • 일반적으로 저장공간이 좀더 많이 필요함
  • 여러 키에 해당하는 주소가 동일할 경우 충돌을 해결하기 위한 별도 자료구조가 필요함

용도

  • 검색이 많이 필요한 경우
  • 저장, 삭제, 읽기가 빈번한 경우
  • 캐쉬 구현시 (중복 확인이 쉽기 때문)

 

4. 예시

import java.util.HashMap;

public class HashMapEx {
	public static void main(String[] args) {
            HashMap<String, String> map = new HashMap<>();
            map.put("A", "apple");
            map.put("B", "banana");
            map.put("C", "carrot");

            System.out.println(map.get("A"));
            System.out.println(map.get("B"));
            System.out.println(map.get("C"));
	}
}

 

5. 충돌(Collision) 해결 알고리즘 (좋은 해쉬 함수 사용하기)

해쉬 테이블의 가장 큰 문제는 충돌의 경우이다. 이 문제를 충돌 또는 해쉬 충돌이라고 부른다.

 

- Chaining 기법

 

개방 해슁 또는 Open Hashing : 해쉬 테이블 저장공간 외의 공간을 활용하는 기법

충돌이 일어나면 링크드 리스트라는 자료 구조를 사용해서 링크드 리스트로 데이터를 추가로 뒤에 연결시켜서 저장하는 기법

 

- Linear Probing 기법

폐쇄 해슁 또는 Close Hashing : 해쉬 테이블 저장공간 안에서 충돌 문제를 해결하는 기법

충돌이 일어나면 해당 해쉬 어드레스의 다음 address부터 맨 처음 나오는 빈공간에 저장하는 기법

저장공간 활용도를 높이기 위한 기법

 

6. 빈번한 충돌을 개선하는 기법

해쉬 함수를 재정의  및 해쉬 테이블 저장공간을 확대

 

7. 유명한 해쉬 함수들

SHA(Secure Hash Algorithm, 안정한 해시 알고리즘) : 어떤 데이터도 유일한 고정된 크기의 고정값을 리턴해주므로, 해쉬 함수로 유용하게 활용 가능

SHA-1

SHA-256

 

8. 시간 복잡도

일반적인 경우(Collision 없는 경우)는 O(1)

최악의 경우(Collision이 모두 발생하는 경우) O(n)

해쉬 테이블의 경우, 일반적인 경우를 기대하고 만들기 때문에, 시간 복잡도는 O(1) 이라고 말할 수 있음

 

검색에서 해쉬 테이블의 사용 예

16개의 배열에 데이터를 저장하고 검색할 때 O(n)

16개의 데이터 저장공간을 가진 위의 해쉬 테이블에 데이터를 저장하고 검색할 때 O(1)

'Algorithm > 자료구조' 카테고리의 다른 글

힙 (Heap)  (0) 2023.01.16
트리  (0) 2023.01.16
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20

알고리즘 복잡도 표현 방법

 

1. 알고리즘 복잡도 계산이 필요한 이유

하나의 문제를 푸는 알고리즘은 다양할 수 있음

ex) 정수의 절대값 구하기

- 방법1 : 정수값을 제곱한 값에 다시 루트를 씌우기

- 방법2 : 정수가 음수인지 확인해서, 음수일 때만 -1을 곱하기

다양한 알고리즘 중 어느 알고리즘이 더 좋은지를 분석하기 위해, 복잡도를 정의하고 계산함

 

2. 알고리즘 복잡도 계산 항목

  • 시간 복잡도 : 알고리즘 실행 속도
  • 공간 복잡도 : 알고리즘이 사용하는 메모리 사이즈
가장 중요한 시간 복잡도를 꼭 이해하고 계산할 수 있어야 함

 

알고리즘 시간 복잡도의 주요 요소

반복문으로 계산

 

3. 알고리즘 성능 표기법

  • Big O (빅-오) 표기법 : O(N)
    • 알고리즘 최악의 실행 시간을 표기
    • 가장 많이/일반적으로 사용함
    • 아무리 최악의 상황이라도, 이정도의 성능은 보장한다는 의미이기 떄문
  • 오메가 표기법 : 오메가(N)
    • 오메가 표기법은 알고리즘 최상의 실행 시간을 표기
  • 세타 표기법 : 세타(N)
    • 오메가 표기법은 알고리즘 평균 실행 시간을 표기

 

대문자 O 표기법

  • 빅 오 표기법, Big-O 표기법 이라고도 부름
  • O(입력)
    • 입력 n에 따라 결정되는 시간 복잡도 함수
    • O(1), O(logn), O(n), O(nlogn), O(n^2), O(2^n), O(n!) 등으로 표기함
    • 입력 n의 크기에 따라 기하급수적으로 시간 복잡도는 늘어날 수 있음
    • O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n!)
  • 단순하게 입력 n에 따라 몇번 실행이 되는지를 계산하면 됨
    • 표현식에 가장 큰 영향을 미치는 n의 단위로 표기한다.
    • 상수회 : O(1)
    • n + 10 3n + 10 등 : O(n)
    • n^2 100n^2 + 100 등 : O(n^2)
  • ex) 만약 시간 복잡도 함수가 2n^2 + 3n -> 빅 오 표기법으로는 O(n^2)

 

4. 실제 알고리즘을 예로 각 알고리즘의 시간 복잡도와 빅 오 표기법 알아보기

  • 1부터 n까지의 합을 구하는 알고리즘1 
    • 시간 복잡도 : n 빅 오 표기법으로는 O(n)
public Integer sumAll(Integer n) {
	Integer total = 0;
    for (num = 1; num < n + 1; num++) {
    	total += num;
    }
    return total;
}
  • 1부터 n까지의 합을 구하는 알고리즘2
    • 시간 복잡도 : 1 빅 오 표기법으로는 O(1)
public Integer sumAll(Integer n) {
	return int(n * (n + 1) / 2)
}

 

'Algorithm > 자료구조' 카테고리의 다른 글

트리  (0) 2023.01.16
해쉬테이블  (0) 2023.01.03
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20
  (0) 2022.12.20

1. 링크드 리스트 (Linked List) 구조

- 연결 리스트라고도 함

- 배열은 순차적으로 연결된 공간에 데이터를 나열하는 데이터 구조

- 링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조

 

기본 구조와 용어

- 노드(Node) : 데이터 저장 단위 (데이터값, 포인터)로 구성

- 포인터(Pointer) : 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간

 

데이터 다음 데이터 주소 ---> 데이터 다음 데이터 주소 ---> 데이터 다음 데이터 주소

 

2. 간단한 링크드 리스트 예

- Node 구현

public class Node {
    private Integer data;
    public Node next;
    
    public Node(Integer data) {
    	this.data = data;
        this.next = null;
    }
    
    public Node(Integer data, Node next) {
    	this.data = data;
        this.next = next;
    }
    
    public Integer getData() {
        return this.data;
    }
}

 

 

- Node와 Node 연결하기 (포인터 활용)

Node node1 = new Node(1);
Node node2 = new Node(2);
node1.next = node2;
Node head = node1;

 

- 링크드 리스트로 데이터 추가하기

public void add(Integer data) {
    Node node = head;
    while (node.next != null) {
    	node = node.next;
    }
    node.next = new Node(data);
}

 

- 링크드 리스트 데이터 출력하기(검색하기)

Node node = head;
while (node.next != null) {
	System.out.println(node.getData());
    node = node.next;
}
System.out.println(node.getData());

 

3. 링크드 리스트의 장단점

- 장점

  • 미리 데이터 공간을 할당하지 않아도 됨 (배열은 미리 데이터 공간을 할당 해야 함)

- 단점

  • 연결을 위한 별도 데이터 공간이 필요하므로, 저장공간 효율이 높지 않음
  • 연결 정보를 찾는 시간이 필요하므로 접근 속도가 느림
  • 중간 데이터 삭제시, 앞뒤 데이터의 연결을 재구성해야 하는 부가적인 작업 필요

 

4. 링크드 리스트의 복잡한 기능1 (링크드 리스트 데이터 사이에 데이터를 추가)

- 링크드 리스트는 유지 관리에 부가적인 구현이 필요함

Node node1 = new Node(1);
Node node3 = new Node(3);
node1.next = node3;
Node head = node1;

Node node2 = new Node(2);
Node node = head;
Boolean search = true;

while (search) {
	if (node.getData() == 1) {
    	search = false;
    } else {
    	node = node.next;
    }
}

Node node_next = node.next;
node.next = node2;
node2.next = node_next;

 

5. 링크드 리스트의 복잡한 기능2 (특정 노드를 삭제)

- head 삭제

- 마지막 노드 삭제

- 중간 노드 삭제

 

6. 링크드 리스트 구현하기

public class Node {
    private Integer data;
    public Node next;
    
    public Node(Integer data) {
    	this.data = data;
        this.next = null;
    }
    
    public Node(Integer data, Node next) {
    	this.data = data;
        this.next = next;
    }
    
    public Integer getData() {
        return this.data;
    }
}

public class NodeMgmt() {
    private Node head;
	
	public void init(Integer data) {
    	head = new Node(data);
    }
	
	public void add(Integer data) {
    	if (head == null) {
        	head = new Node(data);
        } else {
        	Node node = head;
            while (node.next != null) {
            	node = node.next;
            }
            node.next = new Node(data);
        }
    }
    
    public void desc() {
    	Node node = head;
        while (node != null) {
        	System.out.println(node.getData());
            node = node.next;
        }
    }
    
    public void delete(Integer data) {
    	if (head == null) {
        	System.out.println("해당 값을 가진 노드가 없습니다.");
            return;
        }
        
        // 1. head 삭제
        if (head.getData() == data) {
            head = head.next;
        } else {
        	Node node = head;
            while (node.next != null) {
            	// 2. 중간, 마지막 노드 삭제
            	if (node.next.getData() == data) {
                	Node temp = node.next;
                    node.next = node.next.next; 
                    temp = null;
                    ruturn;
                } else {
                	node = node.next;
                }
            }
        }
    }
    
    public Node searchNode(Integer data) {
    	Node node = head;
        while (node != null) {
        	if (node.getData() == data) {
            	return node;
            } else {
            	node = node.next;
            }
        }
    }
}

 

7. 다양한 링크드 리스트 구조

더블 링크드 리스트 (Double Linked List) 기본 구조

- 이중 연결 리스트라고도 함

- 장점 : 양방향으로 연결되어 있어서 노드 탐색이 양쪽으로 모두 가능

이전 데이터 주소 데이터 다음 데이터 주소 --->
<---
이전 데이터 주소 데이터 다음 데이터 주소
public class Node {
    public Node prev;
    private Integer data;
    public Node next;
    
    public Node(Integer data) {
    	this.prev = null;
        this.data = data;
        this.next = null;
    }
}

public class  NodeMgmt() {
	private Node head;
    private Node tail;
    
	public void init(Integer data) {
    	head = new Node(data);
        tail = head;
    }
    
    public void insert(Integer data) {
    	if (head == null) {
        	head = new Node(data);
            tail = head;
        } else {
        	Node node = head;
        	while (node.next != null) {
            	node = node.next;
            }
            Node newNode = new Node(data);
            node.next = newNode;
            newNode.prev = node;
            tail = newNode;
        }
    }
	
    public void desc() {
    	Node node = head;
        whild (node != null) {
        	System.out.println(node.getData());
            node = node.next;
        }
    }
    
    public Node searchFromHead(Integer data) {
    	if (head == null) {
        	return null;
        }
    	Node node = head;
        while (node != null) {
        	if (node.getData() == data) {
            	return node;
            } else {
            	node = node.next;
            }
        }
        return null;
    }
    
    public Node searchFromTail(Integer data) {
    	if (head == null) {
        	return null;
        }
    	Node node = tail;
        while (node != null) {
        	if (node.getData() == data) {
            	return node;
            } else {
            	node = node.prev;
            }
        }
        return null;
    }
    
    // 노드 데이터가 특정 숫자인 노드 앞에 데이터를 추가하는 함수
    public Boolean insertBefore(Integer data) {
    	if (head == null) {
        	head = new Node(data);
            return true;
        } else {
        	Node node = tail;
            while (node.getData() != data) {
            	node = node.prev;
                if (node == null) {
                	return false;
                }
            }
            Node newNode = new Node(data);
            newNode.prev = node.prev;
            node.prev.next = newNode;
            newNode.prev = node.prev;
            newNode.next = node;
            node.prev = newNode;
            return true;
        }
    }
    
}

 

 

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
스택  (0) 2022.12.20
  (0) 2022.12.20
배열  (0) 2022.12.20

스택 (Stack)

- 데이터를 제한적으로 접근할 수 있는 구조

- 한쪽 끝에서만 자료를 넣거나 뺼 수 있는 구조

- 가장 나중에 쌓은 데이터를 가장 먼저 빼낼 수 있는 데이터 구조

- 큐: FIFO 정책

- 스택: LIFO 정책

 

1. 스택 구조

- 스택은 LIFO(Last-In, First-Out) 또는 FILO(First-In, Last-Out) 데이터 관리 방식을 따름

- 대표적인 스택의 활용: 컴퓨터 내부의 프로세스 구조의 함수 동작 방식

- 주요 기능

  • push(): 데이터를 스택에 넣기
  • pop(): 데이터를 스택에서 꺼내기

예) push(1) -> push(2) -> push(5) -> pop()

Stack
 
 
 
 
 
1

 

Stack
 
 
 
 
2
1

 

Stack
 
 
 
5
2
1
Stack
 
 
 
 
2
1

2. 스택 구조와 프로세스 스택

- 스택 구조는 프로세스 실행 구조의 가장 기본

- 함수 호출시 프로세스 실행 구조를 스택과 비교해서 이해 필요

 

3. 자료 구조 스택의 장단점

장점

- 구조가 단순해서 구현이 쉽다.

- 데이터 저장/읽기 속도가 빠르다.

단점 (일반적인 스택 구현시)

- 데이터 최대 갯수를 미리 정해야 한다.

- 저장 공간의 낭비가 발생할 수 있음, 미리 최대 갯수만큼 저장 공간을 확보해야 함

 

스택은 단순하고 빠른 성능을 위해 사용대므로, 보통 배열 구조를 활용해서 구현하는 것이 일반적임 이 경우 위의 단점이 있을 수 있음

 

4. JAVA 스택 클래스

import java.util.Stack;

Stack<Integer> integerStack = new Stack<>();
Stack<String> stringStack = new Stack<>();

 

값 추가, 삭제, 출력

Stack<Integer> stack = new Stack<>();

stack.push(1);		// stack에 값 1 추가
stack.push(2);		// stack에 값 2 추가

stack.pop();		// stack에 값 제거
stack.clear();		// stack에 전체 값 제거 (초기화)

stack.push(1);		// stack에 값 1 추가
stack.peek();		// stack의 가장 상단의 값 출력
stack.size();		// stackd의 크기 출력 : 1
stack.empty();		// stack이 비어있는지 체크 (비어있다면 true)
stack.contains(1);	// stack에 1이 있는지 체크 (있다면 true)

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
  (0) 2022.12.20
배열  (0) 2022.12.20

큐 (Queue)

 

1. 큐 구조

- 줄을 서는 행위와 유사

- 가장 먼저 넣은 데이터를 가장 먼저 꺼낼 수 있는 구조

- 음식점에서 가장 먼저 줄을 선 사람이 제일 먼저 음식점에 입장하는 것과 동일

- FIFO(First-In, First-Out) 또는 LILO(Last-In, Last-Out) 방식으로 스택과 꺼내는 순서과 반대

 

Input               Output
          8 5 4  

 

Input               Output
            8 5 4

 

Input               Output
              8 5

 

Input               Output
                8

 

2. 알아둘 용어

Enqueue: 큐에 데이터를 넣는 기능

Dequeue: 큐에서 데이터를 꺼내는 기능

 

3. 파생 종류

LifoQueue (Last-In, First-Out)

PriorityQueue (우선순위 큐)

 

4. JAVA  큐 라이브러리

import java.util.LinkedList;
import java.util.Queue;
import java.util.PriorityQueue;
import java.util.Collections;

Queue<Integer> integerQueue = new LinkedList<>();
Queue<String> stringQueue = new LinkedList<>();

// 낮은 숫자 우선 순위 큐
PriorityQueue<Integer> priorityQueueLowest = new PriorityQueue<>();

// 높은 숫자 우선 순위 큐
PriorityQueue<Integer> priorityQueueHighest = new PriorityQueue<>(Collections.reverseOrder());

 

값 추가, 삭제, 출력

Queue<Integer> stack = new LinkedList<>();

queue.add(1);		// queue에 값 1 추가
queue.offer(2);		// queue에 값 2 추가

queue.poll();       	// queue에 첫번째 값을 반환하고 제거 비어있다면 null
queue.remove();		// queue에 첫번째 값 제거
queue.clear();		// queue 초기화

queue.offer(1);		// queue에 값 1 추가
queue.offer(2);		// queue에 값 2 추가
queue.peek();		// queue의 첫번째 값 출력
queue.size();		// queue의 크기 출력

 

어디에 큐가 많이 쓰일까?

멀티 태스킹을 위한 프로세스 스케쥴링 방식을 구현하기 위해 많이 사용됨

큐의 경우에는 장단점 보다는 (특별히 언급되는 장단점이 없음), 큐의 활용 예로 프로세스 스케쥴링 방식을 함께 이해해두는 것이 좋음

 

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20
배열  (0) 2022.12.20

꼭 알아둬야 할 자료구조: 배열 (Array)

- 데이터를 나열하고, 각 데이터를 인덱스에 대응하도록 구성한 데이터 구조

- 파이썬에서는 리스트 타입이 배열 기능을 제공하고 있음

 

1. 배열이 왜 필요할까?

- 같은 종류의 데이터를 효율적으로 관리하기 위해 사용

- 같은 종류의 데이터를 순차적으로 저장

 

데이터 S T R I N G
인덱스(index) 0 1 2 3 4 5

 

배열의 장점:

- 빠른 접근 가능

배열의 단점:

- 추가/삭제가 쉽지 않음

 

2. 파이썬과 C 언어의 배열 예제

#include <stdio.h>

int main(int argc, char * argv[]) {
	char country[3] = "US";
    printf("%c%c\n", country[0], country[1]);
    printf("%s\n", country);
    return 0;
}
country = 'US'
print (country)

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20
  (0) 2022.12.20

+ Recent posts