Search
Duplicate
📒

[Data-Strcture] 04-6. Segment Tree

상태
미진행
수업
Algorithm
주제
Data-Strcture
4 more properties

Segment Tree

NOTE
어떤 데이터가 존재할 떄, 특정 구간의 결과값을 구하는데 사용하는 자료구조
아래와 같이 주어진 배열이 존재할 때, 3~5번째 구간의 합을 구하시오
하지만 배열의 크기가 커지면 어떻게 해야하는가? ⇒ 구간별 합을 저장해두면 빠르게 찾을 수 있다!
하지만 미리 합을 저장해두어도 값이 수정되거나 하면 나머지가 모두 수정되어야함
그렇기에 Segment Tree이진트리 구조를 가진다
segment의 개수는 2의 제곱수여야 한다
public SegmentTree(int[] inputList, String calculationMethod) { this.calculationMethod = calculationMethod; this.inputListLength = inputList.length; this.inputEndIndex = this.inputListLength - 1; this.inputList = new int[this.inputListLength]; for(int i = 0; i < this.inputListLength; i++) { this.inputList[i] = inputList[i]; } this.level = (int) Math.ceil(Math.log(this.inputListLength) / Math.log(2)) + 1; this.length = (int) Math.pow(2, this.level); // 완전이진트리가 아니라 1줄더 생길 수 있으니 넉넉하게 잡음 this.resultList = new int[this.length]; this.make(0, this.inputListLength-1, 1); }
Java
복사
segment tree 초기화
public int make(int inputStartIndex, int inputEndIndex, int treeIndex) { // 1. 리프노드라면 현재값을 채우고 값을 가지고 올라온다 if (inputStartIndex == inputEndIndex) { this.resultList[treeIndex] = this.inputList[inputStartIndex]; return this.resultList[treeIndex]; } // 2. 왼/오를 구분하기 위해 중간값을 가진다 int inputMidIndex = (inputStartIndex + inputEndIndex) / 2; // 3. 왼쪽과 오른쪽 값을 가져온다 int left_result = this.make(inputStartIndex, inputMidIndex, treeIndex * 2); int right_result = this.make(inputMidIndex + 1, inputEndIndex, treeIndex * 2 + 1); // 4. 두 값의 연산결과를 현위치에 저장하고 해당 값을 리턴 this.resultList[treeIndex] = this.method(left_result, right_result); // 5. return this.resultList[treeIndex]; }
Java
복사
segment tree 생성
public int getRangeProcess(int inputStartIndex, int inputEndIndex, int treeIndex, int rangeStartIndex, int rangeEndIndex) { // 완전하게 벗어난 범위는 무효화한다 if ((inputEndIndex < rangeStartIndex) || (inputStartIndex > rangeEndIndex)) { return 0; } // 구간에 완전히 들어간다 (리프 일수도 아닐수도 있다) // 그대로 값 들고옴 (그냥 밑으로 가라) if ((inputStartIndex >= rangeStartIndex) && (inputEndIndex <= rangeEndIndex)) { return this.resultList[treeIndex]; } // 좌우값 위치 구하기 int inputMidIndex = (inputStartIndex + inputEndIndex) / 2; int leftResult = this.getRangeProcess(inputStartIndex, inputMidIndex, treeIndex * 2, rangeStartIndex, rangeEndIndex); int rightResult = this.getRangeProcess(inputMidIndex + 1, inputEndIndex, treeIndex * 2 + 1, rangeStartIndex, rangeEndIndex); return this.method(leftResult, rightResult); } public int getRange(int rangeStartIndex, int rangeEndIndex) { return this.getRangeProcess(this.inputStartIndex, this.inputEndIndex, this.treeIndex, rangeStartIndex, rangeEndIndex); } // 합, 최대값, 최대공약수 구하는 식 public int method(int leftResult, int rightResult) { switch (this.calculationMethod) { case "sum": return leftResult + rightResult; case "max": return Math.max(leftResult, rightResult); case "gcd": return this.gcd(leftResult, rightResult); } return leftResult + rightResult; }
Java
복사
내가 구하고자 하는 구간

세그먼트 트리 - 데이터 변경

NOTE
특정 노드의 값을 2더한다
합의 경우는 그냥 다 더해주면 되지만 다른경우는 좀 까다로워진다
1.
리프노드는 그대로 갱신
2.
부모노드는 왼/오 를 계산해야한다
3.
상관없는 노드는 그대로 리턴한다
public int updateProcess(int inputStartIndex, int inputEndIndex, int treeIndex, int updateIndex, int updateValue) { // 구간에 영향을 미치지 않는다 (그대로 리턴) if ((updateIndex < inputStartIndex) || (updateIndex > inputEndIndex)) { return this.resultList[treeIndex]; } // 업데이트하고자 하는 리프위치에 도달 (그냥 바꿔라) if (inputStartIndex == inputEndIndex) { this.resultList[treeIndex] = updateValue; return this.resultList[treeIndex]; } // 이후 계산해주기 int inputMidIndex = (inputStartIndex + inputEndIndex) / 2; int leftResult = this.updateProcess(inputStartIndex, inputMidIndex, treeIndex * 2, updateIndex, updateValue); int rightResult = this.updateProcess(inputMidIndex + 1, inputEndIndex, treeIndex * 2 + 1, updateIndex, updateValue); this.resultList[treeIndex] = this.method(leftResult, rightResult); return this.resultList[treeIndex]; }
Java
복사