二叉树与堆的概念以及实现

树的概念

  1. 根没有父节点
  2. 子树互不相交
  3. 节点数 = 边数 + 1
  4. 节点的度 = 子树的个数
  5. 树的度:最大节点个数
  6. 树的高度:最大层次

树的表示:双亲表示法,孩子表示法,孩子兄弟表示法

  • 满二叉树:除过叶子,其他节点度都为二,并且每层都是满的
  • 完全二叉树:除过最后一层,剩余结构是一个满二叉树,并且最后一层的节点从左向右中间无间隔

满二叉树一定是完全二叉树

完全二叉树不一定是满二叉树

二叉树

二叉树的节点总数

n0 + n1 + n2 = 二叉树的节点总数

n1 + 2 * n2 = 二叉树边的总数

节点总数 - 1 = 边的个数

n0 + n1 + n2 - 1 = n1 + 2 * n2

n0 = n2 + 1

满二叉树高度:
log ⁡ 2 ( N + 1 ) \log_2(N+1) log2(N+1)

∵ 2 h − 1 = N ∴ h = l o g 2 ( N + 1 ) \because2^h - 1 =N \quad \therefore h = log_2(N+1) 2h1=Nh=log2(N+1)

满二叉树的节点一定为奇数

二叉树的存储形式
  1. 顺序结构:适合存放完全二叉树,没有空间浪费

规则:从第一层开始存放,直到最后一层,每层从左向右依次存放,如果中间有空节点,保留空节点的位置

完全二叉树

父子节点的位置:

parent: left_child = 2 x parent + 1

​ right_child = 2 x parent +2

child : parent = (child - 1) / 2

  1. 链式结构:适合存放一般二叉树

二叉链:数据,指向左右孩子的指针

三叉链:数据,指向左右孩子的指针 + 指向父亲的指针

完全二叉树:总数 2n

度为1的节点出现在倒数第二层

度为一的节点个数:

​ 节点个数为偶数:n1 = 1

​ 节点个数为奇数:n1 = 0

大小堆:

大堆:每个父节点都比子节点大

小堆:每个父节点都比子节点小

堆的向下调整:

隐含的前提:当前需要调整的位置以下的子结构已经是一个堆。

1. 小堆

从当前需要调整的位置开始,(parent)

a. 从左右孩子中选一个最的(child)

b. 如果最的child仍然小于parent:

​ 交换child和parent

​ 更新:parent = child,child = 2 * parent + 1

​ 如果child没有越界,跳到a,继续执行

​ 如果最的child仍然于parent:结束调整

void shiftDown(HPDataType* a, int size, int parent)
{
	int child = 2 * parent + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] < a[child])
			child++;
		if (a[child] < a[parent])
		{
			Swap(a, child, parent);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}

2. 大堆

从当前需要调整的位置开始,(parent)

a. 从左右孩子中选一个最的(child)

b. 如果最的child仍然于parent:

​ 交换child和parent

​ 更新:parent = child,child = 2 * parent + 1

​ 如果child没有越界,跳到a,继续执行

​ 如果最的child仍然于parent:结束调整

void shiftDown(HPDataType* a, int size, int parent)
{
	int child = 2 * parent + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])
			child++;
		if (a[child] > a[parent])
		{
			Swap(a, child, parent);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}
堆的向上调整

从当前位置开始(child),与父节点进行比较,如果是大堆,则若孩子大于父亲,交换二者,如果是小堆,则若孩子小于父亲,交换二者,直到不能交换为止。

void shiftUp(HPDataType* a, int child)
{
	//小堆
	/*int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(a, child, parent);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}*/
	//大堆
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(a, child, parent);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

堆的实现

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

堆的实现使用了线性表来存储数据

void Swap(HPDataType* a, int left, int right)
{
	int tmp = a[left];
	a[left] = a[right];
	a[right] = tmp;
}

// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int size)
{
	hp->_a = (HPDataType*)malloc(sizeof(HPDataType)* size);
	memcpy(hp->_a, a, sizeof(HPDataType)* size);
	hp->_size = size;
	hp->_capacity = size;

	for (int parent = (size - 2) / 2; parent >= 0; parent--)
	{
		shiftDown(hp->_a, size, parent);
	}
}

堆得插入与删除,堆得插入则直接在线性表末尾插入,然后进行向上调整,堆得删除则需先交换堆顶与最后一个叶子结点,接着线性表进行尾删,然后进行向下调整

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	if (hp->_size >= hp->_capacity)
	{
		hp->_capacity *= 2;
		hp->_a = (HPDataType*)realloc(hp->_a, sizeof(HPDataType)* hp->_capacity);
	}
	hp->_a[hp->_size++] = x;
	shiftUp(hp->_a, hp->_size - 1);

}
// 堆的删除
void HeapPop(Heap* hp)
{
	if (hp->_size > 0)
	{
		Swap(hp->_a, 0, hp->_size - 1);
		hp->_size--;
		shiftDown(hp->_a, hp->_size, 0);
	}
}

二叉树的实现

二叉树的遍历
  1. 前序(先序)遍历,先访问父节点,在访问左孩子,最后访问右孩子。
void BinaryTreePrevOrder(BTNode* root)
{
	if (root)
	{
		printf("%c ", root->_data);
		BinaryTreePrevOrder(root->_left);
		BinaryTreePrevOrder(root->_right);
	}
}

递归方式

void BinaryTreePrevOrderNoR(BTNode* root)
{
	Stack st;
	StackInit(&st);
	BTNode* cur = root;
	BTNode* top = NULL;
	while (cur || !StackEmpty(&st))
	{
		while (cur)
		{
			printf("%c ", cur->_data);
			StackPush(&st, cur);
			cur = cur->_left;
		}
		top = StackTop(&st);
		StackPop(&st);
		cur = top->_right;

	}
	printf("\n");
}

非递归方式,使用到了栈

  1. 中序遍历,先访问左孩子,在访问父节点,最后访问右孩子。
void BinaryTreeInOrder(BTNode* root)
{
	if (root)
	{
		BinaryTreeInOrder(root->_left);
		printf("%c ", root->_data);
		BinaryTreeInOrder(root->_right);
	}
}

递归方式

void BinaryTreeInOrderNoR(BTNode* root)
{
	Stack st;
	StackInit(&st);
	BTNode* cur = root;
	BTNode* top = NULL;
	while (cur || !StackEmpty(&st))
	{
		while (cur)
		{
			StackPush(&st, cur);
			cur = cur->_left;
		}
		top = StackTop(&st);
		StackPop(&st);
		printf("%c ", top->_data);
		cur = top->_right;
	}
	printf("\n");
}

非递归,使用到了栈

  1. 后序遍历,先访问左孩子,后访问右孩子,最后访问父节点。
void BinaryTreePostOrder(BTNode* root)
{
	if (root)
	{
		BinaryTreePostOrder(root->_left);
		BinaryTreePostOrder(root->_right);
		printf("%c ", root->_data);
	}
}

递归方式

void BinaryTreePostOrderNoR(BTNode* root)
{
	Stack st;
	StackInit(&st);
	BTNode* cur = root;
	BTNode* top = NULL;
	BTNode* prev = NULL;
	while (cur || !StackEmpty(&st))
	{
		while (cur)
		{
			StackPush(&st, cur);
			cur = cur->_left;
		}
		top = StackTop(&st);
		if (!top->_right || top->_right == prev)
		{
			printf("%c ", top->_data);
			StackPop(&st);
			prev = top;
		}
		else
			cur = top->_right;
	}
	printf("\n");
}

非递归方式,使用到了栈

  1. 层序遍历,从第一层开始,每层向后遍历。

使用队列实现

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", front->_data);
		if (front->_left)
			QueuePush(&q, front->_left);
		if (front->_right)
			QueuePush(&q, front->_right);
	}
	printf("\n");
}
二叉树的构建

此处使用先序遍历的方式建树,‘ # ’代表数中的空节点NULL

BTNode* BinaryTreeCreate(BTDataType* a,int n, int* pi)
{
	if (a[*pi] != '#' && *pi < n)
	{
		BTNode* root = (BTNode*)malloc(sizeof(BTNode));
		root->_data = a[*pi];
		(*pi)++;
		root->_left = BinaryTreeCreate(a, n, pi);
		(*pi)++;
		root->_right = BinaryTreeCreate(a, n, pi);
		return root;
	}
	return NULL;
}
二叉树的节点个数问题

拆分成左右子数的问题,用递归实现

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (!root)
		return 0;
	return BinaryTreeSize(root->_left)
		+ BinaryTreeSize(root->_right)
		+ 1;
}

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (!root)
		return 0;
	if (!root->_left && !root->_right)
		return 1;
	return BinaryTreeLeafSize(root->_left)
		+ BinaryTreeLeafSize(root->_right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (!root)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1)
		+ BinaryTreeLevelKSize(root->_right, k - 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值