하나: 소개
1. 우선순위 큐: 지난 수업에서 이야기한 허프만 트리를 아직도 기억하시나요? 개발 작업을 대폭 줄이기 위해 우선순위 큐를 사용했는데, 이 우선순위 큐가 내부적으로 어떻게 구현되는지 아시나요?
해결 방법: 힙 상단이 크면 먼저 힙 상단을 삭제하세요.
2. 사용자 핫서치 순위 기능(Weibo 핫서치)을 어떻게 구현하나요? 1억 개의 키워드가 포함된 사용자 검색 로그가 있는 경우 상위 10개의 키워드를 추출하는 방법은 무엇입니까? 당신에게 주어진 처리 기계: 2CPU 2G 메모리. 연령, 0~200
해결 방법: 1. 발생 빈도 계산 2. 크기 10의 큰 상단 힙 유지
3.2 TOP K 문제, 예를 들어 1,000만 개의 숫자로 구성된 문자열이 주어지면 상위 k개의 가장 큰 숫자를 찾으세요. 하나는 정적 데이터이고 다른 하나는 동적 데이터입니다.
2: 힙의 정의
힙이란 무엇입니까? 힙은 다음 두 가지 사항을 충족해야 하는 특별한 종류의 트리입니다.
1. 완전 이진 트리 입니다 . ( 마지막 레이어를 제외하고 다른 레이어의 모든 노드는 가득 차고 마지막 레이어의 노드는 왼쪽에 배열되어야 합니다. )
2. 각 노드의 값은 왼쪽 및 오른쪽 자식 노드의 값보다 크거나 같거나 작거나 같습니다.
그러나 이진 검색 트리와는 다릅니다. 왼쪽 노드는 루트 노드보다 작고 오른쪽 노드는 루트 노드보다 큽니다.
![]()
작은 상단 더미 ![]()
큰 꼭대기 더미
3: 힙 작업
3.1 힙 저장
내가 전에 말한 것을 기억한다면 완전한 이진 트리를 위한 가장 좋은 저장 구조는 배열이라는 것을 알아야 합니다 . 특별한 속성이 있기 때문에 아래 첨자를 직접 사용하여 왼쪽 및 오른쪽 노드를 나타낼 수 있습니다.
예를 들어 오른쪽의 구조는 다음과 같습니다.
1시 10분
2시 8분
3시 7분
4시 6분
5시 4분
6시 3분
이전에 이야기한 왼쪽 및 오른쪽 하위 노드에 대한 공식을 기억하십시오. Left = 2*I i는 현재 점이 위치한 배열의 첨자입니다. 오른쪽=2*i+1
아래 첨자가 0부터 시작하면 다음 두 공식이 됩니다: 2*i+1, 2*i+2
3.2 힙에 삽입(두 가지 삽입 방법: 위에서 아래로, 아래에서 위로)
힙이 삽입되는 위치를 힙화라고 합니다.
위에서 아래로: 실제로 삽입된 지점을 힙의 맨 위에 놓고 순서대로 비교하면 됩니다.
아래에서 위로: 오른쪽 그림의 9를 삽입해 보겠습니다. 당연히 이때 9를 삽입한 후에는 힙 트리의 속성을 만족하지 못하므로 어떻게 해야 할까요? 실제로 더 이상 교환할 수 없을 때까지만 교환하면 됩니다.아래 그림을 참조하세요.
완전한 이진 트리. 완전 이진 트리와 유사하게 32개의 레이어는 21억 개 이상의 포인트를 가질 수 있습니다. n개의 숫자는 몇 번이나 교환될 수 있습니까?
최대 교환 횟수는 책의 높이이며, 마지막 레이어는 처리할 필요가 없으므로 최대 교환 횟수는 Logn-1 회 이다.
![]()
9 삽입과정
3.3 힙 삭제
1. 힙 상단 삭제: 힙 상단의 숫자를 마지막 숫자와 교환한 다음 힙을 정렬하면 결국 완전한 이진 트리가 보장될 수 있습니다. 노드.
4: 힙 트리 적용
1. 힙 정렬
이전에 정렬에 대해 이야기할 때 힙 정렬에 대해 이야기하지 않았다는 것을 다들 기억하실 겁니다. 오늘은 살펴보도록 하겠습니다. 이 힙 트리를 정렬에 사용하면 어떨까요?
수열이 주어졌다고 가정합니다: 8 4 20 7 3 1 25 14 17
힙 트리를 사용하여 정렬:
1. 먼저 완전한 이진 트리에 순서대로 저장합니다.
힙을 구축합니다.
2. 리프가 아닌 마지막 노드에서 힙을 생성합니다 . 마지막 리프 노드가 아닌 마지막 비리프 노드인 이유는 무엇입니까? 예를 보세요:
하나: 처음부터 오른쪽으로, 즉 처음부터 뒤쪽으로 건물을 짓는 것입니다.
두 가지 유형: 뒤에서 앞으로 교정. 어레이 뒤에서 앞으로 이동
정렬: 그렇다면 정렬을 어떻게 구현합니까?힙의 상단과 마지막 요소를 교환하고 교환 후 힙 변환을 수행하며 이 작업을 순차적으로 수행합니다.그냥 해보고 모두가 와서 보게 해주세요. 동시에, 각 정렬 후에 다시 정렬된 항목을 다시 정렬할 필요가 없습니다.
package tree.堆树;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int data[] = { 8, 4, 20, 7, 3, 1, 25, 14, 17 };
heapSort(data);
System.out.println(Arrays.toString(data));
}
/**
* 有int start, int end:参数是因为已经建好的就不需要再比较了,所以每次都有范围
* @param data
* @param start
* @param end
*/
public static void maxHeap(int data[], int start, int end) { // 建一个大顶堆,end表示最多建到的点 lgn
int parent = start;
int son = parent * 2 + 1; // 下标是从0开始的就要加1,从1就不用
while (son < end) {
int temp = son;
// 比较左右节点和父节点的大小 son:表示左结点 son+1:表示右结点
if (son + 1 < end && data[son] < data[son + 1]) { // 表示右节点比左节点到
temp = son + 1; // 就要换右节点跟父节点
}
// temp表示的是 我们左右节点大的那一个
if (data[parent] > data[temp])
return; // 不用交换
else { // 交换
int t = data[parent];
data[parent] = data[temp];
data[temp] = t;
parent = temp; // 继续堆化
son = parent * 2 + 1;
}
}
return;
}
public static void heapSort(int data[]) {
int len = data.length;
for (int i = len / 2 - 1; i >= 0; i--) { //o(nlgn) len / 2 - 1:代表最后一个非叶子结点开始
maxHeap(data, i, len); //
}
for (int i = len - 1; i > 0; i--) { //o(nlgn)
int temp = data[0];
data[0] = data[i];
data[i] = temp;
maxHeap(data, 0, i); //这个i能不能理解?因为len~i已经排好序了
}
}
}