二叉树系列

 涉及到二叉树的题,大部分是利用递归求解,因为二叉树这种结构看不见、摸不着,只能交给程序一层一层跑,而且主要是因为结构相同。

递归求解,最主要的就是要考虑好终止的条件。

目录

剑指Offer(7)--重建二叉树

剑指Offer(8)--二叉树的下一个节点

剑指Offer(26)--树的子结构

剑指Offer(27)--二叉树的镜像

剑指Offer(28)--对称的二叉树

剑指Offer(32)--从上到下打印二叉树和之字形打印二叉树

剑指Offer(33)--二叉搜索树的后序遍历序列

剑指Offer(34)--二叉树中和为某一值的路径

 剑指Offer(36)--二叉搜索树与双向链表

剑指Offer(37)--序列化二叉树

剑指Offer(54)--二叉搜索树的第K大节点

剑指Offer(55)--二叉树的深度和平衡二叉树

二叉搜索树的最近公共祖先

二叉树的最近公共节点

给定一棵二叉搜索树,请找出其中的第k小的结点

剑指Offer(7)--重建二叉树

由前序遍历和中序遍历得整棵二叉树

/*
你可以假设树中没有重复的元素。
*/
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
	return buildTree(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder,
	int pre_left, int pre_right, int in_left, int in_right)
{
	if (pre_left > pre_right || in_left>in_right)
		return nullptr;
	int value = preorder[pre_left];
	TreeNode *root = new TreeNode(value);
	int i = in_left;
	for (; i < inorder.size(); ++i)
		if (inorder[i] == value)
			break;
	int leftSize = i - in_left;
	int rightSize = in_right - i;
	root->left = buildTree(preorder, inorder, pre_left + 1, pre_right + leftSize, in_left, i - 1);
	root->right = buildTree(preorder, inorder, pre_left + leftSize + 1, pre_right, i + 1, in_right);
	return root;
}

 从中序、后序推导二叉树

TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder,
	int in_left, int in_right, int post_left, int post_right)
{
	if (in_left > in_right || post_left > post_right)
		return nullptr;
	int value = postorder[post_right];
	TreeNode *root = new TreeNode(value);
	int i = in_left;
	for (; i < inorder.size(); ++i)
		if (inorder[i] == value)
			break;
	int leftSize = i - in_left;
	int rightSize = in_right - i;
	root->left = buildTree(inorder, postorder, in_left, i - 1, post_left, post_left + leftSize - 1);
	root->right = buildTree(inorder, postorder, i + 1, post_right, post_left + leftSize, post_right - 1);
	return root;
}

剑指Offer(8)--二叉树的下一个节点

给定一棵二叉树和其中的一个节点,找出中序遍历序列的下一个节点。

#include<iostream>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
	BinaryTreeNode *m_parent;
};
//辅助函数,找右子树的最左节点
BinaryTreeNode *mostLeftNode(BinaryTreeNode *node)
{
	if (node->m_Right == nullptr)
		return nullptr;
	else
	{
		BinaryTreeNode *leftNode = node->m_Right;
		while (leftNode->m_Left != nullptr)
			leftNode = leftNode->m_Left;
		return leftNode;
	}
}
BinaryTreeNode *findNextNode(BinaryTreeNode *node)
{
	if (node == nullptr)
		return nullptr;
	//该节点的右子树不为空的情况
	if (node->m_Right != nullptr)
		return mostLeftNode(node);
	//该节点的右子树为空
	else
	{
		BinaryTreeNode *parentNode = node->m_parent;
		while (parentNode != nullptr)
		{
			if (parentNode->m_Left == node)
				return parentNode;
			else
			{
				node = parentNode;
				parentNode = node->m_parent;
			}
		}
		return nullptr;
	}
}

剑指Offer(26)--树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构

#include<iostream>
#include<queue>
using namespace std;
struct BinaryTreeNode
{
	double m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
bool Equal(double num1, double num2);
bool isSubTreeCore(BinaryTreeNode *node1, BinaryTreeNode *node2);
bool isSubTree(BinaryTreeNode *head1, BinaryTreeNode *head2)
{
	bool result = false;
	if (head1 != nullptr && head2 != nullptr)
	{
		if (head1->m_Value == head2->m_Value)
			result = isSubTreeCore(head1, head2);
		if (!result)
			result = isSubTree(head1->m_Left, head2);
		if(!result)
			result = isSubTree(head1->m_Right, head2);
	}
	return result;
}
bool isSubTreeCore(BinaryTreeNode *node1, BinaryTreeNode *node2)
{
	if (node2 == nullptr)
		return true;
	if (node1 == nullptr)
		return false;
	bool result = false;
	if (!Equal(node1->m_Value, node2->m_Value))
		return false;
	return isSubTreeCore(node1->m_Left, node2->m_Left) && isSubTreeCore(node2->m_Right, node2->m_Right);
}
/*
不能直接用等号判断两个小数是否相等,因为计算机表示小数(float,double)会有误差
*/
bool Equal(double num1, double num2)
{
	if ((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001))
		return true;
	else
		return false;
}

剑指Offer(27)--二叉树的镜像

#include<iostream>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
void mirrorTree(BinaryTreeNode *head)
{
	if (head == nullptr)
		return;
	if (head->m_Left == nullptr && head->m_Right == nullptr)
		return;
	BinaryTreeNode *tempNode = head->m_Left;
	head->m_Left = head->m_Right;
	head->m_Right = tempNode;
	if (head->m_Left != nullptr)
		mirrorTree(head->m_Left);
	if (head->m_Right != nullptr)
		mirrorTree(head->m_Right);
}

剑指Offer(28)--对称的二叉树

如果一棵二叉树和它的镜像一样,那么它是对称的。

#include<iostream>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
bool isSymmrtryCore(BinaryTreeNode *node1, BinaryTreeNode *node2);
bool isSymmrtry(BinaryTreeNode *head)
{
	return isSymmrtryCore(head, head);
}
bool isSymmrtryCore(BinaryTreeNode *node1, BinaryTreeNode *node2)
{
	if (node1 == nullptr && node2 == nullptr)
		return true;
	if (node1 == nullptr || node2 == nullptr)
		return false;
	if (node1->m_Value != node2->m_Value)
		return false;
	return (isSymmrtryCore(node1->m_Left, node2->m_Right) &&
		isSymmrtryCore(node1->m_Right, node2->m_Left));
}

剑指Offer(32)--从上到下打印二叉树和之字形打印二叉树

从上到下打印二叉树其实就是层次遍历二叉树

思路:

利用两个栈实现

#include<iostream>
#include<stack>
using namespace std;
class BinaryTreeNode
{
public:
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
	BinaryTreeNode(int value)
	{
		m_Value = value;
		m_Left = m_Right = nullptr;
	}
};

void PrintTree(BinaryTreeNode *head)
{
	if (head == nullptr)
		return;
	stack<BinaryTreeNode *> s1;
	stack<BinaryTreeNode *> s2;
	BinaryTreeNode *node = nullptr;
	s1.push(head);
	int index = 1;
	while (!s1.empty() || !s2.empty())
	{
		if (index == 1)
		{
			while (!s1.empty())
			{
				node = s1.top();
				cout << node->m_Value << " ";
				s1.pop();
				if (node->m_Left)
					s2.push(node->m_Left);
				if (node->m_Right)
					s2.push(node->m_Right);
			}
			cout << endl;
			index = -index;
		}
		if (index == -1)
		{
			while (!s2.empty())
			{
				node = s2.top();
				cout << node->m_Value << " ";
				s2.pop();
				if (node->m_Right)
					s1.push(node->m_Right);
				if (node->m_Left)
					s1.push(node->m_Left);
			}
			cout << endl;
			index = -index;
		}
	}
	return;
}

之字形打印二叉树思路:定义两个栈,在打印一个栈里的节点时,它的子节点保存到另一个栈里。当一层所有的节点打印完毕时,开始打印另一个栈里的元素。

如果打印的是奇数层,则先保存左子节点再保存右子节点到另一个栈里;如果当前打印的是偶数层,则先保存右子节点再保存左子节点到另一个栈里。 

剑指Offer(33)--二叉搜索树的后序遍历序列

题目:

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

思路:

在后序遍历序列中,最后一个数字是树的根节点的值。数组中前面的数字可以分为两部分:第一部分是左子树节点的值,它们都比根节点的值小;第二部分是右子树节点的值,它们都比根节点的值大。

所以先取数组中最后一个数,作为根节点。然后从数组开始计数比根节点小的数,并将这些记作左子树,然后记录第一个大于根节点的数,说明从此数之后都属于右子树,如果出现一个小于root的数,说明不是二叉搜素数。全满足的话,依次对左子树和右子树递归判断。

#include<iostream>
using namespace std;
bool Verify(int *arr, int length)
{
	if (arr == nullptr || length < 1)
		return false;
	int root = arr[length - 1];
	int i = 0;
	//找到第一个大于根节点的值,说明从i往后都是根节点的右子树
	for (; i < length - 1; ++i)
	{
		if (arr[i]>root)
			break;
	}
	int j = i;
	//如果右子树中出现小于根节点的值,说明不是二叉搜索树
	for (; j < length - 1; ++j)
	{
		if (arr[j] < root)
			return false;
	}
	//判断左子树是不是二叉搜索树
	bool left = true;
	if (i > 0)
		left = Verify(arr, i);
	//判断右子树是不是二叉搜索树
	bool right = true;
	if (i < length - 1)
		right = Verify(arr + i, length - i - 1);
	return (left&&right);
}

剑指Offer(34)--二叉树中和为某一值的路径

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

#include<iostream>
#include<vector>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
void FindPath(BinaryTreeNode *head, int sum)
{
	if (head == nullptr)
		return;
	vector<int> path;
	int currentSum = 0;
	FindPathCore(head, sum, path, currentSum);
}
void FindPathCore(BinaryTreeNode *node, int sum, vector<int>& path, int currentSum)
{
	if (node == nullptr)
		return;
	currentSum += node->m_Value;
	path.push_back(node->m_Value);
	if (node->m_Left == nullptr && node->m_Right == nullptr && currentSum == sum)
	{
		vector<int>::iterator iter = path.begin();
		for (; iter != path.end(); ++iter)
			cout << *iter << "	";
		cout << endl;
	}
	//如果不是叶结点,则遍历它的子节点
	if (node->m_Left != nullptr)
		FindPathCore(node->m_Left, sum, path, currentSum);
	if (node->m_Right != nullptr)
		FindPathCore(node->m_Right, sum, path, currentSum);
	//在返回父节点之前,在路径上删除这个节点
	path.pop_back();
}

 剑指Offer(36)--二叉搜索树与双向链表

题目:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。比如输入下图中左边的二叉搜索树,则输出转换之后的排序双向链表。

思路:

实际上就是中序遍历就行转换,所以左子树和右子树都用递归进行处理,我们只需要处理根节点,和左子树连接起来,然后交给右子树进行处理。

#include <iostream>
using namespace std;
class BSTNode {
public:
	int data;
	BSTNode *left;
	BSTNode *right;
	BSTNode(int value)
	{
		data = value;
		left = right = nullptr;
	}
};
BSTNode *Convert2DoubleLinkList(BSTNode *root)
{
	if (root == nullptr)
		return nullptr;
	BSTNode *last = nullptr;
	//二叉排序树转换为排序双向链表
	Convert(root, &last);
	//取得双向链表的头指针
	while (root->left != nullptr)
		root = root->left;
	return root;
}
void Convert(BSTNode *root, BSTNode** last)
{
	if (root == nullptr)
		return;
	//遍历左子树
	Convert(root->left, last);
	//处理根节点
	root->left = *last;
	if (*last)
		(*last)->right = root;
	*last = root;
	//遍历右子树
	Convert(root->right, last);
	return;
}

剑指Offer(37)--序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。

#include<iostream>
#include<queue>
#include<string>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
//先序序列化二叉树
string serialByPre(BinaryTreeNode *head)
{
	if (head == NULL)
		return "#_";
	string res = to_string(head->m_Value).append("_");
	res += serialByPre(head->m_Left);
	res += serialByPre(head->m_Right);
	return res;
}
//先序遍历的反序列化
//怎么序列化的就怎么反序列化
BinaryTreeNode *reconPre(queue<string>& qNode)
{
	string value = qNode.front();
	qNode.pop();
	if (value == "#_")
		return NULL;
	//stoi:将n进制的字符串转化为十进制
	BinaryTreeNode *head = new BinaryTreeNode();
	head->m_Value = stoi(value);
	head->m_Left = reconPre(qNode);
	head->m_Right = reconPre(qNode);
	return head;
}

剑指Offer(54)--二叉搜索树的第K大节点

题目:

给定一颗二叉树,找出第K大的节点,这个第K大的节点是从小到大的第K个节点,所以也可以说是第K小的节点。

思路:

递归,左右子树都是无脑递归,判断当前root是否满足。

#include<iostream>
#include<vector>
using namespace std;

struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
		val(x), left(NULL), right(NULL) {
	}
};
TreeNode* KthNode(TreeNode* root, int k)
{
	if (root == nullptr || k < 1)
		return nullptr;
    int count=0;
	TreeNodeCore(root, k, count);
}
TreeNode* TreeNodeCore(TreeNode* root, int k, int &index)
{
	if (root == nullptr)
		return nullptr;
	//左子树中寻找
	TreeNode *node = TreeNodeCore(root, k, index);
	//只要返回值不为空,就说明在左子树中找到了
	if (node != nullptr)
		return node;
	//判断当前节点
	++index;
	if (index == k)
		return root;
	//右子树中寻找
	node = TreeNodeCore(root, k, index);
	//只要返回值不为空,就说明在右子树中找到了
	if (node != nullptr)
		return node;
	return nullptr;
}

剑指Offer(55)--二叉树的深度和平衡二叉树

二叉树的深度

#include<iostream>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}
int DepthTree(BinaryTreeNode *head)
{
	if (head == nullptr)
		return 0;
	return max(DepthTree(head->m_Left), DepthTree(head->m_Right)) + 1;
}

平衡二叉树

判断一棵二叉树是否是平衡二叉树

第一种方法是:对二叉树中的每一个节点从根到叶,依次进行判断,但这样效率不高,会有重复计算。

第二种方法如下所示:用后序遍历的方式遍历二叉树的每个节点,那么在遍历到一个节点之前就知道它的左右子树是否为平衡二叉树。

#include<iostream>
#include<stack>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}
bool IsBance(BinaryTreeNode *head)
{
	int depth = 0;
	return IsBanlanceCore(head, &depth);
}
bool IsBanlanceCore(BinaryTreeNode *head, int *depth)
{
	if (head == nullptr)
	{
		*depth = 0;
		return true;
	}
	int left, right;
	if (IsBanlanceCore(head->m_Left, &left) && IsBanlanceCore(head->m_Right, &right))
	{
		int distance = left - right;
		if (distance <= 1 && distance >= -1)
		{
			*depth = 1 + max(left, right);
			return true;
		}
	}
	return false;
}

二叉搜索树的最近公共祖先

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==nullptr) return nullptr;
        if( (root->val >= p->val && root->val <= q->val) || (root->val <= p->val && 
root->val >= q->val)) return root;
            return root;
        else if(root->val >p->val && root->val >q->val) return lowestCommonAncestor
(root->left, p, q);
        else if(root->val <p->val && root->val <q->val) return lowestCommonAncestor
(root->right, p, q);
        else return NULL;
    }
};

二叉树的最近公共节点

#include<iostream>
using namespace std;

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
/*
注意p,q必然存在树内, 且所有节点的值唯一!!!
递归思想, 对以root为根的(子)树进行查找p和q, 如果root == null || p || q 直接返回root
表示对于当前树的查找已经完毕, 否则对左右子树进行查找, 根据左右子树的返回值判断:
1. 左右子树的返回值都不为null, 由于值唯一左右子树的返回值就是p和q, 此时root为LCA
2. 如果左右子树返回值只有一个不为null, 说明只有p和q存在与左或右子树中, 最先找到的那个节点为LCA
3. 左右子树返回值均为null, p和q均不在树中, 返回null
*/
TreeNode* lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) {
		if (root == nullptr) {
			return nullptr;
		}
		if (root == p || root == q) {
			return root;
		}
		TreeNode *left = lowestCommonAncestor(root->left, p, q);
		TreeNode *right = lowestCommonAncestor(root->right, p, q);
		if (left != nullptr && right != nullptr) {
			return root;
		}
		else if (left != nullptr) {
			return left;
		}
		else if (right != nullptr) {
			return right;
		}
		return nullptr;
}

给定一棵二叉搜索树,请找出其中的第k小的结点

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    int index = 0; //计数器
    
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(pRoot != NULL){ //中序遍历寻找第k个
            TreeNode* node = KthNode(pRoot->left,k);
            if(node != NULL)
                return node;//只要返回node,说明就是找到了
            //说明pRoot的左子节点没有第k个节点,接下来判断是否是pRoot结点
            index ++;
            if(index == k)
                return pRoot;
            node = KthNode(pRoot->right,k);
            if(node != NULL)只要返回node,说明就是找到了
                return node;
        }
        //如果没找到答案,就是返回NULL
        return NULL;
    }
};
//思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。
//     所以,按照中序遍历顺序找到第k个结点就是结果。

给定一个整数 n,生成所有由 1 ... n 为节点所组成的叉搜索树。

#include<iostream>
#include<vector>
using namespace std;
/*
给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

示例:
输入: 3
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
*/
vector<TreeNode*> generateTreesCore(int start, int end);
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	
};
vector<TreeNode*> generateTrees(int n)
{
	vector<TreeNode*> result;
	if (n == 0)
		return result;
	else
		return generateTreesCore(1, n);
}
vector<TreeNode*> generateTreesCore(int start, int end)
{
	vector<TreeNode*> subTree;
	if (start > end)
	{
		subTree.push_back(nullptr);
		return subTree;
	}
	for (int k = start; k <= end; ++k)
	{
		vector<TreeNode*> leftTree = generateTreesCore(start, k - 1);
		vector<TreeNode*> rightTree = generateTreesCore(k + 1, end);
		for (auto i : leftTree)
		{
			for (auto j : rightTree)
			{
				TreeNode *node = new TreeNode(k);
				node->left = i;
				node->right = j;
				//存放的是当前这颗树的根结点
				subTree.push_back(node);
			}
		}
	}
	return subTree;
}
int main(void)
{
	system("pasue");
	return 0;
}

二叉树的竖直遍历

注意,5和2是在一个数组里面的

class TreeNode {
public:
	int val;
	TreeNode *left;
	TreeNode *right;
};
vector<vector<int>> verticalOrder(TreeNode* root)
{
	vector<vector<int>> res;
	if (!root)
		return res;
	map<int, vector<int>> m;
	queue<pair<int, TreeNode *>> q;
	q.push({ 0,root });
	while (!q.empty())
	{
		auto a = q.front();
		q.pop();
		m[a.first].push_back(a.second->val);
		if (a.second->left)
			q.push({ a.first - 1,a.second->left });
		if (a.second->right)
			q.push({ a.first + 1,a.second->right });
	}
	for (auto a : m)
		res.push_back(a.second);
	return res;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值