欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

數(shù)據(jù)結(jié)構(gòu)之堆的具體使用

 更新時(shí)間:2022年02月24日 09:51:50   作者:小倪同學(xué) -_-  
本文主要介紹了數(shù)據(jù)結(jié)構(gòu)之堆的具體使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

堆的概念及結(jié)構(gòu)

在這里插入圖片描述

在這里插入圖片描述

定義堆

實(shí)現(xiàn)堆的功能首先要定義堆的結(jié)構(gòu)體

typedef int HPDataTpye;

typedef struct Heap
{
	HPDataTpye* a;		//存儲(chǔ)數(shù)據(jù)
	int size;           //保存元素個(gè)數(shù)
	int capacity;       //存儲(chǔ)容量
}HP;

堆的初始化

思路:

  • 先開辟一塊空間,將傳入的數(shù)據(jù)存放到堆的結(jié)構(gòu)體中
  • 將堆中數(shù)據(jù)建堆排序
  • 將堆結(jié)構(gòu)中容量,元素個(gè)數(shù)初始化

開辟空間不難,那么如何建堆呢?

這里有兩種思路,一是從上往下調(diào)整,二是從下往上調(diào)整

思路一:
從上往下調(diào)整

將傳入的結(jié)點(diǎn)當(dāng)做父節(jié)點(diǎn),比較其兩個(gè)子節(jié)點(diǎn),將子節(jié)點(diǎn)與父節(jié)點(diǎn)比較,如果不滿足堆的條件就交換,并將原先子節(jié)點(diǎn)的位置當(dāng)成父節(jié)點(diǎn),重復(fù)上述操作。如果滿足堆的條件就結(jié)束操作。(注意:該程序是建立在左右子樹都為大堆基礎(chǔ)上的)

在這里插入圖片描述

代碼如下

void Swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
// 條件:左右子樹都是小堆/大堆
void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 選出左右孩子中小 or 大的那個(gè)
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		// 1、如果小 or 大的孩子比父親小 or 大,則交換,繼續(xù)往下調(diào)整
		// 2、如果小 or 大 的孩子比父親大 or 小,則結(jié)束調(diào)整
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

思路二:
從下往上建

將傳入的結(jié)點(diǎn)當(dāng)做子節(jié)點(diǎn),找到其父結(jié)點(diǎn)并與之比較,不滿足堆的條件就交換,并將原父結(jié)點(diǎn)的位置當(dāng)成子節(jié)點(diǎn)重復(fù)之前操作。滿足堆的條件則退出程序。(注意:該程序建立在除傳入的子節(jié)點(diǎn)外,其余結(jié)點(diǎn)都滿足堆條件基礎(chǔ)上的)

在這里插入圖片描述

代碼實(shí)現(xiàn)

void Swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	//while (parent >= 0)  不對(duì)的 parent不會(huì)小于0
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

初始化總體代碼

void Swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}

// 條件:左右子樹都是小堆/大堆
void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 選出左右孩子中小 or 大的那個(gè)
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		// 1、如果小 or 大的孩子比父親小 or 大,則交換,繼續(xù)往下調(diào)整
		// 2、如果小 or 大 的孩子比父親大 or 小,則結(jié)束調(diào)整
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	//while (parent >= 0)  不對(duì)的 parent不會(huì)小于0
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapInit(HP* php, HPDataTpye* a, int n)
{
	assert(php);
	//開辟空間
	php->a = (HPDataTpye*)malloc(sizeof(HPDataTpye)*n);
	if (php->a == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	//轉(zhuǎn)移數(shù)據(jù)
	memcpy(php->a, a, sizeof(HPDataTpye)*n);
	//建堆排序
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, n, i);
	}

	php->capacity = n;
	php->size = n;
}

插入數(shù)據(jù)

思路:

  • 檢查是否滿容量,滿了就擴(kuò)容
  • 插入數(shù)據(jù),并將size+1

代碼:

void HeapPush(HP* php, HPDataTpye x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		HPDataTpye* tmp = (HPDataTpye*)realloc(php->a, 2 * php->capacity*sizeof(HPDataTpye));
		if (php->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		php->capacity *= 2;
	}
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

判空

思路:

判空只需判斷其元素個(gè)數(shù)是否為0即可

代碼:

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

刪除堆頂?shù)臄?shù)據(jù)

思路:

  • 先判空處理
  • 將堆頂數(shù)據(jù)和最后一個(gè)葉結(jié)點(diǎn)數(shù)據(jù)交換
  • 從上往下調(diào)整堆

在這里插入圖片描述

代碼:

void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	//交換頭尾數(shù)據(jù)
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

獲取堆頂數(shù)據(jù)

思路:

先判空,再取出堆頂數(shù)據(jù)

代碼:

HPDataTpye HeapTop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	return php->a[0];
}

獲取元素個(gè)數(shù)

直接返回size

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

打印

void HeapPrint(HP* php)
{
	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

銷毀堆

將開辟的空間釋放,并將size,capacity賦值為0

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

Topk問題

問:如何取出一組數(shù)據(jù)中最大的前K個(gè)值

有人會(huì)想到把所有數(shù)據(jù)建大堆,取出堆頂數(shù)據(jù)再刪除該數(shù)據(jù),重復(fù)操作K次
操作如下

void TestHeap()
{
	int a[] = { 27, 37, 28, 18, 19, 34, 65, 4, 25, 49, 15 };
	HP hp;
	HeapInit(&hp, a, sizeof(a) / sizeof(int));
	HeapPrint(&hp);

	printf("\n");

	int k = 0;
	scanf("%d", &k);
	printf("找出數(shù)組中最小的前%d個(gè):", k);
	while (!HeapEmpty(&hp)&&k--)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");
}

在這里插入圖片描述

如果該組數(shù)據(jù)個(gè)數(shù)為一萬,十萬呢?

這時(shí)用該方法不但耗費(fèi)時(shí)間而且十分耗內(nèi)存,那有沒有時(shí)間復(fù)雜符度較小的用堆實(shí)現(xiàn)的方法呢?
答案是有的,那就是Topk算法

Topk基本思路如下:

用數(shù)據(jù)集合中前K個(gè)元素來建堆
求前k個(gè)最大的元素,則建小堆
求前k個(gè)最小的元素,則建大堆用剩余的N-K個(gè)元素依次與堆頂元素來比較,不滿足則替換堆頂元素
將剩余N-K個(gè)元素依次與堆頂元素比完之后,堆中剩余的K個(gè)元素就是所求的前K個(gè)最小或者最大的元素。

代碼實(shí)現(xiàn)

void PrintTopK(int* a, int n, int k)
{
	HP hp;
	HeapInit(&hp, a, k);

	for(int i = k; i < n; i++)
	{
		if (a[i]>HeapTop(&hp))
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
	HeapPrint(&hp);
	HeapDestroy(&hp);
}

檢測(cè)

這里利用隨機(jī)數(shù)來檢測(cè)

void TestTopk()
{
	int n = 100000;
	int* a = (int*)malloc(sizeof(int)*n);
	srand(time(0));
	for (size_t i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;

	PrintTopK(a, n, 10);
}

運(yùn)行結(jié)果

在這里插入圖片描述

代碼總結(jié)

Heap.h 頭文件

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include<time.h>

typedef int HPDataTpye;

typedef struct Heap
{
	HPDataTpye* a;
	int size;
	int capacity;
}HP;

void Swap(int* px, int* py);
void AdjustDown(int* a, int n, int parent);
void AdjustUp(int* a, int child);


//void HeapInit(HP* php);
//初始化
void HeapInit(HP* php, HPDataTpye* a, int n);
// 插入x,保持他繼續(xù)是堆
void HeapPush(HP* php, HPDataTpye x);
//判空
bool HeapEmpty(HP* php);
// 刪除堆頂數(shù)據(jù),刪除后保持他繼續(xù)是堆
void HeapPop(HP* php);
// 獲取堆頂?shù)臄?shù)據(jù),也就是最值
HPDataTpye HeapTop(HP* php);
//獲取堆中元素個(gè)數(shù)
int HeapSize(HP* php);
//打印
void HeapPrint(HP* php);
//銷毀堆
void HeapDestroy(HP* php);

Heap.c 函數(shù)文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"

void Swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}

// 條件:左右子樹都是小堆/大堆
void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 選出左右孩子中小 or 大的那個(gè)
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		// 1、如果小 or 大的孩子比父親小 or 大,則交換,繼續(xù)往下調(diào)整
		// 2、如果小 or 大 的孩子比父親大 or 小,則結(jié)束調(diào)整
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	//while (parent >= 0)  不對(duì)的 parent不會(huì)小于0
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapInit(HP* php, HPDataTpye* a, int n)
{
	assert(php);
	//開辟空間
	php->a = (HPDataTpye*)malloc(sizeof(HPDataTpye)*n);
	if (php->a == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	//轉(zhuǎn)移數(shù)據(jù)
	memcpy(php->a, a, sizeof(HPDataTpye)*n);
	//建堆排序
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, n, i);
	}

	php->capacity = n;
	php->size = n;
}

// 插入x,保持它繼續(xù)是堆
void HeapPush(HP* php, HPDataTpye x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		HPDataTpye* tmp = (HPDataTpye*)realloc(php->a, 2 * php->capacity*sizeof(HPDataTpye));
		if (php->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		php->capacity *= 2;
	}
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

// 刪除堆頂數(shù)據(jù),刪除后保持他繼續(xù)是堆
void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

// 獲取堆頂?shù)臄?shù)據(jù),也就是最值
HPDataTpye HeapTop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	return php->a[0];
}


int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

void HeapPrint(HP* php)
{
	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

test.c 測(cè)試文件

#include "Heap.h"

void TestHeap()
{
	int a[] = { 27, 37, 28, 18, 19, 34, 65, 4, 25, 49, 15 };
	HP hp;
	HeapInit(&hp, a, sizeof(a) / sizeof(int));
	HeapPrint(&hp);

	printf("\n");

	int k = 0;
	scanf("%d", &k);
	printf("找出數(shù)組中最小的前%d個(gè):", k);
	while (!HeapEmpty(&hp)&&k--)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");
}

void PrintTopK(int* a, int n, int k)
{
	HP hp;
	HeapInit(&hp, a, k);

	for(int i = k; i < n; i++)
	{
		if (a[i]>HeapTop(&hp))
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
	HeapPrint(&hp);
	HeapDestroy(&hp);
}

void TestTopk()
{
	int n = 100000;
	int* a = (int*)malloc(sizeof(int)*n);
	srand(time(0));
	for (size_t i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;

	PrintTopK(a, n, 10);
}

int main()
{
	//TestHeap();
	TestTopk();

	return 0;
}

到此這篇關(guān)于數(shù)據(jù)結(jié)構(gòu)之堆的具體使用的文章就介紹到這了,更多相關(guān)數(shù)據(jù)結(jié)構(gòu) 堆內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論