一个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;
}