目录
1. 问题提出
今天在写二叉搜索树的模板类的时候选择继承了二叉树的模板类,但在二叉搜索树的方法内部直接调用二叉树的成员变量m_root(根节点)和m_size(记录存储的元素个数)时,系统报错,查询了相关资料发现是因为c++标准中,模板类采取的是二段名字查找方法,先上代码
2. 代码实现
#ifndef BINARYTREE_H
#define BINARYTREE_H
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
//节点类
template<typename E>
class Node{
public:
E m_element;
//左子节点
Node<E>*m_left;
//右子节点
Node<E>*m_right;
//父节点
Node<E>*m_parent;
Node(E element,Node<E>*parent){
m_element=element;
m_parent=parent;
m_left=0;
m_right=0;
}
//判断节点是否为叶子节点
bool isLeaf(){
return (m_left==NULL)&&(m_right==NULL);
}
bool hasTwoChildren(){
return (m_left!=NULL)&&(m_right!=NULL);
}
};
template<typename T>
class BinaryTree{
private:
//前序遍历
void preorderTraversal(Node<T>*node){
if(node==NULL) return;
cout<<node->m_element<<" ";
preorderTraversal(node->m_left);
preorderTraversal(node->m_right);
}
//中序遍历
void inorderTraversal(Node<T>*node){
if(node==NULL) return;
inorderTraversal(node->m_left);
cout<<node->m_element<<" ";
inorderTraversal(node->m_right);
}
//后序遍历
void postorderTraversal(Node<T>*node){
if(node==NULL) return;
postorderTraversal(node->m_left);
postorderTraversal(node->m_right);
cout<<node->m_element<<" ";
}
//递归实现获得二叉树的高度
int height1(Node<T>*node){
if(node==NULL) return 0;
return max(height1(node->m_left),height1(node->m_right))+1;
}
//迭代实现获得二叉树的高度
int height2(Node<T>*node){
if(node==NULL) return 0;
int height=0;
//存储每一层的元素数量
int levelSize=1;
queue<Node<T>*>q;
q.push(node);
while(!q.empty()){
Node<T>*temp=q.front();
q.pop();
levelSize--;
if(temp->m_left!=NULL){
q.push(temp->m_left);
}
if(temp->m_right!=NULL){
q.push(temp->m_right);
}
if(levelSize==0){
height++;
levelSize=q.size();
}
}
return height;
}
//递归翻转二叉树
void invert1(Node<T>*node){
if(node==NULL) return;
Node<T>*temp=node->m_right;
node->m_right=node->m_left;
node->m_left=temp;
invert(node->m_left);
invert(node->m_right);
}
//迭代翻转二叉树
void invert2(Node<T>*node){
if(node==NULL) return;
queue<Node<T>*>q;
q.push(node);
while(!q.empty()){
Node<T>*temp=q.front();
q.pop();
Node<T>*tmp=temp->m_left;
temp->m_left=temp->m_right;
temp->m_right=tmp;
if(temp->m_left!=NULL){
q.push(temp->m_left);
}
if(temp->m_right!=NULL){
q.push(temp->m_right);
}
}
}
protected:
int m_size;
//根节点
Node<T>*m_root;
//前驱结点,中序遍历中前一个节点
Node<T>*predesessor(Node<T>*node){
if(node==NULL) return NULL;
//有左子树
if(node->m_left!=NULL){
Node<T>*p=node->m_left;
while(p->m_right!=NULL){
p=p->m_right;
}
return p;
}
//无左子树
if(node->m_left==NULL){
while((node->m_parent!=NULL)&&(node==node->m_parent->m_left)){
node=node->m_parent;
}
//parent为空,或者node为父节点的右子节点
return node->m_parent;
}
}
//后继节点
Node<T>*successor(Node<T>*node){
if(node==NULL) return NULL;
//有右子树
if(node->m_right!=NULL){
Node<T>*s=node->m_right;
while(s->m_left!=NULL){
s=s->m_left;
}
return s;
}
//无右子树
if(node->m_right==NULL){
while((node->m_parent!=NULL)&&(node==node->m_parent->m_right)){
node=node->m_parent;
}
}
//parent为空,或者node为父节点的左子节点
return node->m_parent;
}
//用于获得节点
Node<T>*node(T element){
Node<T>*curr=m_root;
while(curr!=NULL){
int cmp=compare(element,curr->m_element);
if(cmp==0) return curr;
if(cmp>0){
curr=curr->m_right;
}
if(cmp<0){
curr=curr->m_left;
}
}
return NULL;
}
//返回值等于0代表e1==e2,返回值大于0代表e1大于e2,返回值小于0,e1小于e2
int compare(T&e1,T&e2){
if(e1>e2) return 1;
if(e1<e2) return -1;
else return 0;
}
public:
BinaryTree(){
m_size=0;
m_root=0;
}
int size(){return m_size;}
bool empty(){return m_size==0;}
void clear(){
queue<Node<T>*>q;
q.push(m_root);
while(!q.empty()){
Node<T>*temp=q.front();
q.pop();
if(temp->m_left!=NULL){
q.push(temp->m_left);
}
if(temp->m_right!=NULL){
q.push(temp->m_right);
}
delete temp;
}
m_size=0;
}
//前序遍历接口
void preorderTraversal(){
preorderTraversal(m_root);
}
//中序遍历接口
void inorderTraversal(){
inorderTraversal(m_root);
}
//后序遍历接口
void postorderTraversal(){
postorderTraversal(m_root);
}
//层序遍历
void levelorderTraversal(){
if(m_root==NULL) return;
queue<Node<T>*>q;
q.push(m_root);
while(!q.empty()){
Node<T>*temp=q.front();
q.pop();
cout<<temp->m_element<<" ";
if(temp->m_left!=NULL){
q.push(temp->m_left);
}
if(temp->m_right!=NULL){
q.push(temp->m_right);
}
}
}
//判断是否为完全二叉树
bool isComplete(){
if(m_root==NULL) return false;
queue<Node<T>*>q;
q.push(m_root);
bool leaf=false;
while(!q.empty()){
Node<T>*temp=q.front();
q.pop();
if(leaf&&(!temp->isLeaf())){
return false;
}
if(temp->hasTwoChildren()){
q.push(temp->m_left);
q.push(temp->m_right);
}else if((temp->m_left==NULL)&&(temp->m_right!=NULL)){
return false;
}else{
//左不为空,右为空或者左右都为空
//从下一个节点开始,全是叶子节点
leaf=true;
}
}
return true;
}
//高度接口
int height(){
return height2(m_root);
}
//翻转二叉树接口
void invert(Node<T>*node){
invert2(node);
}
};
#endif
这是BinarySearchTree.h文件(已修改过)
#ifndef BINARYSEARCHTREE_H
#define BINARYSEARCHTREE_H
#include"..\05-BinaryTree\BinaryTree.h"
template<typename T>
class BinarySearchTree:public BinaryTree<T>{
private:
//删除curr节点
void remove(Node<T>*curr){
if(curr==NULL) return;
//度为2
if(curr->hasTwoChildren()){
//找到后继节点
Node<T>*s=this->successor(curr);
//覆盖
curr->m_element=s->m_element;
curr=s;
}
//删除curr,curr必然是度为1或是0
Node<T>*replacement=0;
if(curr->m_left!=NULL){
replacement=curr->m_left;
}else{
replacement=curr->m_right;
}
//curr是度为1的节点
if(replacement!=NULL){
replacement->m_parent=curr->m_parent;
//curr为度为1的节点,并且是根节点
if(curr->m_parent==NULL){
this->m_root=replacement;
delete curr;
}
if(curr==curr->m_parent->m_left){
curr->m_parent->m_left=replacement;
delete curr;
}else if(curr==curr->m_parent->m_right){
curr->m_parent->m_right=replacement;
}
}else{
//curr为叶子节点
//是根节点
if(curr->m_parent==NULL){
delete this->m_root;
this->m_root=0;
}else{
//curr是叶子节点,但不是根节点
Node<T>*parent=curr->m_parent;
if(curr==parent->m_left){
delete curr;
parent->m_left=NULL;
}else{
delete curr;
parent->m_right=NULL;
}
}
}
this->m_size--;
}
public:
BinarySearchTree(){
this->m_size=0;
this->m_root=0;
}
void add(T element){
//是添加的第一个节点
if(this->m_root==NULL){
this->m_root=new Node<T>(element,NULL);
this->m_size++;
return;
}
//添加的不是第一个节点
Node<T>*node=this->m_root;
//存父节点
Node<T>*parent=this->m_root;
int cmp=0;
while(node!=NULL){
cmp=this->compare(element,node->m_element);
parent=node;
if(cmp>0){
node=node->m_right;
}else if(cmp<0){
node=node->m_left;
}else{
//覆盖
//自定义对象
node->m_element=element;
}
}
Node<T>*newNode=new Node<T>(element,parent);
if(cmp>0){
parent->m_right=newNode;
}else{
parent->m_left=newNode;
}
this->m_size++;
}
//删除element的节点
void remove(T element){
remove(this->node(element));
}
};
#endif
3. 导致的原因
原先在binarySearchTree.h文件中,我并没有加入this指针,vscode报错提示为m_size,m_root,node都未声明(not declared)。查阅了相关资料之后发现,c++标准中,对于模板类中名字查找,分为两个阶段进行:
1. 第一阶段是模板定义阶段。系统只检查语法的正确性,并且只处理独立(non-dependent)(指该变量不依赖传进来的参数模板)的变量。因为BinaryTree依赖于BinarySearchTree的参数列表T来实现,所以BinaryTree是非独立的,于是编译器认为BinaryTree<T>不存在,但仍合法,所以认为BinaryTree<T>的所有函数和变量都为非成员属性。这也是not decalred的原因
2.第二阶段是模板实例化阶段。系统会处理非独立的变量。
4. 解决方法
添加了this指针之后,编译器对m_size, m_root和node函数的查找会推迟到第二阶段,因为相当于告诉编译器,这就是成员函数或者属性。在子类找不到便会去父类中查找
5. 参考链接
1. Two phase name lookup for C++ templates-Why?
https://stackoverflow.com/questions/12561544/two-phase-name-lookup-for-c-templates-why
2. c++模板&二段式名字查找