怎么做網(wǎng)站鏡像小程序商城
在上篇文章數(shù)據(jù)結(jié)構(gòu)6——樹與二叉樹中,我們了解了樹和二叉樹的概念,接著上篇文章,在本篇文章中我們學(xué)習(xí)二叉樹順序結(jié)構(gòu)的實(shí)現(xiàn)。
目錄
1. 二叉樹的順序存儲(chǔ)結(jié)構(gòu)
2. 堆的概念及結(jié)構(gòu)
1. 堆的概念
2. 堆的結(jié)構(gòu)
3. 堆的實(shí)現(xiàn)
1. 堆節(jié)點(diǎn)
2. 交換節(jié)點(diǎn)
3. 打印
4. 堆的插入
向上調(diào)整:
插入:
5. 堆的刪除
向下調(diào)整:
刪除:
6. 初始化
7. 銷毀
驗(yàn)證:
源代碼:
Heap.h:
Heap.c:
test.c:
1. 二叉樹的順序存儲(chǔ)結(jié)構(gòu)
二叉樹一般可以使用兩種結(jié)構(gòu)存儲(chǔ),一種是順序結(jié)構(gòu),一種是鏈?zhǔn)浇Y(jié)構(gòu)。本篇文章我們先來研究二叉樹的順序存儲(chǔ),下篇文章詳解二叉樹的鏈?zhǔn)酱鎯?chǔ)。
普通的二叉樹是不適合用數(shù)組來存儲(chǔ)的,因?yàn)榭赡軙?huì)存在大量的空間浪費(fèi)。而完全二叉樹更適合使用順序結(jié)構(gòu)存儲(chǔ)。現(xiàn)實(shí)中我們通常把堆(一種二叉樹)使用順序結(jié)構(gòu)的數(shù)組來存儲(chǔ),需要注意的是這里的堆和操作系統(tǒng)虛擬進(jìn)程地址空間中的堆是兩回事,一個(gè)是數(shù)據(jù)結(jié)構(gòu),一個(gè)是操作系統(tǒng)中管理內(nèi)存的一塊區(qū)域分段。
如圖: 完全二叉樹順序存儲(chǔ):
非完全二叉樹順序存儲(chǔ):
2. 堆的概念及結(jié)構(gòu)
1. 堆的概念
簡單來說,堆除了是一棵完全二叉樹之外,還要滿足堆序性。
堆中每個(gè)節(jié)點(diǎn)的值都大于等于其子節(jié)點(diǎn)的值,根節(jié)點(diǎn)是堆中最大的元素,這樣的堆稱為最大堆或大根堆;
相反的,堆中每個(gè)節(jié)點(diǎn)的值都小于等于其子節(jié)點(diǎn)的值,根節(jié)點(diǎn)是堆中最小的元素,這樣的堆稱為最小堆或小根堆;
2. 堆的結(jié)構(gòu)
3. 堆的實(shí)現(xiàn)
(本篇文章只實(shí)現(xiàn)最小堆,最大堆與最小堆是相同的道理)
1. 堆節(jié)點(diǎn)
typedef int HPDataType;
//堆的物理結(jié)構(gòu)與順序表相似
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;
2. 交換節(jié)點(diǎn)
//交換
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
3. 打印
//打印
void HeapPrint(HP* php)
{assert(php);for (size_t i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}
4. 堆的插入
可是現(xiàn)在堆屬性并不滿足,因?yàn)?0在5的上面,這是一個(gè)最小堆,我們需要將小的數(shù)字在上面。
為了恢復(fù)堆屬性,我們需要交換30和5:
可是現(xiàn)在仍舊沒有滿足最小堆的堆屬性,所以還需要交換10和5:
此時(shí)才得到了最小堆。
因此在插入數(shù)據(jù)后每次都要進(jìn)行向上調(diào)整,于是向上調(diào)整的實(shí)現(xiàn)為:
向上調(diào)整:
//向上調(diào)整
void AdjustUP(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}
插入:
//插入
void HeapPush(HP* php, HPDataType x)
{assert(php);//判斷是否需要擴(kuò)容if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL)//檢查是否擴(kuò)容成功{perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUP(php->a, php->size - 1);//傳數(shù)組的最后一個(gè)數(shù)據(jù)
}
5. 堆的刪除
由于堆頂是最大或最小元素,為了滿足堆屬性,所以堆中每次只能刪除堆頂元素,一般的做法是先將堆頂元素與數(shù)組末尾元素先交換,再刪除末尾元素。
可是現(xiàn)在40在了堆頂,反而成了最大的元素,并不滿足最小堆,這時(shí)就需要向下調(diào)整:
每次刪除都要調(diào)整,所以向下調(diào)整的具體實(shí)現(xiàn)為:
向下調(diào)整:
//向下調(diào)整
void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){//找出小的那個(gè)孩子if (child + 1 < n && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);//繼續(xù)往下調(diào)整parent = child;child = parent * 2 + 1;}else{break;}}
}
刪除:
//刪除
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);--php->size;AdjustDown(php->a, php->size, 0);
}
6. 初始化
//初始化
void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}
7. 銷毀
//銷毀
void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}
驗(yàn)證:
int main()
{int a[] = { 9,3,7,10,24,14,28,72,21,5 };HP hp;HeapInit(&hp);for (size_t i = 0; i < sizeof(a) / sizeof(int); i++){HeapPush(&hp, a[i]);}HeapPrint(&hp);HeapDestroy(&hp);return 0;
}
源代碼:
Heap.h:
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int HPDataType;
//堆的物理結(jié)構(gòu)與順序表相似
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;//交換
void Swap(HPDataType* p1, HPDataType* p2);//打印
void HeapPrint(HP* php);//初始化
void HeapInit(HP* php);//向上調(diào)整
void AdjustUP(HPDataType* a, int child);
//插入
void HeapPush(HP* php, HPDataType x);//向下調(diào)整
void AdjustDown(HPDataType* a, int n, int parent);
//刪除
void HeapPop(HP* php);//銷毀
void HeapDestroy(HP* php);
Heap.c:
#include "Heap.h"//交換
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//打印
void HeapPrint(HP* php)
{assert(php);for (size_t i = 0; i < php->size; i++){printf("%d ", php->a[i]);}printf("\n");
}//向上調(diào)整
void AdjustUP(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}//插入
void HeapPush(HP* php, HPDataType x)
{assert(php);//判斷是否需要擴(kuò)容if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL)//檢查是否擴(kuò)容成功{perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUP(php->a, php->size - 1);//傳數(shù)組的最后一個(gè)數(shù)據(jù)
}//向下調(diào)整
void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){//找出小的那個(gè)孩子if (child + 1 < n && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);//繼續(xù)往下調(diào)整parent = child;child = parent * 2 + 1;}else{break;}}
}//刪除
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);--php->size;AdjustDown(php->a, php->size, 0);
}//初始化
void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}//銷毀
void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}
test.c:
int main()
{int a[] = { 9,3,7,10,24,14,28,72,21,5 };HP hp;HeapInit(&hp);for (size_t i = 0; i < sizeof(a) / sizeof(int); i++){HeapPush(&hp, a[i]);}HeapPrint(&hp);HeapDestroy(&hp);return 0;
}