T_era

[JAVA] 힙정렬 구현하기 본문

Programing/Java

[JAVA] 힙정렬 구현하기

블스뜸 2025. 4. 3. 17:58

힙정렬 : 힙 트리구조를 이용한 정렬알고리즘

힙정렬을 알기 위해서 이진 트리를 알아야한다. 그 중 완전 이진트리에 대해 알아보자

완전 이진 트리
한개의 노드(RootNode)로 시작하여 새로운 노드가 들어오면 해당 노드의 왼쪽과 오른쪽 충 2개가 연결될 때 까지 연결
2개가 모두 연결되면 이미 연결된 자식노드 중에서 왼쪽노드의 왼쪽자리부터 차례대로 연결
이렇게 완성된 노드들을 완전 이진 트리 라고한다

힙정렬은 이 완전 이진 트리에서 최대값과 최소값을 빠르게 찾기위해 이용하는 정렬

최대 힙 : 부모 노드가 자식노드보다 큰 노드
힙정렬은 최대힙이 가장 위에서 부터 내려오는 정렬
하지만 최대힙이 성립되지 않아 힙구조가 무너지는 경우 성립되지 않은 부모노드를 가장 큰 자식노드와 교체한다

작성순서
1. 사용할 노드를 정의한다

class HeapNode {
    private final int value;
    private HeapNode parent;
    private HeapNode leftNode;
    private HeapNode rightNode;

    public HeapNode(int value) {
        this.value = value;
        leftNode = null;
        rightNode = null;
        parent = null;
    }

    public boolean isLeftChild() {
        return leftNode == null;
    }

    public boolean isRightChild() {
        return rightNode == null;
    }

    public int getValue() {
        return value;
    }

    public boolean setLeftNode(HeapNode node) {
        if (isLeftChild()) {
            leftNode = node;
            leftNode.parent = this;
            return true;
        }
        return false;
    }

    public boolean setRightNode(HeapNode node) {
        if (isRightChild()) {
            rightNode = node;
            rightNode.parent = this;
            return true;
        }
        return false;
    }

    public HeapNode getLeftNode() {
        return leftNode;
    }

    public HeapNode getRightNode() {
        return rightNode;
    }

    public HeapNode getParent() {
        return parent;
    }

    public void setParent(HeapNode node) {
        parent = node;
    }
}

2. 정의한 노드를 통해 이진트리구조를 만든다 

public void insertNode(HeapNode node) {
        if (rootIsEmpty()) {
            root = node;
            last = node;
            return;
        }

        Queue<HeapNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            HeapNode current = queue.poll();

            if (current.getLeftNode() == null) {
                current.setLeftNode(node);
                last = node;
                return;
            } else {
                queue.offer(current.getLeftNode());
            }

            if (current.getRightNode() == null) {
                current.setRightNode(node);
                last = node;
                return;
            } else {
                queue.offer(current.getRightNode());
            }
        }
    }

3. 이진트리 구조를 최대힙구조를 충족할 수 있게 한다 heapify 알고리즘 수행

private void buildMaxHeap(HeapNode current) {
        if (current == null) {
            return;
        }
        buildMaxHeap(current.getLeftNode());
        buildMaxHeap(current.getRightNode());
        maxHeapify(current);
    }

    private void maxHeapify(HeapNode current) {
        HeapNode largest = current;
        HeapNode left = current.getLeftNode();
        HeapNode right = current.getRightNode();
        if (left != null && left.getValue() > largest.getValue()) {
            largest = left;
        }
        if (right != null && right.getValue() > largest.getValue()) {
            largest = right;
        }
        if (largest != current) {
            swap(current, largest);
            maxHeapify(largest);
        }
    }

4. 정렬을 한다 ( root를 제일 끝단의 노드와 바꾸고 그 노드를 제외하고 heapify알고리즘 수행)

public void sort(int[] arr) {
        for (int i = arr.length - 1; i >= 0; i--) {
            swap(root, last);
            arr[i] = last.getValue();
            moveLastHeap();
            buildMaxHeap(root);
        }
    }
    
    public void moveLastHeap() {
        HeapNode lastToParent = last.getParent();
        if (lastToParent == null) return;
        if (lastToParent.getLeftNode() == last) {
            lastToParent.removeLeft();
        } else {
            lastToParent.removeRight();
        }
        last.setParent(null);
        index--;
        last = nodeList.get(index);
     }


전체코드(뭔가 너무 막쓴코딩 같아서 한번 수정해야겠다..)
수정필요한 내용
1. list를 통한 last노드를 찾는 방식을 빼고 관리하는 방법을 생각해보자
2. swap을 value교환이 아닌 객체교환으로 하는 방법을 생각해보

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        NodeManager nodeManager = new NodeManager();
        nodeManager.insertNode(new HeapNode(10));
        nodeManager.insertNode(new HeapNode(15));
        nodeManager.insertNode(new HeapNode(17));
        nodeManager.insertNode(new HeapNode(12));
        nodeManager.insertNode(new HeapNode(14));
        nodeManager.insertNode(new HeapNode(16));
        nodeManager.insertNode(new HeapNode(13));
        nodeManager.insertNode(new HeapNode(11));
        nodeManager.insertNode(new HeapNode(19));

        nodeManager.heapSort(9);

    }
}

class NodeManager {
    private final ArrayList<HeapNode> nodeList;
    private HeapNode root;
    private HeapNode last;
    private int index;

    public NodeManager() {
        root = null;
        last = null;
        index = 0;
        nodeList = new ArrayList<>();
    }

    public boolean rootIsEmpty() {
        return root == null;
    }

    public HeapNode getRoot() {
        return root;
    }

    public void insertNode(HeapNode node) {
        if (rootIsEmpty()) {
            root = node;
            nodeList.add(root);
            return;
        }

        Queue<HeapNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            HeapNode current = queue.poll();

            if (current.getLeftNode() == null) {
                current.setLeftNode(node);
                last = node;
                nodeList.add(last);
                index++;
                return;
            } else {
                queue.offer(current.getLeftNode());
            }

            if (current.getRightNode() == null) {
                current.setRightNode(node);
                last = node;
                nodeList.add(last);
                index++;
                return;
            } else {
                queue.offer(current.getRightNode());
            }
        }
    }

    public void heapSort(int arrSize) {
        buildMaxHeap(root);
        int[] arr = new int[arrSize];
        sort(arr);
        for (int i = 0; i < arrSize; i++) {
            System.out.println(arr[i]);
        }
    }

    public void sort(int[] arr) {
        for (int i = arr.length - 1; i >= 0; i--) {
            swap(root, last);
            System.out.println("root " + root.getValue() + "last" + last.getValue());
            arr[i] = last.getValue();
            moveLastHeap();
            buildMaxHeap(root);
        }
    }

    public void moveLastHeap() {
        HeapNode lastToParent = last.getParent();
        if (lastToParent == null) return;
        if (lastToParent.getLeftNode() == last) {
            lastToParent.removeLeft();
        } else {
            lastToParent.removeRight();
        }
        last.setParent(null);
        index--;
        last = nodeList.get(index);
     }

    private void buildMaxHeap(HeapNode current) {
        if (current == null) {
            return;
        }
        buildMaxHeap(current.getLeftNode());
        buildMaxHeap(current.getRightNode());
        maxHeapify(current);
    }

    private void maxHeapify(HeapNode current) {
        HeapNode largest = current;
        HeapNode left = current.getLeftNode();
        HeapNode right = current.getRightNode();
        if (left != null && left.getValue() > largest.getValue()) {
            largest = left;
        }
        if (right != null && right.getValue() > largest.getValue()) {
            largest = right;
        }
        if (largest != current) {
            swap(current, largest);
            maxHeapify(largest);
        }
    }

    public void swap(HeapNode parent, HeapNode child) {
        if (parent == null || child == null) return;
        int temp = parent.getValue();
        parent.setValue(child.getValue());
        child.setValue(temp);
    }

}

class HeapNode {
    private int value;
    private HeapNode parent;
    private HeapNode leftNode;
    private HeapNode rightNode;

    public HeapNode(int value) {
        this.value = value;
        leftNode = null;
        rightNode = null;
        parent = null;
    }

    public boolean isLeftChild() {
        return leftNode == null;
    }


    public boolean isRightChild() {
        return rightNode == null;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public void removeLeft() {
        leftNode = null;
    }

    public void removeRight() {
        rightNode = null;
    }

    public boolean setLeftNode(HeapNode node) {
        if (isLeftChild()) {
            leftNode = node;
            leftNode.parent = this;
            return true;
        }
        return false;
    }

    public boolean setRightNode(HeapNode node) {
        if (isRightChild()) {
            rightNode = node;
            rightNode.parent = this;
            return true;
        }
        return false;
    }

    public HeapNode getLeftNode() {
        return leftNode;
    }

    public HeapNode getRightNode() {
        return rightNode;
    }

    public HeapNode getParent() {
        return parent;
    }

    public void setParent(HeapNode node) {
        parent = node;
    }
}

'Programing > Java' 카테고리의 다른 글

[JAVA] 문법정리  (0) 2025.04.15
[JAVA] Map에 객체를 value로 하기 위한 메서드  (0) 2025.04.14
[JAVA] 병합정렬 구현하기  (0) 2025.04.02
[JAVA] 퀵정렬 알고리즘 구현하기  (0) 2025.04.02
[JAVA] Comparator 구현하기  (0) 2025.03.29