C 언어 이 진 트 리 와 더미 의 개념 과 실현

머리말 나무 이야기
자연계 에 나무 가 많아 요.이렇게 돼 있어 요.

그런데 저희 눈 에는 이렇게 보 여요.

분명 한 것 은 나무의 특징 은 한 쌍 이 많다 는 것 이다.우 리 는 이 한 쌍 의 많은 특징 을 이용 하여 프로 그래 밍 중의 문 제 를 잘 해결 할 수 있다.나무 에서 가장 기본 적 인 이 진 트 리 는 우리 의 중점 연구 대상 이다.
신기 한 정렬 된 동적 그림 을 보고 있 습 니 다.

일 을 하려 면 먼저 옳 은 것 을 구하 고,교묘 한 것 을 추구 해 야 한 걸음 한 걸음 성취 할 수 있 으 니,우리 기초 부터 시작 합 시다!
나무의 기본 성질 과 묘사
 나 무 는 한 쌍 이 넘 는 특수 한 데이터 형식 으로 나무의 뿌리 는 위의 잎 에 있다.일생 2,2,3,3 생 만물 의 느낌 이 든다.
나무의 기본 특징
나 무 는 뿌리 가 하나 있 고 뿌리 는 후계 점 이 없다.
나 무 는 서로 교차 하지 않 는 다.
不相交

ps:그림 은 폐쇄 회로 가 구성 되 지 않 으 면 교차 하지 않 는 다 는 것 을 알 수 있다.
모든 결점 은 하나의 나무 로 나 눌 수 있다.
나무의 정 의 는 나무 가 정 의 를 내 릴 때 나무의 개념 을 사용 하 는 재 귀적 인 정의 이다.그 는 나무의 고유 한 특성 을 말 했다.
트 리 키워드 분석
나무 에 큰 키워드 가 있어 서 골 치 아파 요.
결점 의 분류:

ps:
결점 의 도:결점 이 가지 고 있 는 서브 트 리 수 를 결점 의 도 라 고 한다.도가 0 인 점 은 잎 노드 나 단말기 결점 이 되 고 도가 0 이 아 닌 노드 를 비 단말기 결점 이 라 고 한다.나무의 도 는 나무 안의 각 결점 의 최대 치 이다.이 그림 D 는 가장 큰 결점 입 니 다.
결점 의 차원:뿌리 부터 1 층 으로 순서대로 증가한다.

단순 대비 트 리 구조 와 선형 구조:

나무의 표시 방법
양친 표현법

typedef struct
{
	int data;
	int parent;   //     
}PtNode;
typedef struct
{
	PtNode nodes[10];
	int root ;  
	int n;

}PTree;

이 그림 의 숫자 는 아버지의 결점 을 나타 낸다.

'아버지의 하 표'를 통 해 아버지의 위 치 를 찾 을 수 있다.
주:
부모님 을 찾 는 시간 복잡 도 O(1);아들 을 찾 으 려 면 나 무 를 옮 겨 다 녀 야 한다.
데이터 의 첨삭 과 수정.
왼쪽 아이 오른쪽 형제

typedef struct CSNode
{
	int data;
	struct CSNode* firstchild, * rightsib;
};
왼쪽 아이 오른쪽 형제의 방식 은 바늘 하나 가 더 있 는 것 이다.바늘 하 나 는 자신의 가장 왼쪽 아 이 를 가리 키 고 다른 바늘 은 자신의 오른쪽 형 제 를 가리킨다.

이런 표현법 은 우리 가 자주 사용 하 는 표현법 이다.
이 진 트 리 의 개념 구조
이 진 트 리
이 진 트 리 는 특수 한 나무 로 그의 특징 은 뿌리 노드 두 개의 키 노드 이 고 이 두 개의 키 노드 는 각각 왼쪽 나무 와 오른쪽 나무 라 고도 부른다.

주의 하 다.
이 진 트 리 의 도 는 이 진 트 리 의 좌우 자 수 를 초과 하지 않 고 이 진 트 리 가 있 으 며 한 개의 나무 만 있 을 때 도 좌우 로 구분 해 야 한다.
특수 이 진 트 리
두 갈래 로 된 나무
이 진 트 리 는 층 마다 가득 차 있다.그러면 이 이 진 트 리 는 이 진 트 리 가 가득 하 다.이 진 트 리 의 층수 가 k 이 고 결점 총 수 는(2^k)-1 입 니 다.

완전 이 진 트 리
완전 이 진 트 리 의 앞 k 층 은 모두 가득 찬 k 층 이 불만 을 가 질 수 있 지만 반드시 연속 적 이 고 왼쪽 과 오른쪽 을 만족 시 켜 야 한다.

주의 하 다.
이 진 트 리 는 완전히 이 진 트 리 일 것 이 고,그렇지 않 으 면 옳지 않다.완전 이 진 트 리 는 반드시 먼저 왼쪽 과 오른쪽 이 고,아래 그림 과 같 으 면 맞지 않 는 다.

완전 이 진 트 리 잎의 결점 은 모두 마지막 두 층 에 있 고 왼쪽 은 마지막 k 층 의 잎 에 집중 되 며 오른쪽 은 k-1 층 의 잎 에 집중 된다.
이 진 트 리 의 성질
이 진 트 리 의 i 층 에는 기껏해야 2^(i-1)의 결점 i>0 이 있다.
깊이 가 k 인 이 진 트 리 는 많아야 2^k-1 개의 결점 이 있다
모든 이 진 트 리 T 에 대해 터미널 노드 트 리 가 n0 이 고 도 2 의 노드 트 리 가 n2 이면 no=n2+1 입 니 다.

n 개의 결점 을 가 진 완전 이 진 트 리 의 깊이 는 log 2(n)+1 이다.
n 개의 결점 을 가 진 완전 이 진 트 리 에 대해 위 에서 아래로 왼쪽 에서 오른쪽 순서 로 0 부터 번 호 를 매기 면 번호 와 i 의 결점 이 있다.
1.i>0 이면 i 위치 결점 의 부모 번 호 는(i-1)/2 이다.
2.만약 에 2i+1=n 이면 왼쪽 아이 가 없다.
3.만약 2i+2=n 은 아이 가 없다.

            parent,    leftchild,    ringhtchild。
   : leftchild=parent*2+1
          rightchild=parent*2+2
e~g
2n 개의 결점 을 가 진 완전 이 진 나무 에서 잎의 결점 개 수 는?
완전 이 진 트 리 에 풀 려 있 고 3 가지 경우 만 있 습 니 다.
도 0
도 는 0 이다.즉,뿌리 결점 잎의 결점 개수 만 n 이다.
1 도 1 의 결점 만 있다.

x 는 2 로 읽 는 결 점 수 를 설정 하고 y 는 잎 노드 수 x+y+1=2n 과 같 으 며 잎 수 는 2 로 읽 는 더하기 1 득 y=x+1
득 y=n
도 1 의 결점 이 없다

그림 에서 알 수 있 듯 이 짝수 의 결점 을 구성 하여 버 릴 수 없다.
다시 말 하면 잎 노드 의 개 수 는 n 이다.
완전 이 진 트 리 를 가 진 노드 수 는 531 개 입 니 다.그러면 이 나무의 높이 는?
해:공식 을 직접 가지 고 10.
767 개의 결점 을 가 진 완전 이 진 트 리 의 잎 노드 개 수 는?
앞의 결론 에서 알 수 있 듯 이 이 진 트 리 는 반드시

그래서 쌍 결점 을 x 잎 을 y=x+1 로 설정 하고 y+y-1=767 해 를 y=384 로 설정 합 니 다.
도 2 인 나무 와 이 진 나 무 는 어떤 차이 가 있 습 니까?
해:
도 2 의 나 무 는 무질서 한 나무 로 좌우 가 구분 되 지 않 으 며,이 진 트 리 는 왼쪽,오른쪽 이 어야 한다.도 2 의 나 무 는 반드시 하나의 결점 도 2 이 고,이 진 트 리 는 없 을 수 있다.
k 포크 나무 에 가득 한 잎 매듭 포인트 n0 과 비 잎 매듭 포인트 n1 숨 기기*n0=(k-1)n1+1 증명
우선,우 리 는 이 진 트 리 가 가득 하 다 는 것 을 알 게 되 었 다.이 진 트 리 는 n 층 이상 의 모든 결점 의 도 는 k 이다.
총 분기 결산 포인트=k 배의 n1
총 결 포인트=n0+n1
총 분기 결산 포인트+1=총 결산 포인트
kn1+1=no+n1
이 진 트 리 의 저장 구조
이 진 트 리 의 저장 소 는 위 에서 아래로 왼쪽 에서 오른쪽으로 정렬 된다.

이 두 갈래 나 무 를 배열 에 저장 하면 얻 을 수 있 습 니 다.


두 갈래 나무 와 더미
더 미 는 특수 한 수,즉 완전 이 진 트 리 이다.

이 나 무 를 관찰 하면 그의 아버지 결점 은 모두 자 결점 보다 작다.우 리 는 그것 을 최소 더미 라 고 부른다.반대로 모든 아버지 결점 이 자 결점 보다 크 면 이런 완전 이 진 나 무 는 최대 더미 가 된다.
주의:
더 미 는 완전 이 진 트 리 더미 이 고 큰 더미 와 작은 더미 두 가지 만 있 습 니 다.
쌓 인 실현
다음 배열 이 있 습 니 다.int arr[] = { 23,2,5,12,7,17,25,19,36,99,22,28,46,92 };어떻게 그것 을 최소 더미 로 조정 합 니까?

그림 에서 알 수 있 듯 이 가장 작은 무더기 의 특징 은 아버지 가 아들 보다 작 고 이 나무 가 둘 러 싼 곳 은 아들 이 아버지 보다 크기 때문에 우 리 는 가장 작은 아들 을 바 꿔 야 한다.

바 꿔 보니까 저희 가 만족 하지 않 아서 바 꿔 야 돼 요.

원 을 그 리 는 곳 에 있 는 이 두 갈래 나 무 는 만족 하지 않 고 한 번 만 더 교환 하면 가장 작은 더미 이다.

이때 이 진 트 리 는 최소 더 미 를 만족 시 켰 다.
이 과정의 알고리즘 을 우 리 는 아래로 조정 하 는 알고리즘 이 라 고 부 릅 니 다.만약 우리 가 이 진 트 리 를 구분 하면...

아래로 조정 하 는 본질은 먼저 위의 것 을 만족 시 키 고 아래 의 것 을 만족 시 키 는 것 이다.
주의:
아래로 알고리즘 을 조정 합 니 다.원 소 를 조정 하 는 좌우 트 리 는 모두 최소 더미 여야 합 니 다.아래로 알고리즘 을 조정 하고 잎 이 맺 힐 때 멈 출 수 있 습 니 다.어린 아이 가 아버지 보다 때 리 면 처리 할 필요 가 없습니다.나무 전체 가 가장 작은 더미 입 니 다.
코드 를 첨부 하 다

#include<stdio.h>
void AdjustDown(int a[], int n, int parent)
{
	
	int child = 2 * parent + 1;//      
	
	while (child < n)
	{
		if (child+1<n&&a[child] > a[child + 1])
		{
			child++;
		}
		if (a[parent] > a[child])
		{
			
			int tmp = a[child];
			a[child] = a[parent];
			a[parent] = tmp;
				parent = child;
		child = 2 * parent + 1;
		}
	
		else
		{
			break;
		}
	}
}
int main()
{
	int arr[14] = { 23,2,5,12,7,17,25,19,36,99,22,28,46,92 };
	int n = 14;
	 AdjustDown(arr, n, 0);
	return 0;
}
코드 판독




그렇다면 더 일반적인 상황 에 서 는 좌우 나무 가 작은 더미 가 아 닌 데 어떻게 조정 할 것 인가?
우 리 는 단지 아래 에서 위로,작은 나무 더미 에서 큰 나무 더미 로 바 뀌 어야 한다.
먼저 만족 하고 아래 는 만족 하 는 것 이다.

먼저 아래 의 것 을 만족 시 키 고 만족 위 에 쌓 으 면 함수 에 모든 아버지 결점 을 차례대로 전달 하면 됩 니 다.

#include<stdio.h>
void AdjustDown(int a[], int n, int parent)
{
	
	int child = 2 * parent + 1;
	
	while (child < n)
	{
		if (child+1<=n && a[child] >a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			
			int tmp = a[child];
			a[child] = a[parent];
			a[parent] = tmp;
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

int main()
{
	int arr[14] = { 23,2,5,12,7,17,25,19,36,99,22,28,46,92 };
	int n = 14;
	 
	int tmp = 0;
	int i = (n - 1 - 1) / 2;
	for (i; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}
	return 0;
}
더미 정렬
상승 순 서 를 예 로 들 면,우 리 는 먼저 작은 무 더 기 를 생각 하지만,작은 무 더 기 는 적합 하지 않다.보다

우 리 는 쌓 아 올 리 는 요소 가 반드시 가장 작 을 것 이라는 것 을 안다.그러면 우 리 는 순서대로 쌓 아 올 리 는 것 만 가 져 가 야 한다.
저희 가 2 를 가 져 가면 7 이 쌓 여 있어 요.

쌓 인 지붕 을 제거 한 후 다음 요 소 는 작은 더 미 를 채 워 서 저장 되 지 않 고 순서 가 모두 어 지 러 워 졌 다.그래서 작은 무 더 기 는 오름차 순 에 적합 하지 않다.
무더기 의 상승 순 서 는 또 어떻게 해 야 합 니까?

이때 우 리 는 10 과 80 을 바 꾸 고 80 을 쌓 아 올 리 지 않 으 면 된다.

그렇다면 코드 실현 은 어 떨 까?

전체 코드 첨부

#include<stdio.h>
void AdjustDown(int a[], int n, int parent)
{
	
	int child = 2 * parent + 1;
	
	while (child < n)
	{
		if (child+1<n && a[child] <a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			
			int tmp = a[child];
			a[child] = a[parent];
			a[parent] = tmp;
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

int main()
{
	int arr[14] = { 23,2,5,12,7,17,25,19,36,99,22,28,46,92 };
	int n =14 ;
	 
	int tmp = 0;
	int i = (n - 1 - 1) / 2;
	for (i; i >= 0; i--)
	{
		AdjustDown(arr, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		int tmp = arr[0];
		arr[0] = arr[end];
		arr[end] = tmp;
		AdjustDown(arr, end, 0);
		end--;
	}
	return 0;
}
쌓 기 정렬 은 효율 적 인 정렬 이다.
쌓 인 총 결:
물리 구 조 는 하나의 배열 이다논리 구 조 는 완전 이 진 트 리무더기 와 작은 무더기 의 관계
더미 정렬
요소 삽입
4.567917.최대 또는 최소 찾기쌓 인 기능 실현
더미 삽입
삽입여기 서 우 리 는 더미 의 상 향 조정 을 도입 한다.


그러면 코드 는 어떻게 실현 합 니까?아래 정렬 과 유사

코드 를 첨부 하 다

void AdjustUp(int* a, int n, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
요 소 를 삽입 한 후 한 번 에 위로 정렬 하면 됩 니 다.
아래로 정렬 할 수도 있 지만 귀 찮 습 니 다.
TOPK 문제
TopK 문제 의 본질은 작은 무 더 기 를 취하 여 무 더 기 를 만 든 다음 에 무 더 기 를 만 드 는 것 이다.심지어 너 는 하나의 순서 라 고 할 수 있다.앞의 네 개 를 다른 배열 에 넣다.그러나 정렬 은 시간의 복잡 도가 가중 되 는 것 을 의미 하 므 로 독자 들 은 지식 을 쌓 고 데 이 터 를 쌓 는 방식 으로 최소 데 이 터 를 얻 고 여기 서 문제 와 답 을 제시 해 주 십시오.

 * Note: The returned array must be malloced, assume caller calls free().
 */
/*    */
void swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

/*           ,        */
void swim(int* nums, int k) {
    while (k > 1 && nums[k] > nums[k / 2]) {
        swap(&nums[k], &nums[k / 2]);
        k /= 2;
    }
}

/*            ,        */
void sink(int* nums, int k, int numsSize) {
    while (2 * k < numsSize) {
        int child = 2 * k;
        if (child < numsSize && nums[child] < nums[child + 1]) {
            child++;
        }
        if (nums[k] > nums[child]) {
            break;
        }
        swap(&nums[k], &nums[child]);
        k = child;
    }
}

/*         */
typedef struct Heap {
    int* data;
    int szie;
    int capacity;
}T_Heap, *PT_Heap;

/*        */
PT_Heap createHeap(int k) {
    PT_Heap obj = (PT_Heap)malloc(sizeof(T_Heap));
    obj->data = (int*)malloc(sizeof(int) * (k + 1));
    obj->szie = 0;
    obj->capacity = k + 1;
    return obj;
}

/*         */
bool isEmpty(PT_Heap obj) {
    return obj->szie == 0;
}

/*          */
int getHeapSize(PT_Heap obj) {
    return obj->szie;
}

/*       */
void pushHeap(PT_Heap obj, int elem) {
    /*              */
    obj->data[++obj->szie] = elem;
    /*         ,          */
    swim(obj->data, obj->szie);
}

/*        */
int getHeapTop(PT_Heap obj) {
    return obj->data[1];
}

/*         */
int popHeap(PT_Heap obj) {
    /*        */
    int top = obj->data[1];
    /*             ,        */
    swap(&obj->data[1], &obj->data[obj->szie--]);
    /*            INT_MIN */
    obj->data[obj->szie + 1] = INT_MIN;
    /*           */
    sink(obj->data, 1, obj->szie);
    return top;
}

int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize){
    /*      、 k 0,  NULL */
    if (arrSize == 0 || k == 0) {
        *returnSize = 0;
        return NULL;
    } else {
        *returnSize = k;
    }
    /*        k */
    int* ret = (int*)calloc(k, sizeof(int));
    /*         k   */
    PT_Heap heap = createHeap(k);
    /*       k      */
    for (int i = 0; i < k; i++) {
        pushHeap(heap, arr[i]);
    }
    /*                ,     k   */
    for (int i = k; i < arrSize; i++) {
        if (arr[i] < getHeapTop(heap)) {
            popHeap(heap);
            pushHeap(heap, arr[i]);
        }
    }
    /*             */
    for (int i = 0; i < k; i++) {
        ret[i] = popHeap(heap);
    }
    return ret;
}

이 진 트 리 의 구조 및 구현
두 갈래 나무의 옮 겨 다 니 기
1.선착순 옮 겨 다 니 기
이 진 트 리 가 비어 있 으 면 비어 있 습 니 다.그렇지 않 으 면
루트 노드 접근 하기;
먼저 왼쪽 트 리 를 옮 겨 다 니 세 요.
오른쪽 하위 트 리 를 먼저 옮 겨 다 니 기;
2.중간 순서 로 옮 겨 다 니 기
이 진 트 리 가 비어 있 으 면 비어 있 습 니 다.그렇지 않 으 면
왼쪽 하위 트 리 를 중앙 순서 로 옮 겨 다 니 기
루트 노드 접근 하기;
오른쪽 하위 트 리 를 중간 순서 로 옮 겨 다 니 기;
3.후 순 옮 겨 다 니 기
뒤 순 서 는 왼쪽 하위 트 리 를 옮 겨 다 닌 다.
오른쪽 하위 트 리 를 옮 겨 다 니 기;
루트 노드 접근 하기;
코드 구현

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf(" NULL ");
		return;
	}
	printf(" %c ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL");
		return;
	}
	InOrder(root->left);
	printf(" %c ", root->data);
	InOrder(root->right);
}
void PostOrder(BTNode* root)
{
	const BTNode* a = root;
                                                                                                                                                                                                                                                                                                                                                                              	if (root == NULL)
	{
		printf(" NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf(" %c ", root->data);
}
프로그램 실현 방법 과 재 귀 기술 재 귀 는 먼저 당신 이 재 귀 하려 는 함수 의 기능 을 확정 합 니 다.예 를 들 어 그 가 무엇 을 되 돌려 주 고 그 가 무엇 을 전 달 했 는 지,그 가 무엇 을 했 는 지 상황 에 따라 토론 을 했 는 지 가능 한 한 분명하게 구분 합 니 다.함수 출구 설계
본 제 를 예 로 들다

이 세 줄 의 코드 순서 가 바 뀌 면 옮 겨 다 니 는 방식 도 달라 지 는 이 유 는 무엇 일 까?
우선 함수 의 출구 는 루트 가 비어 있 습 니 다PostOrder(root->left);왼쪽 나무 방향 으로 쭉 갑 니 다.언제 까지 다음 문장 을 진행 합 니까?끝까지 가면 그림 과 같다.

이때 함수 출구 에 따라return ;위 층 으로 돌아 갑 니 다.그림 과 같 습 니 다.

이때 입장PostOrder(root->right);그림 참조.

이때 함수 출구 를 만족 시 켰 습 니 다.
총결산
C 언어 이 진 트 리 와 쌓 기 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 C 언어 이 진 트 리 와 쌓 기 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기