1. 程式人生 > >二叉樹系列

二叉樹系列

 涉及到二叉樹的題,大部分是利用遞迴求解,因為二叉樹這種結構看不見、摸不著,只能交給程式一層一層跑,而且主要是因為結構相同。

劍指Offer(7)--重建二叉樹

由前序遍歷和中序遍歷得整棵二叉樹

#include<iostream>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
BinaryTreeNode *construct(int *preorder, int *inorder, int length)
{
	if (preorder == nullptr || inorder == nullptr || length < 1)
		return nullptr;
	return constructCore(preorder, preorder + length - 1, inorder, inorder + length - 1);
}
BinaryTreeNode *constructCore(int *startPreorder, int *endPreorder,
	int *startInorder, int *endInorder)
{
	int rootValue = startInorder[0];
	BinaryTreeNode *rootNode = new BinaryTreeNode();
	rootNode->m_Value = rootValue;
	rootNode->m_Left = rootNode->m_Right = nullptr;
	if (startPreorder == endPreorder)
	{
		if (startInorder == endInorder && *startPreorder == *startInorder)
			return rootNode;
		else
			return nullptr;
	}
	int *rootInorder = startInorder;
	//在中序遍歷中找到根結點rootInorder
	while (rootInorder <= endInorder && *rootInorder == rootValue)
		++rootInorder;
	int length = rootInorder - startInorder;
	int *leftPreorder = startPreorder + length;
	if (length > 0)
	{
		//構建左子樹
		rootNode->m_Left = constructCore(startPreorder + 1, leftPreorder, startInorder, rootInorder-1);
	}
	if (length < endPreorder - startPreorder)
	{
		//構建右子樹
		rootNode->m_Right = constructCore(leftPreorder + 1, endPreorder, rootInorder + 1, endInorder);
	}
	return rootNode;
}

  劍指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<queue>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
void PrintTree(BinaryTreeNode *head)
{
	if (head == nullptr)
		return;
	queue<BinaryTreeNode*> q;
	q.push(head);
	while (!q.empty())
	{
		BinaryTreeNode *node = q.front();
		cout << node->m_Value << " ";
		q.pop();
		if (node->m_Left != nullptr)
			q.push(node->m_Left);
		if (node->m_Right != nullptr)
			q.push(node->m_Right);
	}
}

之字形列印二叉樹思路:定義兩個棧,在列印一個棧裡的節點時,它的子節點儲存到另一個棧裡。當一層所有的節點列印完畢時,開始列印另一個棧裡的元素。

如果列印的是奇數層,則先儲存左子節點再儲存右子節點到另一個棧裡;如果當前列印的是偶數層,則先儲存右子節點再儲存左子節點到另一個棧裡。 

#include<iostream>
#include<stack>
using namespace std;
struct BinaryTreeNode
{
	int m_Value;
	BinaryTreeNode *m_Left;
	BinaryTreeNode *m_Right;
};
void PrintTree(BinaryTreeNode *head)
{
	if (head == nullptr)
		return;
	stack<BinaryTreeNode*> s[2];
	int current = 0;
	int next = 1;
	s[0].push(head);
	while (!s[0].empty() || !s[1].empty())
	{
		BinaryTreeNode *node = s[current].top();
		s[current].pop();
		cout << node->m_Value << " ";
		if (current == 0)
		{
			if (node->m_Left != nullptr)
				s[next].push(node->m_Left);
			if (node->m_Right != nullptr)
				s[next].push(node->m_Right);
		}
		else
		{
			if (node->m_Right != nullptr)
				s[next].push(node->m_Right);
			if (node->m_Left != nullptr)
				s[next].push(node->m_Left);
		}
		if (s[current].empty())
		{
			cout << endl;
			current = 1 - current;
			next = 1 - next;
		}
	}
}

劍指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(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(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;
}