完全二叉树

满二叉树
所有节点的度要么为0,要么为2,且所有的叶子节点都在最后一层。
1、第i层的节点数量为:2^{i-1}
2、总节点数n = 2^h-1,h为高度

这里说的满二叉树其实就是其他地方说的完美二叉树。

完全二叉树
叶子节点只会出现在最后2层,且最后一层的叶子节点都靠左对齐。
相当于是一个数组,其从上到下,从左到右排列。
1、根节点编号为N,则左孩子编号为2N+1,右孩子为2N+2
2、如果完全二叉树的左孩子编号为M,则根节点的编号为(M-1)/2
3、如果完全二叉树的右孩子编号为M,则根节点编号为(M-2)/2
4、如果完全二叉树孩子编号为M,则根节点的编号为(M-1)/2

 

//CompleteBinaryTree.h

#pragma once
/*
满二叉树
所有节点的度要么为0,要么为2,且所有的叶子节点都在最后一层。
1、第i层的节点数量为:2^{i-1}
2、总节点数n = 2^h-1,h为高度

完全二叉树
叶子节点只会出现在最后2层,且最后一层的叶子节点都靠左对齐。
相当于是一个数组,其从上到下,从左到右排列。
1、根节点编号为N,则左孩子编号为2N+1,右孩子为2N+2
2、如果完全二叉树的左孩子编号为M,则根节点的编号为(M-1)/2
3、如果完全二叉树的右孩子编号为M,则根节点编号为(M-2)/2
4、如果完全二叉树孩子编号为M,则根节点的编号为(M-1)/2
*/

#include <iostream>

template <class T>
class CompleteBinaryTree
{
public:
	CompleteBinaryTree()
	{
		m_num = 0;
		pRoot = nullptr;
	}
	~CompleteBinaryTree()
	{
		if (pRoot)
		{
			delete pRoot;
			pRoot = nullptr;
		}		
	}

	void insertNode(const T&data);//插入
	void preTravel();//先序遍历,可转换成遍历数组,只是按照先序遍历的顺序来遍历数组中的索引值即可
	void preTravelIndex(int data);
	int addr2index(const T *node);//返回node节点对应的数组索引值
	T *getParent(const T *node);//已知某个节点地址返回其父节点地址
	T *getLeftChild(const T *node);//已知某个节点地址返回其右孩子地址
	T *getRightChild(const T *node);//已知某个节点地址返回其左孩子地址
	T *getNodeAdd(const T &data);//已知节点数据,返回节点地址
	bool deleteNode(const T &node);//删除节点
private:
	T *pRoot;	//根节点地址
	size_t m_num;	//节点数量
};


template <class T>
void CompleteBinaryTree<T>::insertNode(const T& data)
{
	//新开内存
	T *pNew = new T[m_num + 1];
	if (pRoot)
	{
		memcpy(pNew, pRoot, sizeof(T)*m_num);
		delete pRoot;
	}
	pRoot = pNew;
	pRoot[m_num++] = data;
}

template <class T>
void CompleteBinaryTree<T>::preTravel()
{
	int iRootIdx = addr2index(pRoot);
	
	preTravelIndex(iRootIdx);
}

template <class T>
void CompleteBinaryTree<T>::preTravelIndex(int data)
{
	if (data <0 || data >= m_num)
		return;
	std::cout << pRoot[data]<<" ";
	int iLeft = 2 * data + 1;
	int iRight = 2 * data + 2;
	preTravelIndex(iLeft);
	preTravelIndex(iRight);
}

template <class T>
int CompleteBinaryTree<T>::addr2index(const T* node)
{
	int index = 0;
	T *pTempNode = pRoot;
	while (index < m_num)
	{
		if (pTempNode == node)
			return index;
		index++; 
		pTempNode++;
	}
	return index;
}

template<class T>
T * CompleteBinaryTree<T>::getParent(const T * node)
{
	int idx = addr2index(node);
	if (-1 == idx)
		return nullptr;
	if (idx < 1)
		return nullptr;
	return pRoot + (idx-1)/2;
}

template<class T>
inline T * CompleteBinaryTree<T>::getLeftChild(const T * node)
{
	int idx = addr2index(node);
	if (-1 == idx)
		return nullptr;
	return pRoot + (2*idx+1);
}

template<class T>
inline T * CompleteBinaryTree<T>::getRightChild(const T * node)
{
	int idx = addr2index(node);
	if (-1 == idx)
		return nullptr;
	return pRoot + 2 * idx + 2;
}

template<class T>
T * CompleteBinaryTree<T>::getNodeAdd(const T & data)
{
	for (size_t i = 0; i < m_num; i++)
	{
		if (data == pRoot[i])
			return pRoot + i;
	}
	return nullptr;
}

template<class T>
bool CompleteBinaryTree<T>::deleteNode(const T & node)
{
	//节点是否在树中
	int index = addr2index(getNodeAdd(node));
	if (-1 == index)
		return false;

#if 0
	//思路1
	//删除的内存没释放
	//循环从要删除的节点的下一个节点往前挪动
	for (int i = index + 1; i < m_num; i++)
		pRoot[i - 1] = pRoot[i];
	m_num--;
#else
	//1。如果树中只有一个元素,直接释放内存
	
	if (1 == m_num)
	{
		delete[]pRoot;
		pRoot = nullptr;
		m_num = 0;
		return true;
	}

	T *pTem = new T[m_num - 1];
	if(index != 1)//是第一个节点,则不需要拷贝
		memcpy(pTem, pRoot, index * sizeof(T));
	if(index < m_num-1)
	memcpy(pTem + index, pRoot + index+1, (m_num - 1 - index) * sizeof(T));

	delete[]pRoot;
	pRoot = pTem;
	m_num--;
	return true;
		
#endif

}

 

#include "CompleteBinaryTree.h"

int main()
{
	CompleteBinaryTree<char> tree;
	for (int i = 0; i < 10; i++)
		tree.insertNode('A'+i);
	tree.preTravel();
	std::cout << "\n";
	char *p = tree.getLeftChild(tree.getNodeAdd('B'));
	std::cout << *p << std::endl;
	bool bIsOK = tree.deleteNode('C');
	if (bIsOK)
		std::cout << "OK" << std::endl;
	else
		std::cout << "NG" << std::endl;
	tree.preTravel();

	return 0;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值