数据结构:关于二叉树的一些操作
template<class ElemType> struct BinaryTreeNode { ElemType data; BinaryTreeNode* LChild, * RChild; BinaryTreeNode() : LChild(NULL), RChild(NULL) {} //构造函数1,用于构造根结点 BinaryTreeNode(const ElemType& item, BinaryTreeNode* Lptr = NULL, BinaryTreeNode* Rptr = NULL) //构造函数2,用于构造其他结点 //函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面 { LChild = Lptr; RChild = Rptr; data = item; } ElemType getData() { return data; } //取得结点中的数据 void SetLChild(BinaryTreeNode* link) { LChild = link; } //修改结点的左孩子域 void SetRChild(BinaryTreeNode* link) { RChild = link; } //修改结点的右孩子域 void SetData(ElemType value) { data = value; } //修改结点的data域 BinaryTreeNode* GetLChild() const { return LChild; } //获取左孩子结点 BinaryTreeNode* GetRChild() const { return RChild; } //获取左孩子结点 };
但是其实由于结构体成员默认是public,所以其实我们不必定义那么多成员函数,通过->可以直接访问、修改.所以我们定义结构体只要简单的定义:
//二叉树的结点 template<class ElemType> struct BinaryTreeNode { ElemType data; BinaryTreeNode* LChild, * RChild; };
要访问左孩子的数据:只要x->LChild->data、修改右孩子的数据、只要x->RChild->data=....即可.但是我下面的代码都利用的是第一种比较复杂的,就是不破坏ADT抽象类数据类型的封装性,在假设我一开始不知道struct内的数据成员名时,只知道成员函数时进行操作。
二、链式二叉树ADT模板类的构建
先是模板类的构建,包括一些基本的函数:生成树,有参无参构造函数、返回根结点,返回根节点的元素值,销毁树,销毁子树,判断二叉树是否为空:,当然,拷贝构造函数和重载=号还没写,等有用到再来完善。
//链式二叉树 template<class ElemType> class BinaryTree { private: BinaryTreeNode<ElemType>* root; // 头指针 void BinaryTreeDestroy_Cursive(BinaryTreeNode<ElemType>* T) //销毁树(递归准备,private) { if (T == NULL) return; BinaryTreeDestroy_Cursive(T->LChild()); BinaryTreeDestroy_Cursive(T->RChild()); delete T; } public: //无参数的构造函数 BinaryTree() :root(NULL) {} //带参数的构造函数 BinaryTree(const ElemType& item) { root = new BinaryTreeNode<ElemType>(item); } //生成树 void makeBinaryTree(const ElemType& item, BinaryTree& left, BinaryTree& right) { BinaryTreeNode<ElemType>* t = new BinaryTreeNode<ElemType>; t->SetData(item); t->SetLChild(left); t->SetRChild(right); root = t; } //判断二叉树是否为空 bool BinaryTreeisEmpty()const { return root == NULL; } //获取根结点元素值 ElemType GetRootData() const { return root->data; } //设置根结点 void SetRoot(BinaryTreeNode<ElemType>* p) { root = p; } //获取根结点 BinaryTreeNode<ElemType>* GetRoot() const { return root; } //销毁树 void BinaryTreeDestroy() { BinaryTreeDestroy_Cursive(root); } //销毁子树 void ChildDestroy(BinaryTreeNode<ElemType>* r, int flag)//flag=1销毁左子树 flag=0销毁右子树 { if (flag) { BinaryTreeDestroy_Cursive(r->GetLChild()); } else { BinaryTreeDestroy_Cursive(r->GetRChild()); } } //拷贝构造函数 // BinaryTree(BinaryTree& b) //析构函数 // ~BinaryTree() // { BinaryTreeDestroy(); } //重载函数:赋值 //LinkList& operator=(LinkList&List); }
三、成员函数:location 函数
传入一个元素,一个引用的空结点指针p,和一个带查找的头结点指针r,利用递归来查找是否有结点元素为x,如果有,就返回true,并且让p指向这个结点,否则返回false,并且p为NULL。前提是前提是只有一个结点为x。
bool Location(ElemType& x, BinaryTreeNode<ElemType>* r, BinaryTreeNode<ElemType>*& p) { if (!r) { p = NULL; return false; } else if (r->getData() == x) { p = r; return true; } else if (Location(x, r->GetLChild(), p) || Location(x, r->GetRChild(), p)) { return true; } else return false; }
四、成员函数:递归的前序后序中序遍历二叉树
//前序遍历 void PreOrderTraverse(BinaryTreeNode<ElemType>* T) const { if (T) { cout << T->getData(); PreOrderTraverse(T->GetLChild()); PreOrderTraverse(T->GetRChild()); } } //中序遍历 void InOrderTraverse(BinaryTreeNode<ElemType>* T) const { if (T) { InOrderTraverse(T->GetLChild()); cout << T->getData(); InOrderTraverse(T->GetRChild()); } } //后序遍历 void PostOrderTraverse(BinaryTreeNode<ElemType>* T) const { if (T) { PostOrderTraverse(T->GetLChild()); PostOrderTraverse(T->GetRChild()); cout << T->getData(); } }
五、成员函数:递归的前序遍历二叉树的构建
传入一个数组,和空指针的符号empty :就是数组中的empty就表示空指针,数组是前序遍历二叉树的序列,来构建二叉树,再传入一个参数n代表递归到了第几个数字,方便递归
//建立二叉树的存储结构 BinaryTreeNode<ElemType>* CreateBinaryTree(vector<ElemType>& x, ElemType& empty, int& n) { ElemType ch = x[n]; n++; if (ch == empty) { return NULL; } else { BinaryTreeNode<ElemType>* Node = new BinaryTreeNode<ElemType>; Node->data = ch; Node->LChild = CreateBinaryTree(x, empty, n); Node->RChild = CreateBinaryTree(x, empty, n); return Node; } }
六、外部函数:递归计算二叉树结点数
传入一个树的根
//计算二叉树的节点数 template<class ElemType> int BinaryTreeSize(BinaryTreeNode<ElemType>* T) { if (T == NULL) return 0; else return (BinaryTreeSize(T->GetLChild()) + BinaryTreeSize(T->GetRChild()) + 1); }
七、外部函数:替换左右子树
传入一个二叉树的根 也可以是子树的根
//替换左右子树 template<class ElemType> void BinaryTree_Revolute_Cursive(BinaryTreeNode<ElemType>* root) { if (root->GetLChild()) { BinaryTree_Revolute_Cursive(root->GetLChild()); } if (root->GetRChild()) { BinaryTree_Revolute_Cursive(root->GetRChild()); } BinaryTreeNode<ElemType>* temp = root->GetLChild(); root->SetLChild(root->GetRChild()); root->SetRChild(temp); return; }
八、外部函数:求二叉树的深度
传入一个二叉树的根
//求二叉树的层数 template<class ElemType> int Cal_BinaryTree_Layers(BinaryTreeNode<ElemType>* T) { if (!T) return 0; else { return max(Cal_BinaryTree_Layers(T->GetLChild()), Cal_BinaryTree_Layers(T->GetRChild())) + 1; } }
八、外部函数:求以结点x为根的深度
//求结点x为头结点的深度 template<class ElemType> int GetBinaryTreeHeight(BinaryTree<ElemType>& T, ElemType& x) { BinaryTreeNode<ElemType>* y = NULL; T.Location(x, T.GetRoot(), y); if (!y) return 0; return Cal_BinaryTree_Layers(y); }
九、外部函数:以结点x的双亲
有两个重载:结点x为指针或者给的是data
重载1:
template<class ElemType>//重载1 利用data寻找双亲结点 BinaryTreeNode<ElemType>* getparent(BinaryTreeNode<ElemType>* root, ElemType& x) { if (root->GetLChild() && root->GetLChild()->getData() == x) { return root; } if (root->GetRChild() && root->GetRChild()->getData() == x) { return root; } if (root->GetLChild()) { return getparent(root->GetLChild(), x); } if (root->GetRChild()) { return getparent(root->GetRChild(), x); } if (!root->GetLChild() && !root->GetRChild()) { return NULL; } }
重载2:
template<class ElemType>//重载2 利用指针寻找双亲结点 BinaryTreeNode<ElemType>* getparent(BinaryTreeNode<ElemType>* root, BinaryTreeNode<ElemType>* p) { if (root->GetLChild() && root->GetLChild() == p) { return root; } if (root->GetRChild() && root->GetRChild() == p) { return root; } if (root->GetLChild()) { return getparent(root->GetLChild(), p); } if (root->GetRChild()) { return getparent(root->GetRChild(), p); } if (!root->GetLChild() && !root->GetRChild()) { return NULL; } }
十、外部函数:求结点x的兄弟结点
flag :0代表求左兄弟 1代表求右兄弟
//求x的兄弟结点 flag :0代表求左兄弟 1代表求右兄弟 template<class ElemType> BinaryTreeNode<ElemType>* GetSibling(BinaryTree<ElemType>& T, ElemType& x, int flag) { BinaryTreeNode<ElemType>* temp; if (!T.Location(x, T.GetRoot(), temp)) return NULL; BinaryTreeNode<ElemType>* ans = getparent(T.GetRoot(), x); if (!ans) return NULL; if (!flag) { if (x == ans->GetLChild()->getData()) { return NULL; } else { return ans->GetLChild(); } } if (flag) { if (x == ans->GetRChild()->getData()) { return NULL; } else { return ans->GetRChild(); } } }
十一、外部函数:删除以x为根节点的孩子
flag :0代表求左兄弟 1代表求右兄弟
template<class ElemType> bool DeleteChild(BinaryTree<ElemType>& T, ElemType& x, int flag) { BinaryTreeNode<ElemType>* temp; T.Location(x, T.GetRoot(), temp); if (!temp) return false; if (!flag) { if (temp->GetLChild())//子树本来为空也算删除失败 temp->SetLChild(NULL); else return 0; } else { if (temp->GetRChild()) temp->SetRChild(NULL); else return 0; } return true; }
十二、外部函数:求叶子结点数
//求叶子结点数 template<class ElemType> int count0(BinaryTreeNode<ElemType>* r) { if (!r->GetLChild() && !r->GetRChild()) return 1; if (r->GetLChild() && r->GetRChild()) return count0(r->GetLChild()) + count0(r->GetRChild()); else if (r->GetLChild()) return count0(r->GetLChild()); else if (r->GetRChild()) return count0(r->GetRChild()); } template<class ElemType> int LeafCount(BinaryTree<ElemType>& T)//求叶子结点数 { return count(T.GetRoot()); }
十三、外部函数:求叶子结点数
//度为2的节点数 template<class ElemType> int count2(BinaryTreeNode<ElemType>* r) { if (!r->GetLChild() && !r->GetRChild()) return 0; if (r->GetLChild() && r->GetRChild()) return count2(r->GetLChild()) + count2(r->GetRChild()) + 1; else if (r->GetLChild()) return count2(r->GetLChild()); else if (r->GetRChild()) return count2(r->GetRChild()); } //度为2的节点数 template<class ElemType> int CountDegreeTwo(BinaryTree<ElemType>& T) { return count2(T.GetRoot()); }
十四、外部函数:输出根到结点x的路径
可能有多个x结点
vector<BinaryTreeNode<string>*>q;//路径数组 //输出某一个顶点到某一个结点的路径 template<class ElemType> void dfs_path(BinaryTreeNode<ElemType>* p, ElemType s) { q.push_back(p); if (p->getData() == s) { int i = 0; for (i = 0; i < q.size() - 1; ++i) cout << q[i]->getData() << "->"; cout << q[i]->getData() << endl; } if (p->GetLChild()) { dfs_path(p->GetLChild(), s); } if (p->GetRChild()) { dfs_path(p->GetRChild(), s); } q.pop_back();//回溯出队 } template<class ElemType> void finds_path(BinaryTree<ElemType>& T, ElemType s) { dfs_path(T.GetRoot(), s); }
十五、外部函数:非递归的先序中序后序遍历
//非递归的先序遍历输出 template<class ElemType> void preorder(BinaryTree<ElemType>& T) { stack<BinaryTreeNode<ElemType>*>s; BinaryTreeNode<ElemType>* root = T.GetRoot(); while (root || !s.empty()) { //go left down to the ground while (root) { pre.push_back(root->getData()); s.push(root); root = root->GetLChild(); } root = s.top()->GetRChild(); s.pop(); } } //非递归的中序遍历输出 template<class ElemType> void inorder(BinaryTree<ElemType>& T) { stack<BinaryTreeNode<ElemType>*>s; BinaryTreeNode<ElemType>* root = T.GetRoot(); while (root || !s.empty()) { while (root) { s.push(root); root = root->GetLChild(); } in.push_back(s.top()->getData()); root = s.top()->GetRChild(); s.pop(); } } //非递归的后序遍历输出 template<class ElemType> void postorder(BinaryTree<ElemType>& T) { stack<BinaryTreeNode<ElemType>*>s; BinaryTreeNode<ElemType>* root = T.GetRoot(); while (root || !s.empty()) { while (root) { post.push_back(root->getData()); s.push(root); root = root->GetRChild(); } root = s.top()->GetLChild(); s.pop(); } reverse(post.begin(), post.end()); }
十六、外部函数:层次遍历构建二叉树
传入一个层次遍历的数组,构建二叉树,利用队列:
template<class ElemType> void CreateTree_Layer(BinaryTree<ElemType>& T, ElemType& str, ElemType& empty)//层次结构建立二叉树 { vector<string>s = depart_string(str); queue<BinaryTreeNode<ElemType>*>q; int b = 0; BinaryTreeNode<ElemType>* temp = new BinaryTreeNode<ElemType>;; createnode(temp, s[b]); q.push(temp); T.SetRoot(temp); while (!q.empty()) { BinaryTreeNode<ElemType>* x = q.front(); q.pop(); if (b == s.size() - 1)//关键点 ①:他最后的空指针有可能缺省 { x->SetLChild(NULL); x->SetRChild(NULL); continue; } if (s[++b] != empty) { BinaryTreeNode<ElemType>* t = new BinaryTreeNode<ElemType>;; createnode(t, s[b]); x->SetLChild(t); q.push(t); } else { x->SetLChild(NULL); } if (b == s.size() - 1)//关键点 ②:他最后的空指针有可能缺省 可能只写了最后一个结点的左边空指针,没有写右边空指针 { x->SetRChild(NULL); continue; } if (s[++b] != empty) { BinaryTreeNode<ElemType>* t = new BinaryTreeNode<ElemType>;; createnode(t, s[b]); x->SetRChild(t); q.push(t); } else { x->SetRChild(NULL); } } }
十七、求两个结点A、B最近的祖先
利用到了前面求路径函数,我们加以改造,加入一个引用型vector数组ans,在到达目的地结点时,将整个q数组copy到ans里,这样ans里面就是根到A的路径,再求出根到B的路径。 然后我们先pop_back一下两个ans数组,因为是求共同的祖先,如果肯定不能算自己。然后把两个数组利用algorithm的函数reverse一下,这样可以保证离结点最近的祖先在数组的前面,先遍历到,因为是求最近的祖先。然后用二重循环遍历两个数组先出现的相同的元素,就是对于比较短的路径的每一个祖先,在长的路径中寻找看是否出现过,出现过就是最近的。对于数组的长短,我们可以if判断一下,默认a短b长,如果a长,就swap一下。
求路径数组:
/把根节点到结点的路径存储在ans中 template<class ElemType> void dfs_path(BinaryTreeNode<ElemType>* p, ElemType s, vector<BinaryTreeNode<string>*>& ans) { q.push_back(p); if (p->getData() == s) { for (int i = 0; i < q.size(); ++i) ans.push_back(q[i]); return; } if (p->GetLChild()) { dfs_path(p->GetLChild(), s,ans); } if (p->GetRChild()) { dfs_path(p->GetRChild(), s,ans); } q.pop_back();//回溯出队 } template<class ElemType> vector<BinaryTreeNode<string>*>find_path_vec(ElemType x,BinaryTree<ElemType>&T) { q.clear(); vector<BinaryTreeNode<string>*>ans; dfs_path(T.GetRoot(),x,ans); return ans; }
求最近共同祖先:
//寻找两个结点最近的共同祖先 template<class ElemType> ElemType findtwosame(BinaryTree<ElemType>& T, ElemType a, ElemType b) { vector<BinaryTreeNode<string>*>ans1 = find_path_vec(a,T); vector<BinaryTreeNode<string>*>ans2 = find_path_vec(b,T); ans1.pop_back();//关键点1 不能算自己 ans2.pop_back(); reverse(ans1.begin(), ans1.end());//关键点2 reverse一下便于寻找祖先 reverse(ans2.begin(), ans2.end()); ElemType ans; int i = 0; int l1 = ans1.size(); int l2 = ans2.size(); if (l1 > l2) { vector<BinaryTreeNode<string>*>temp = ans1; ans1 = ans2; ans2 = temp; } for (int i = 0; i < l1; ++i) { for (int j = 0; j < l2; ++j) { if (ans1[i]->getData()== ans2[j]->getData()) { ans = ans1[i]->getData(); return ans; } } } }
目前只运用到这些代码,有其他新的,我会加以补充。