二叉树操作的总结

二叉树操作的总结

1.根据前序序列和中序序列创建二叉树
2.求二叉树中节点的个数
3.求二叉树中叶子节点的个数
4.求二叉树的高度
5.求二叉树的宽度
6.求二叉树第K层的节点个数
7.判断二叉树是否是完全二叉树
8.求二叉树中节点的最大距离
9.求二叉树中两个节点的最近公共祖先节点
10.输出一个二叉树的前序序列
11.输出一个二叉树的中序序列
12.输出一个二叉树的后序序列
13.输出一个二叉树的层次序列
14.左右子树互换
15.判断两棵树是否是相同
16.判断第二棵树是否是第一棵树的子树
17.合并两棵二叉树
18.复制一棵二叉树

操作1是创建二叉树是后面操作的基础,输入是2个string类型的字符串
二叉树tree是有限个元素的集合(可以为空),当二叉树非空时,其中一个元素为根,余下的元素(如果有的话)被划分为两棵二叉树,分别为tree的左子树和右子树
因为二叉树的定义就是一种递归的定义,因此很多操作都可以用递归的方法来实现
所有操作都已经验证

下面是操作中会用到的数据结构

// 树的节点
struct treeNode
{
	char element; 
	treeNode *leftChild;
	treeNode *rightChild;
	treeNode()
	{
		leftChild = rightChild = NULL;
	}
	treeNode(char theElement)
	{
		element = theElement;
		leftChild = rightChild = NULL;
	}
	treeNode(char theElement, treeNode *theLeftChild, treeNode *theRightChild)
	{
		element = theElement;
		leftChild = theLeftChild;
		rightChild = theRightChild;
	}
};

// 队列的数据结构 
class arrayQueue
{
	public:
		treeNode* queue[100];

		arrayQueue()
		{
			queueFront = -1;
			elementSize = 0;
			queueBack = -1; 		
}
		void push(treeNode *theTreeNode)
		{
			queueBack = (queueBack+1)%100;
			queue[queueBack] = theTreeNode;
			elementSize++;
		}
		void pop()
		{
			queueFront = (queueFront+1)%100;
delete queue[queueFront];
			elementSize--;
		}
		treeNode* front()
		{
			return queue[(queueFront+1)%100]; 
		}
		bool empty()
		{
			return elementSize == 0;
		}
	private:
		int queueFront; // 队首元素的前一个索引
		int elementSize;
		int queueBack; // 队尾元素的索引

};

1.根据前序序列和中序序列创建二叉树
思想:递归求解,前序序列的第一个节点是根节点,在中序序列中找到该根节点,其左半部分为左子树,右半部分为右子树,对左子树和右子树继续执行该过程

treeNode* makeTree(string str1, string str2, int degree)
{ 
	// str1为前序序列 str2为中序序列 degree为二叉树的度 
	if (degree == 0)
		return NULL;
	int k = 0;
	while (str1[0] != str2[k])
		k++;
	treeNode *tree = new treeNode(str1[0]);
	tree->leftChild = makeTree(str1.substr(1, k), str2.substr(0, k), k);
	tree->rightChild = makeTree(str1.substr(k+1, degree-k-1), str2.substr(k+1, degree-k-1), degree-k-1);
	return tree;
}  

2.求二叉树中节点的个数
思想:递归求解,二叉树的节点个数=左子树的节点数+右子树的节点数+根节点

int getNums(treeNode* tree)
{
	if (tree == NULL)
		return 0;
	return getNums(tree->leftChild) + getNums(tree->rightChild) + 1;
}  

3.求二叉树中叶子节点的个数
思想:递归求解,叶子节点是自身非空,左右子节点都为空。计算左右子节点都为空的节点数目。

int getLeafNums(treeNode* tree)
{
	if (tree == NULL)
		return 0;
	if (tree->leftChild == NULL && tree->rightChild == NULL)
		return 1;
	int leftNums = getLeafNums(tree->leftChild);
	int rightNums = getLeafNums(tree->rightChild);
	return leftNums + rightNums;	
} 

4.求二叉树的高度
思想:递归求解,二叉树的高度=max(左子树的高度, 右子树的高度) + 1

int getHeight(treeNode* tree)
{
	if (tree == NULL)
		return 0;
	int maxLeft = getHeight(tree->leftChild);
	int maxRight = getHeight(tree->rightChild);
	return maxLeft > maxRight ? (maxLeft+1) : (maxRight+1);
}

5.求二叉树的宽度
思想:树的宽度是指,元素最多的那一层的元素数量
递归求解,计算每一层的宽度,求所有宽度中的最大值

void getWidth(treeNode* tree, int result[], int p)
{
// result数组初始化为0,p为当前的层数初始时传入1
// 在main函数中,result数组中的最大值即为二叉树的宽度,或者在该方法末尾加一
// 个if(p==1) {求数组最大值}
	if (tree != NULL)
	{
		result[p] += 1;
		getWidth(tree->leftChild, result, p+1);
		getWidth(tree->rightChild, result, p+1);
	}
} 

6.求二叉树第k层的节点个数
思想:递归求解,二叉树第k层节点的个数=第k-1层左子树的数目+第k-1层右子树的数目

int getLevelNums(treeNode* tree, int k)
{
	if (tree == NULL || k == 0)
		return 0;
	if (k == 1)
		return 1;
	int leftNums = getLevelNums(tree->leftChild, k-1);
	int rightNums = getLevelNums(tree->rightChild, k-1);
	return leftNums + rightNums;
} 

7.判断二叉树是否是完全二叉树
思想:完全二叉树中只有最后一层的右侧才可以有空节点
层序遍历,一旦一个节点含有空子树,之后所有节点的子树都为空才是完全二叉树

bool isCompleteBinaryTree(treeNode* tree)
{
	if (tree == NULL)
		return true;
	arrayQueue q;
	bool haveNULL = false;
	treeNode* root = tree;
	q.push(root);
	while (!q.empty())	
	{
		root = q.front();
		q.pop();
		if (haveNULL)
		{
			if (root->leftChild != NULL || root->rightChild != NULL)
				return false;
		}
		else
		{
			if (root->leftChild != NULL && root->rightChild != NULL)  
			{
				// 左右非空
				q.push(root->leftChild);
				q.push(root->rightChild);
			}
			else if (root->leftChild != NULL && root->rightChild == NULL)
			{
				// 左非空右空
				haveNULL = true;
				q.push(root->leftChild); 
			}
			else if (root->leftChild == NULL && root->rightChild != NULL)
			{
				// 左空右非空 
				return false;
			}
			else
			{
				// 左右为空
				haveNULL = true; 
			}
		}		
	}
	return true;
} 

8.求二叉树中节点的最大距离
思想:该距离是指任意两个节点相连的路径长度的最大值,一定要理解好这个概念
树为空:距离为0;
树非空:左子树中的最大距离,右子树中的最大距离,从左子树到根再到右子树的最大距离;
这两个节点可能分别在根节点的左右子树上,也可能在根节点的同一棵子树上。

int getMaxDistance(treeNode* tree, int& maxLeft, int& maxRight)
{
	// maxLeft和maxRight分别为左右子树中的节点到根的最大距离
// 初始时传入maxLeft = maxRight = 0
	if (tree == NULL)
	{
		maxLeft = 0;
		maxRight = 0;
		return 0;	
	}	
	// 下面四个变量表示当前节点的左右子树中,到该节点左右子节点的最大距离 
	int maxLL = 0, maxLR = 0, maxRL = 0, maxRR = 0; 
    int maxDistLeft = 0, maxDistRight = 0; // 左、右子树中的最大距离
    if (tree->leftChild) {
        maxDistLeft = getMaxDistance(tree->leftChild, maxLL, maxLR);
        maxLeft = max(maxLL, maxLR) + 1;
    } else {
        maxDistLeft = 0;
        maxLeft = 0;
    }
    if (tree->rightChild) {
        maxDistRight = getMaxDistance(tree->rightChild, maxRL, maxRR);
        maxRight = max(maxRL, maxRR) + 1;
    } else {
        maxDistRight = 0;
        maxRight = 0;
    }
    return max(maxLeft+maxRight, max(maxDistLeft, maxDistRight)); 
} 

9.求二叉树中两个节点的最近公共祖先节点
思想:递归求解,如果两个节点分别在根节点的左子树和右子树中,那么最近公共祖先节点是根节点。如果两个都在左子树或都在右子树,则递归处理左子树或递归处理右子树。
需要一个辅助函数在该函数的下方,需要的关于STL中list的知识,会在注释中给出,现在你需要知道list将元素有序存储在链表中
默认根节点的祖先节点为根节点

treeNode* getLastCommon(treeNode* tree, treeNode* node1, treeNode* node2) 
{
	if (tree == NULL || node1 == NULL || node2 == NULL)
		return NULL;
	list<treeNode*> path1, path2;
	bool found1 = getPath(tree, node1, path1);
	bool found2 = getPath(tree, node2, path2); 
	treeNode* ancester = NULL; // 英文小贴士:ancester 祖先 
	if (!found1 || !found2)
		return NULL;
	list<treeNode*>::const_iterator iter1 = path1.begin(); // 该函数:返回指向第一个元素的迭代器。 
	list<treeNode*>::const_iterator iter2 = path2.begin();
	while (iter1 != path1.end() && iter2 != path2.end()) // 该函数:返回末尾的迭代器  
	{
		if (*iter1 != *iter2)
			break;
		ancester = *iter1;
		iter1++;
		iter2++; 
	} 
	return ancester;
}
// 下面是辅助函数
bool getPath(treeNode* root, treeNode* node, list<treeNode*> &path)
{
	if (root == NULL)
		return false;
	// 获得根到该节点的路径 
	if (root->element == node->element)
	{
		path.push_back(root); // 该函数:在list的末尾添加一个元素。 
		return true;
	}
	bool found = false;
	path.push_back(root); // 路径从根节点开始
	found = getPath(root->leftChild, node, path); // 在左子树中查找 
	if (!found) 
		found = getPath(root->rightChild, node, path); // 在右子树中查找 
	if (!found)
		path.pop_back(); // 该函数:删除最后一个元素。在此树中没找到,pop出已压入的此子树的节点
	return found; 
} 

10.输出一个二叉树的前序序列

void preOrder(treeNode *tree)
{
	if (tree != NULL)
	{
		cout << tree->element;
		preOrder(tree->leftChild);
		preOrder(tree->rightChild);
	}
} 

11.输出一个二叉树的中序序列

void inOrder(treeNode *tree)
{
	if (tree != NULL)
	{
		inOrder(tree->leftChild);
		cout << tree->element;
		inOrder(tree->rightChild);
	}
}

12.输出一个二叉树的后序序列

void postOrder(treeNode *tree)
{
	if (tree != NULL)
	{
		postOrder(tree->leftChild);
		postOrder(tree->rightChild);
		cout << tree->element;	
	} 
}

13.输出一个二叉树的层次序列

void levelOrder(treeNode *tree)
{
	arrayQueue q;
q.push(tree);
	while (!q.empty())
	{
tree = q.top();
		cout << tree->element;
		if (tree->leftChild != NULL)
			q.push(tree->leftChild);
		if (tree->rightChild != NULL)
			q.push(tree->rightChild);
		q.pop();
	}
}

14.左右子树互换
思想:递归求解,递归的交换左右子树

treeNode* transformTree(treeNode* tree)
{
	if (tree == NULL)
		return NULL;
	treeNode* left = transformTree(tree->leftChild);
	treeNode* right = transformTree(tree->rightChild);
	tree->leftChild = right;
	tree->rightChild = left;
	return tree;
} 

15.判断两棵树是否是相同
思想:递归求解,先判断根是否相同,再判断左子树和右子树是否相同

bool judgeTreeEqual(treeNode* tree1, treeNode* tree2)
{
	if (tree1 == NULL && tree2 == NULL)
		return true;
	if (tree1 == NULL || tree2 == NULL)
		return false;
	if (tree1->element != tree2->element)
		return false;
	bool left = judgeTreeEqual(tree1->leftChild, tree2->leftChild);
	bool right = judgeTreeEqual(tree1->rightChild, tree2->rightChild);
	return left && right;	
}  

16.判断第二棵树是否是第一棵树的子树
思想:空树不是任何一棵树的子树,判断tree1是否含有tree2
递归求解,判断以当前根为节点的树是否包含,否则去左子树中寻找,再否则去右子树中寻找
需要一个辅助函数在该函数的下方

bool judgeTreeHasSubtree(treeNode* tree1, treeNode* tree2)
{
	bool result = false;
	if (tree1 != NULL && tree2 != NULL)
	{
		if (tree1->element == tree2->element)
		{
			result = isHasSubtree(tree1, tree2);	
		} 
		if (!result)
		{
			result = judgeTreeHasSubtree(tree1->leftChild, tree2);
		}
		if (!result)
		{
			result = judgeTreeHasSubtree(tree1->rightChild, tree2);
		}
	} 
	return result;	
} 
// 辅助函数
bool isHasSubtree(treeNode* tree1, treeNode* tree2)
{
	// tree2遍历完了且都对应上,返回true
	if (tree2 == NULL)
	{
		return true;
	} 
	// tree1遍历完了但tree2没有遍历完,返回false 
	if (tree1 == NULL)
	{
		return false;
	}
	if (tree1->element != tree2->element)
	{
		return false;
	}
	return isHasSubtree(tree1->leftChild, tree2->leftChild) && isHasSubtree(tree1->rightChild, tree2->rightChild);
}

17.合并两棵二叉树
思想:合并规则:如果两个节点重叠,则以新节点的值为准。tree1为旧树,tree2为新树。将tree2合并到tree1中。
你也可以制定你自己的合并规则,可以相加相减等等

void mergeTwoTrees(treeNode* tree1, treeNode* tree2)
{
	if (tree1 != NULL && tree2 != NULL)
	{
		tree1->element = tree2->element;
		mergeTwoTrees(tree1->leftChild, tree2->leftChild);
		mergeTwoTrees(tree1->rightChild, tree2->rightChild);
	} 
	if (tree1 == NULL && tree2 != NULL)
	{
		tree1 = tree2; 
	}
}

18.复制一棵二叉树
思想:递归复制,先复制根节点,再复制左子树和右子树

treeNode* copyTree(treeNode* tree)
{
	if (tree == NULL)
		return NULL;
	treeNode* newTree = new treeNode(tree->element);
	newTree->leftChild = copyTree(tree->leftChild);
	newTree->rightChild = copyTree(tree->rightChild);
	return newTree;
}
发布了36 篇原创文章 · 获赞 20 · 访问量 2824

猜你喜欢

转载自blog.csdn.net/weixin_43360801/article/details/103318672