数据结构-堆和二叉树(2)

简介: 数据结构-堆和二叉树

4.堆的实现

这里我们用数组来实现。

先定义一个结构体:

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

初始化堆

void HeapInit(HP* php)
{
  php->a = (HeapDatatype*)malloc(4*sizeof(HeapDatatype));
  if (php->a == NULL)
  {
    perror("malloc fail\n");
    return;
  }
  php->size = 0;
  php->capacity = 4;
}

堆的插入

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

向上调整法

堆要么是大堆,树的任意一个父节点都大于等于子节点,要么是小堆,树的任意一个父亲都小于等于孩子,所以我们每插入一个数据都要和它的父亲进行比较,这里使用向上调整法:

假设我们要得小堆,那每当插入的孩子小于父亲时都要交换它们的位置,前文我们讲了,可以通过孩子的下标找到父亲,再把父亲的下标给孩子,直到孩子是根节点或者中途父亲就已经小于孩子,就停止循环(如果要得到大堆,当插入的孩子大于父亲时交换它们的位置)。

void AdjustUp(HeapDatatype*a,int child)
{
  int parent = (child - 1) / 2;
  while (child > 0)
  {
    if (a[parent] > a[child])
    {
      HeapDatatype p = a[parent];
      a[parent] = a[child];
      a[child] = p;
      child = parent;
      parent = (child - 1) / 2;
    }
    else
    {
      break;
    }
  }
}

堆的删除

删除有两种方法:

  1. 直接删除根节点,然后把剩下的节点重新生成堆。

  2. 删除堆顶元素,然后把最后一个元素放到堆顶,然后使用向下调整法,直到满足堆的性质。

第一种方法过于复杂,我们采用第二种方法。

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);
}

向下调整法

具体步骤如下:

我们可以通过child=parent*2+1和child=parent*2+2得到父节点的左右子节点,然后从堆顶开始,将堆顶元素与其左右子节点中较小的那个进行比较,如果堆顶元素小于其子节点中的较小值,则将其与较小值交换位置,并继续向下比较,直到堆的性质被满足(如果要得到大堆就与较大的那个进行比较,如果堆顶元素大于子节点中的较大值,则将其和较大值交换位置

代码如下:

void AdjustDown(HeapDatatype*a, int n,int parent)
{
  int child = parent * 2 + 1;
  while (child  < n)
  {
    if (child + 1 < n && a[child] > a[child + 1])
    {
      child++;
    }
    if (a[parent] > a[child])
    {
      swap(&a[parent],&a[child]);
      parent = child;
      child = parent * 2 + 1;
    }
    else
    {
      break;
    }
  }
}

函数swap()用来交换两个数的值

swap(HeapDatatype* p1, HeapDatatype* p2)
{
  HeapDatatype tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}

取堆顶的数据

堆顶数据就是数组中下标为0的数据。

代码如下:

HeapDatatype HeapTop(HP* php)
{
  assert(php);
  assert(!HeapEmpty(php));
  return php->a[0];
}

堆的数据个数

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

堆的判空

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

堆的销毁

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

完整代码:

test.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
int main()
{
  HP hp;
  HeapInit(&hp);
  int arr[] = { 65,100,70,32,50,60 };
  int i = 0;
  for (i = 0; i < sizeof(arr) / sizeof(int); i++)
  {
    HeapPush(&hp, arr[i]);
  }
  while (!HeapEmpty(&hp))
  {
    HeapDatatype top = HeapTop(&hp);
    printf("%d ", top);
    HeapPop(&hp);
  }
  return 0;
}

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HeapDatatype;
typedef struct Heap
{
  HeapDatatype* a;
  int size;
  int capacity;
}HP;
//堆的初始化
void HeapInit(HP* php);
//堆的销毁
void HeapDestory(HP* php);
//堆的插入
void HeapPush(HP* php,HeapDatatype x);
//堆的删除
void HeapPop(HP* php);
//取堆顶元素
HeapDatatype HeapTop(HP* php);
//堆中数据个数
int HeapSize(HP* php);
//堆的判空
bool HeapEmpty(HP* php);
//向上调整法
void AdjustUp(HeapDatatype* a, int child);
//向下调整法
void AdjustDown(HeapDatatype* a, int n, int parent);

Heap.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
//堆的初始化
void HeapInit(HP* php)
{
  php->a = (HeapDatatype*)malloc(4*sizeof(HeapDatatype));
  if (php->a == NULL)
  {
    perror("malloc fail\n");
    return;
  }
  php->size = 0;
  php->capacity = 4;
}
//堆的销毁
void HeapDestory(HP* php)
{
  assert(php);
  free(php->a);
  php->a = NULL;
  php->size = 0;
  php->capacity = 0;
}
//交换两数值
swap(HeapDatatype* p1, HeapDatatype* p2)
{
  HeapDatatype tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}
//向上调整法
void AdjustUp(HeapDatatype*a,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;
    }
  }
}
//堆的插入
void HeapPush(HP* php, HeapDatatype x)
{
  if (php->size == php->capacity)
  {
    HeapDatatype* tmp = (HeapDatatype*)realloc(php->a, sizeof(HeapDatatype) * (php->capacity) * 2);
    if (tmp == NULL)
    {
      perror("realloc fail\n");
      return;
    }
    php->a = tmp;
    php->capacity *= 2;
  }
  php->a[php->size] = x;
  php->size++;
  AdjustUp(php->a, php->size-1);
}
//向下调整法
void AdjustDown(HeapDatatype*a, int n,int parent)
{
  int child = parent * 2 + 1;
  while (child  < n)
  {
    if (child + 1 < n && a[child] > a[child + 1])
    {
      child++;
    }
    if (a[parent] > a[child])
    {
      swap(&a[parent],&a[child]);
      parent = child;
      child = parent * 2 + 1;
    }
    else
    {
      break;
    }
  }
}
//堆的判空
bool HeapEmpty(HP* php)
{
  assert(php);
  return php->size == 0;
}
//堆的删除
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);
}
//取堆顶元素
HeapDatatype HeapTop(HP* php)
{
  assert(php);
  assert(!HeapEmpty(php));
  return php->a[0];
}
//堆的数据个数
int HeapSize(HP* php)
{
  assert(php);
  return php->size;
}

测试:

我们要得到的是小堆,通过调试可以看到,堆中的元素依次是 32 50 60 100 65 70

很明显,满足小堆的性质。

我们再来打印一下堆顶元素,

每次pop后再打印堆顶元素出来,数据是升序,那说明堆可以实现数据的排序,那我们用堆排序每次都要写一个堆出来吗,那岂不是太麻烦了?

下节我们再来详细讲解堆排序及相关问题,未完待续。。。

成屿
+关注
目录
打赏
0
0
1
0
3
分享
相关文章
|
4月前
|
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
68 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
3天前
|
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
35 9
 算法系列之数据结构-二叉树
|
2月前
|
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
60 12
|
2月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
58 10
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
55 2
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
168 16
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
138 4
|
4月前
|
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
236 8
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
200 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等