N叉树的增删改查

一个N叉树至少需要三个指针,指向父节点的指针,指向第一个兄弟的指针,指向第一个孩子的指针。

默认除第一个孩子外其他的孩子没有孩子

仅限熟悉N叉树使用。

//NTree.h


#pragma once
#include <iostream>
#include<memory>

//创建一颗N叉树最少需要三个指针,向上、向下、向右三个方向的指针,即:
//分别是指向父节点、指向当前节点的第一个孩子、指向当前节点的第一个兄弟


template <class T>
class NTree
{
public:
	NTree();
	~NTree();
	void insertRootNode(const T& data);
	//插入函数,定好规则。
	/*
	如果成为孩子(成为最小孩子的最小孩子);
	如果成为兄弟(成为最小孩子的最小兄弟)
	*/
	void insertNode(const T& data, bool bIsChile = true);

	//遍历树
	void traverTree(NTree *pTree);

	//查找某个数,找到则返回该值的地址信息,找不到返回null
	NTree *getNodePos(const T& data);

	//删除
	/*
	删除树的某个子树,删除成功返回true, 失败返回false
	删除前需要定一下删除的规则:
	如果删除的结点pDel有孩子
	1、pDel有兄弟,则pDel的孩子归兄弟领养,pDel的第一个兄弟成为pDel的父节点的孩子
	2、pDel没有兄弟,如果pDel的孩子成为其父节点的孩子
	*/
	bool deleteNode(const T& data);

	//查找删除点的其前一个兄弟
	NTree* getPreNodeBrother(const T& data);

	void showData(NTree *tree)
	{
		if (tree)
			std::cout << tree->data << std::endl;
		else
		{
			std::cout << "No find" << std::endl;
		}
	}

private:
	NTree *pParent;//指向父节点指针
	NTree *pBrother;//指向第一个兄弟指针
	NTree *pChild;	//指向第一个孩子指针
	T	  data;
};



//!!!!使用模板类的话,模板的定义和实现不能分开写,即不可再.h中定义,在.cpp中实现
//不是模板类的话是可以这样的
//解决办法是:1、在.h中来实现函数的定义
//2、在调用的地方,比如main.cpp中使用#include"NTree.cpp"而不是#include"NTree.h"
template <class T>
NTree<T>::NTree()
{
	pParent = pBrother = pChild = nullptr;
	data = 0;
}

template <class T>
NTree<T>::~NTree()
{
	pParent = nullptr;
}

template <class T>
void NTree<T>::insertRootNode(const T&data)
{
	this->data = data;
}

template<class T>
void NTree<T>::insertNode(const T & data, bool bIsChild)
{
	//创建新节点
	NTree *pNew = new NTree();
	memset(pNew, 0, sizeof(NTree));
	pNew->data = data;
	//找到要插入的节点位置那一层,即找到最小的孩子
	//遍历到最小的孩子
	NTree *pTemp = this;
	while (pTemp->pChild)
	{
		pTemp = pTemp->pChild;
	}

	if (bIsChild)
	{
		//新节点成为最小的孩子的孩子
		pTemp->pChild = pNew;
		//新节点指向父节点
		pNew->pParent = pTemp;
	}
	else
	{
		//遍历到最后一个兄弟
		while (pTemp->pBrother)
		{
			pTemp = pTemp->pBrother;
		}
		// 在最后一个兄弟后面插入新节点
		pTemp->pBrother = pNew;
		//新节点指向父节点
		pNew->pParent = pTemp->pParent;
	}
}


template <class T>
void NTree<T>::traverTree(NTree *pTree)
{
	NTree *pTemp = pTree;
	NTree *pTempBrother = nullptr;

	//遍历到树为null为止
	while (pTemp)
	{
		//先遍历兄弟
		pTempBrother = pTemp;
		while (pTempBrother)
		{
			std::cout << pTempBrother->data << " ";
			pTempBrother = pTempBrother->pBrother;
		}
		std::cout << std::endl;
		//再遍历孩子
		pTemp = pTemp->pChild;
	}
}

template <class T>
NTree<T>* NTree<T>::getNodePos(const T& data)
{
	NTree *pTemp = this;
	NTree *pTempBrother = nullptr;

	while (pTemp)
	{
		pTempBrother = pTemp;
		while (pTempBrother)
		{
			if (pTempBrother->data == data)
			{
				return pTempBrother;
			}
			pTempBrother = pTempBrother->pBrother;
		}

		pTemp = pTemp->pChild;
	}

	return nullptr;
}

//按值来删除
template <class T>
bool NTree<T>::deleteNode(const T &data)
{
	//1、先查找值是否在树中
	NTree *pDel = getNodePos(data);
	if (pDel == nullptr)
		return false;

	//判断是都是老大。父节点的孩子指针指向的是老大
	NTree *pDelParent = pDel->pParent;

	//如果parent为null,则要删除的为根节点
	if (pDelParent == nullptr)
	{
		//根节点有兄弟,第一个兄弟成为根节点
		if (pDel->pBrother)
		{
			//根节点有孩子,孩子成为根节点的兄弟的第一个孩子
			if (pDel->pChild)
			{
				pDel->pBrother->pChild = pDel->pChild;
				pDel->pChild->pParent = pDel->pBrother;
			}
			// 被删除节点的孩子的兄弟指向根节点的兄弟
			NTree *pTemp = pDel->pChild;
			while (pTemp)
			{
				pTemp->pParent = pDel->pBrother;
				pTemp = pTemp->pBrother;
			}
			//兄弟节点成为根节点
			pDel->data = pDel->pBrother->data;
			pDel->pBrother = pDel->pBrother->pBrother;

		}
		else//根节点无兄弟
		{
			//根节点有孩子,孩子成为根节点
			if (pDel->pChild)
			{
				/*
				类似于跳过删除点的孩子重新连线
				1、将删除点孩子的值付给删除的节点,
				2、然后将删除点孩子的兄弟付给删除点当兄弟
				3、删除点的孩子的孩子成为其孩子
				*/
				pDel->data = pDel->pChild->data;
				pDel->pBrother = pDel->pChild->pBrother;
				pDel->pChild = pDel->pChild->pChild;
			}
		}
		return true;
	}
	//删除的不是根节点
	else
	{
		//要删除的节点是老大
		if (pDelParent->pChild == pDel)
		{
			//老大有兄弟,删除点的兄弟成为第一个孩子
			if (pDel->pBrother)
			{
				pDel->data = pDel->pBrother->data;
				pDelParent->pChild = pDel->pBrother;
				pDel->pBrother->pChild = pDel->pChild;
			}
			//老大没兄弟
			else
			{
				//有孩子,其删除点孩子成为其父亲的孩子
				if (pDel->pChild)
				{
					pDel->data = pDel->pChild->data;
					pDelParent->pChild = pDel->pChild;
					//删除点孩子的兄弟指向删除点的父亲
					NTree *pTemp = pDel->pChild;
					while (pTemp)
					{
						pTemp->pParent = pDelParent;
						pTemp = pTemp->pBrother;
					}
				}
				//无孩子,表示到尾部了,删除点的父节点指向null
				else
				{
					pDelParent->pChild = nullptr;
				}
			}
		}
		else//要删除的节点不是老大
		{
			//有兄弟,则前一个兄弟指向其后一个兄弟
			if (pDel->pBrother)
			{
				NTree *pPreNodeBrother = getPreNodeBrother(data);
				pPreNodeBrother->pBrother = pDel->pBrother;
#if 0
				NTree *pDelTempBrother = pDel->pBrother;
				if (pDel->pBrother)
				{
					pDel->data = pDel->pBrother->data;
					//pDelTempBrother->pParent = pDelTempBrother->pParent;
					pDel->pBrother = pDel->pBrother->pBrother;
				}
				delete pDelTempBrother;
				pDelTempBrother = nullptr;
#endif
			}
			else
			{
				//无兄弟的时候,先获取其前一个兄弟的地址,然后让前一个兄弟的兄弟指向nullptr
				NTree *pPreNodeBrother = getPreNodeBrother(data);
				pPreNodeBrother->pBrother = nullptr;
			}
		}
	}
	delete pDel;
	return true;
}

template <class T>
NTree<T>* NTree<T>::getPreNodeBrother(const T& data)
{
	NTree *pTemp = this;
	NTree *pTempBrother = nullptr;

	while (pTemp)
	{
		pTempBrother = pTemp;
		while (pTempBrother->pBrother)
		{
			if (pTempBrother->pBrother->data == data)
			{
				return pTempBrother;
			}
			pTempBrother = pTempBrother->pBrother;
		}

		pTemp = pTemp->pChild;
	}

	return nullptr;
}

 

//main.cpp
#include "NTree.h"

int main()
{
	NTree<int> tree;
	//tree.insertRootNode(0);
	tree.insertNode(1);
	tree.insertNode(2, false);
	tree.insertNode(3, false);
	tree.insertNode(4);
	tree.insertNode(5, false);
	tree.insertNode(7, false);
	tree.insertNode(8, false);
	tree.insertNode(6);
	tree.traverTree(&tree);
	tree.deleteNode(5);
	tree.traverTree(&tree);

	//tree.showData(tree.getNodePos(6));
	return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值