1. 程式人生 > >二叉堆的一些基本操作,以及升序降序

二叉堆的一些基本操作,以及升序降序

我要理解二叉樹如何定義一個堆的

  • 必須是一顆完全二叉樹
  • 我們可以分大堆和小堆
  • 這裡主要通過順序連結串列的方法定義堆

在這裡插入圖片描述

  • 這是一個從上往下的大堆

  • 我們要如何儲存這個堆了,顯然我們需要一個數組,然後通過寫演算法實現這個堆的順序儲存。

  • 這裡主要說一下堆的升序與降序,以及初始化,插入刪除吧。

  • 堆其實理解起來相比二叉樹的鏈式儲存要容易很多,堆升序時候我們其實主要需要的這是一個判斷條件結點是否存在
    以及二叉樹的一個基本關係式,parent = child * 2 + 1 || parent = child * 2 + 2

  • 好了還是看程式碼吧,短時間這個我也不知道怎麼說了

#include <stddef.h>
#include <stdio.h>
#include <assert.h>

typedef struct Heap
{
	int	array[100];
	int size;
}	Heap;

static void Swap(int *a, int *b)
{
	int t = *a;
	*a = *b;
	*b = t;
}


void HeapInit(Heap *pH, int source[], int size)
{
	for (int i = 0; i < size; i++) {
		pH->array[i] = source[i];
	}
	pH->size = size;
}

void HeapAdjustDown(Heap *pH, int root)
{
	int parent = root;

	while (1) {
		// 先判斷有沒有孩子(葉子結點)
		// 陣列角度去想	-> 孩子的下標是否越界
		// 只要判斷左孩子的下標(因為是完全二叉樹)
		int left = parent * 2 + 1;
		if (left >= pH->size) {
			// 越界,沒有左孩子,也肯定沒有右孩子
			return;
		}

		// 一定有左孩子
		int maxChild = left;
		if (2 * parent + 2 < pH->size && pH->array[2 * parent + 2] > pH->array[left]) {
			// 前一個條件是判斷右孩子有沒有
			// 後一個條件判讀是右孩子是否比左孩子大
			maxChild = 2 * parent + 2;
		}

		if (pH->array[parent] > pH->array[maxChild]) {
			return;
		}

		// 交換 root 和 maxChild 下標所在的值
		int t = pH->array[parent];
		pH->array[parent] = pH->array[maxChild];
		pH->array[maxChild] = t;

		parent = maxChild;
	}
}

// 1. 陣列的越界判斷不對, 是否 == size
// 2. array[left] == NULL	array -> int[]	這麼判斷左孩子在不在是錯的
// 3. 交換的時候,交換的下標,而不是下標所在的值
void HeapAdjustDown2(Heap *pH, size_t root)
{
	size_t parent = root;
	size_t left, right;
	size_t maxChild;

	left = 2 * root + 1;
	right = 2 * root + 2;
	while (left < (size_t)pH->size) {
		maxChild = left;
		if (right < (size_t)pH->size && pH->array[left] < pH->array[right]) {
			maxChild = right;
		}

		if (pH->array[maxChild] < pH->array[parent]) {
			break;
		}

		Swap(pH->array + maxChild, pH->array + parent);
		parent = maxChild;
		left = 2 * parent + 1;
		right = 2 * parent + 2;
	}
	// left 不存在
}

// i-- 寫成 i++
void HeapMakeHeap(Heap *pH)
{
	// [(size - 2) / 2, 0]
	// 理解迴圈的區間的來歷
	for (int i = (pH->size - 2) / 2; i >= 0; i--) {
		HeapAdjustDown(pH, i);
	}
}

void HeapPop(Heap *pH)
{
	pH->array[0] = pH->array[pH->size - 1];
	pH->size--;

	HeapAdjustDown(pH, 0);
}

int HeapTop(const Heap *pH)
{
	return pH->array[0];
}

void HeapAdjustUp(Heap *pH, int child)
{
	int parent;
	while (child > 0) {
		parent = (child - 1) / 2;
		if (pH->array[parent] >= pH->array[child]) {
			return;
		}

		Swap(pH->array + parent, pH->array + child);

		child = parent;
	}
}

void HeapPush(Heap *pH, int data)
{
	assert(pH->size < 100);
	pH->array[pH->size++] = data;
	HeapAdjustUp(pH, pH->size - 1);
}

#include <stdio.h>

void Test()
{
	int array[] = { 53, 17, 78, 9, 45, 65, 87, 23, 31 };
	int size = sizeof(array) / sizeof(int);

	Heap	heap;
	HeapInit(&heap, array, size);
	HeapMakeHeap(&heap);

	HeapPush(&heap, 100);
	printf("%d\n", HeapTop(&heap));

	printf("建堆完成\n");
}

int main()
{

	Test();


	return 0;
}

我們初始化放入的資料是 int array[] = { 53, 17, 78, 9, 45, 65, 87, 23, 31 };
我們看一下我們分別降序升序以後隊中元素的變化吧

  • 降序以後

在這裡插入圖片描述

  • 我們插入100升序以後
    在這裡插入圖片描述
  • 好了,大概就這樣了。