算法實現-二叉樹相關

#pragma once
#include <stack>
#include <queue>
#include <vector>
#include <iostream>

using namespace std;

namespace BinaryTree
{
	void PrintFunStr(string funStr)
	{
		printf("%s", funStr.c_str());
	}

	vector<char> Sting2VtChar(const string& str)
	{
		vector<char> v;
		for (int i = 0; i < str.size(); ++i)
		{
			v.push_back(str[i]);
		}
		return v;
	}

	// 只能轉換數字字符串
	vector<int> Sting2VtInt(const string& str)
	{
		vector<int> v;
		for (int i = 0; i < str.size(); ++i)
		{
			v.push_back(str[i] - '0');
		}
		return v;
	}

	template <typename T>
	struct TreeNode
	{
		T val;
		TreeNode<T> *left = nullptr;
		TreeNode<T> *right = nullptr;
		TreeNode(T v) : val(v), left(nullptr), right(nullptr) {}
	};

	/*
	注意,要將模板類和實習都寫在頭文件中,否則編譯有問題
	*/
	template <typename T>
	class CBinaryTree
	{
	public:
		CBinaryTree();

		CBinaryTree(const vector<T>& vect);

		virtual ~CBinaryTree();

		void SetRoot(TreeNode<T>* node) { m_root = node; }

		TreeNode<T>*  GetRoot() { return m_root; }

		TreeNode<T>* CreateBinaryTree();

		// 創建的時候是按照前序創建的,注意結束符需要輸入'#',如三個節點的滿二叉樹:{'1','2','#','#','3','#','#'}
		TreeNode<T>* CreateBinaryTree(const vector<T>& vect);

		void PreOrderTraverse1(TreeNode<T>* node);

		void PreOrderTraverse2(TreeNode<T>* root);

		void PreOrderTraverse3(TreeNode<T>* root);

		void PreOrderTraverse4(TreeNode<T>* root);

		void InOrderTraverse1(TreeNode<T>* node);

		void InOrderTraverse2(TreeNode<T>* root);

		void InOrderTraverse3(TreeNode<T>* root);

		void InOrderTraverse4(TreeNode<T>* root);

		void PostOrderTraverse1(TreeNode<T>* node);

		void PostOrderTraverse2(TreeNode<T>* root);

		void PostOrderTraverse3(TreeNode<T>* root);

		void PostOrderTraverse4(TreeNode<T>* root);

		void LevelTraverse1(TreeNode<T>* root);

		int GetNodeNum1(TreeNode<T>* root);

		int	GetNodeNum2(TreeNode<T>* root);

		int GetDepth1(TreeNode<T>* root);

		int	GetDepth2(TreeNode<T>* root);

		// 序列化

		// 反序列化


	private:
		TreeNode<T>* m_root;

		int m_index;
	};

	template <typename T>
	CBinaryTree<T>::CBinaryTree()
	{
		m_index = 0;
		m_root = CreateBinaryTree();
	}

	template <typename T>
	CBinaryTree<T>::CBinaryTree(const vector<T>& vect)
	{
		m_index = 0;
		m_root = CreateBinaryTree(vect);
	}

	template <typename T>
	CBinaryTree<T>::~CBinaryTree()
	{
		if (m_root != nullptr)
		{
			// TODO銷燬樹
		}
	}



	/**
	* 1. 創建二叉樹
	* 遞歸
	* @return 樹根節點
	*/
	template <typename T>
	TreeNode<T>* CBinaryTree<T>::CreateBinaryTree()
	{
		T val;
		cin >> val;

		if (val == '#') //標識當前子樹爲空,轉向下一節點
		{
			return nullptr;
		}
		else //遞歸的前序創建左右子樹
		{
			TreeNode<T>* current_node = new TreeNode<T>(val);
			if (current_node != nullptr)
			{
				current_node->left = CreateBinaryTree();
				current_node->right = CreateBinaryTree();
			}

			return current_node;
		}
	}

	template <typename T>
	TreeNode<T>* CBinaryTree<T>::CreateBinaryTree(const vector<T>& vect)
	{
		if (vect.empty() || vect.size() == m_index)
			return nullptr;

		if (vect[m_index] == '#') //標識當前子樹爲空,轉向下一節點
		{
			return nullptr;
		}

		TreeNode<T>* current_node = new TreeNode<T>(vect[m_index]);

		m_index++;
		current_node->left = CreateBinaryTree(vect);

		m_index++;
		current_node->right = CreateBinaryTree(vect);

		return current_node;
	}

	/**
	* 2. 前序遍歷 - 深度優先搜索算法(Depth First Search),簡稱DFS
	* 遞歸
	* 思路:如果二叉樹爲空,空操作;如果二叉樹不爲空,先訪問根節點,再訪問左節點,再訪問右節點
	* @param root 樹根節點
	*/
	template <typename T>
	void CBinaryTree<T>::PreOrderTraverse1(TreeNode<T>* root)
	{
		if (root != nullptr)
		{
			cout << root->val << ", ";
			if (root->left != nullptr)
				PreOrderTraverse1(root->left);

			if (root->right != nullptr)
				PreOrderTraverse1(root->right);
		}
	}

	/**
	* 3. 前序遍歷 -- 深度優先搜索算法(Depth First Search),簡稱DFS,建議使用這個,代碼量小
	* 非遞歸
	* 思路:用一個輔助stack,總是先把右孩子放進棧,因爲先訪問左節點
	* @param root 樹根節點
	*/
	template <typename T>
	void CBinaryTree<T>::PreOrderTraverse2(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<TreeNode<T>* > s;
		s.push(root);

		while (!s.empty())
		{
			TreeNode<T>* cur_node = s.pop();
			if (cur_node != nullptr)
			{
				cout << root->val << ", ";

				// 棧是先進後出,所以先push右節點
				s.push(cur_node->right);
				s.push(cur_node->left);
			}
		}
	}

	// 非遞歸前序遍歷-教科書
	template <typename T>
	void CBinaryTree<T>::PreOrderTraverse3(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<TreeNode<T>*> s;
		TreeNode<T>* cur_node = root;
		while (cur_node != nullptr || !s.empty())
		{
			while (cur_node != nullptr)
			{
				cout << cur_node->val << ", ";
				s.push(cur_node);
				cur_node = cur_node->left;
			}
			if (!s.empty())
			{
				cur_node = s.top();
				cur_node = cur_node->right;
				s.pop();
			}
		}
	}

	// 非遞歸前序遍歷-統一風格,但是使用的棧空間更多
	template <typename T>
	void CBinaryTree<T>::PreOrderTraverse4(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<pair<TreeNode<T>*, bool> > s;
		TreeNode<T>* cur_node = root;
		s.push(make_pair(cur_node, false));
		bool visited; // 是根節點
		while (!s.empty())
		{
			cur_node = s.top().first;
			visited = s.top().second;
			s.pop();
			if (cur_node != nullptr)
			{
				if (visited)
				{
					cout << cur_node->val << ", ";
				}
				else
				{
					s.push(make_pair(cur_node->right, false));
					s.push(make_pair(cur_node->left, false));
					s.push(make_pair(cur_node, true));
				}
			}
		}
	}

	/**
	* 4. 中序遍歷
	* 遞歸
	* 思路:如果二叉樹爲空,空操作;如果二叉樹不爲空,先訪問左節點,再訪問根節點,再訪問右節點
	* @param root 樹根節點
	*/
	template <typename T>
	void CBinaryTree<T>::InOrderTraverse1(TreeNode<T>* root)
	{
		if (root != nullptr)
		{
			InOrderTraverse1(root->left);
			cout << root->val << ", ";
			InOrderTraverse1(root->right);
		}
	}

	/**
	* 5. 中序遍歷
	* 非遞歸
	* 思路:
	* @param root 樹根節點
	*/
	template <typename T>
	void CBinaryTree<T>::InOrderTraverse2(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<TreeNode<T>* > s;
		TreeNode<T>* cur_node = root;

		//while (!s.empty() || curr_node != nullptr)
		//{
		//	if (cur_node != nullptr)
		//	{
		//		s.push(curr_node);
		//		cur_node = cur_node->left; // 注意這裏切換當前節點到左節點
		//	}
		//	else // cur_node = nullptr,棧頂就是葉子節點了
		//	{
		//		cur_node = s.top();
		//		cout << cur_node->val << ", ";
		//		cur_node = cur_node->right; 
		//		s.pop(); // 因爲是中序,根節點用完了直接可以pop,下次循環時將右節點入棧
		//	}
		//}

		// 建議使用這一塊邏輯
		// 先一股腦地把左子節點放到的棧中, 因爲這時棧頂的葉結點就是我們遍歷的起點
		while (cur_node != nullptr)
		{
			s.push(cur_node);
			cur_node = cur_node->left;
		}

		while (!s.empty())
		{
			// 遍歷棧頂的節點
			cur_node = s.top();
			cout << cur_node->val << ", ";
			s.pop();

			// 即然遍歷到了當前的節點,說明了它的左子樹已經遍歷完了,不需要管了。 我們現在
			// 需要關注的是:當前節點的右子樹是否爲空了, 如果不爲空,則需要將它入棧,再先去判斷它的左子樹是否爲空。
			cur_node = cur_node->right;
			while (cur_node != nullptr)
			{
				s.push(cur_node);
				cur_node = cur_node->left;
			}
		}
	}

	// 非遞歸中序遍歷-教科書
	template <typename T>
	void CBinaryTree<T>::InOrderTraverse3(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<TreeNode<T>* > s;
		TreeNode<T>* cur_node = root;
		while (cur_node != nullptr || !s.empty())
		{
			while (cur_node != nullptr)
			{
				s.push(cur_node);
				cur_node = cur_node->left;
			}

			if (!s.empty())
			{
				cur_node = s.top();
				cout << cur_node->val << ", ";
				cur_node = cur_node->right;
				s.pop();
			}
		}
	}

	// 非遞歸中序遍歷-統一風格,但是使用的棧空間更多
	template <typename T>
	void CBinaryTree<T>::InOrderTraverse4(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<pair<TreeNode<T>*, bool> > s;
		TreeNode<T>* cur_node = root;
		s.push(make_pair(cur_node, false));
		bool visited; // 是根節點
		while (!s.empty())
		{
			cur_node = s.top().first;
			visited = s.top().second;
			s.pop();
			if (cur_node != nullptr)
			{
				if (visited)
				{
					cout << cur_node->val << ", ";
				}
				else
				{
					s.push(make_pair(cur_node->right, false));
					s.push(make_pair(cur_node, true));
					s.push(make_pair(cur_node->left, false));
				}
			}
		}
	}

	template <typename T>
	void CBinaryTree<T>::PostOrderTraverse1(TreeNode<T>* root)
	{
		if (root != nullptr)
		{
			PostOrderTraverse1(root->left);
			PostOrderTraverse1(root->right);
			cout << root->val << ", ";
		}
	}

	template <typename T>
	void CBinaryTree<T>::PostOrderTraverse2(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<TreeNode<T>* > s;
		TreeNode<T>* curr_node = root;
		while (curr_node != nullptr)
		{
			s.push(curr_node);

			// 優先選擇不爲空的左節點,如果左節點爲空,再考慮右節點(右節點爲空也沒有關係)
			if (curr_node->left != nullptr)
			{
				curr_node = curr_node->left;
			}
			else
			{
				curr_node = curr_node->right;
			}
		}

		while (!s.empty())
		{
			curr_node = s.top();
			cout << curr_node->val << ", ";
			s.pop();

			// 既然遍歷到了當前節點,說明它的左子樹與右子樹都遍歷完了,不需要管了。我們現在
			// 需要關注的是: 如果當前節點爲父節點的左節點,則需要判斷父節點的右節點是否爲空.
			// 如果不爲空,則需要去處理父節點的右節點了。
			//
			// 另外,如果當前節點不是父節點的右節點,也不需要管,因爲接下來會去遍歷父節點。
			if (!s.empty() && curr_node == s.top()->left)
			{
				curr_node = s.top()->right;
				while (curr_node != nullptr)
				{
					s.push(curr_node);
					// 優先選擇不爲空的左節點,如果左節點爲空,再考慮右節點(右節點爲空也沒有關係)
					if (curr_node->left != nullptr)
					{
						curr_node = curr_node->left;
					}
					else
					{
						curr_node = curr_node->right;
					}
				}
			}
		}
	}

	template <typename T>
	void CBinaryTree<T>::PostOrderTraverse3(TreeNode<T>* root)
	{

	}

	// 非遞後中序遍歷-統一風格,但是使用的棧空間更多
	template <typename T>
	void CBinaryTree<T>::PostOrderTraverse4(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		stack<pair<TreeNode<T>*, bool> > s;
		TreeNode<T>* cur_node = root;
		s.push(make_pair(cur_node, false));
		bool visited; // 是根節點
		while (!s.empty())
		{
			cur_node = s.top().first;
			visited = s.top().second;
			s.pop();
			if (cur_node != nullptr)
			{
				if (visited)
				{
					cout << cur_node->val << ", ";
				}
				else
				{
					s.push(make_pair(cur_node, true));
					s.push(make_pair(cur_node->right, false));
					s.push(make_pair(cur_node->left, false));
				}
			}
		}
	}

	// 層級遍歷-廣度優先搜索算法(Breadth First Search),簡稱BFS-使用隊列實現
	template <typename T>
	void CBinaryTree<T>::LevelTraverse1(TreeNode<T>* root)
	{
		if (root == nullptr)
			return;

		queue<TreeNode<T>* > q;
		TreeNode<T>* cur_node = root;
		q.push(cur_node);
		while (!q.empty())
		{
			cur_node = q.front(); // 拿到最前結點
			cout << cur_node->val << ", ";
			q.pop();

			if (cur_node->left != nullptr)
			{
				q.push(cur_node->left);
			}

			if (cur_node->right != nullptr)
			{
				q.push(cur_node->right);
			}
		}
	}

	template <typename T>
	int CBinaryTree<T>::GetNodeNum1(TreeNode<T>* root)
	{
		if (root == nullptr)
			return 0;
		int left_num = GetNodeNum1(root->left);
		int right_num = GetNodeNum1(root->right);
		return left_num + right_num + 1;
	}

	template <typename T>
	int	CBinaryTree<T>::GetNodeNum2(TreeNode<T>* root)
	{
		return 0;
	}

	template <typename T>
	int CBinaryTree<T>::GetDepth1(TreeNode<T>* root)
	{
		if (root == nullptr)
			return 0;
		int left_height = GetDepth1(root->left);
		int right_height = GetDepth1(root->right);
		return max(left_height, right_height) + 1;
	}

	template <typename T>
	int	CBinaryTree<T>::GetDepth2(TreeNode<T>* root)
	{
		return	 0;
	}



	/****************************************/
	void Test_BinaryTree()
	{
		printf("\n\n************ binary tree ***************\n");
		//CBinaryTree<char> binary_tree(Sting2VtChar("12##3##"));

		// 前序:124##5#6##37###
		// 中序:#4#2#5#6#1#7#3#
		// 後序:##4###652##7#31
		CBinaryTree<char> binary_tree(Sting2VtChar("124##5#6##37###"));

		// 前序:124##5#6##37###
		PrintFunStr("PreOrderTraverse1: ");
		binary_tree.PreOrderTraverse1(binary_tree.GetRoot());
		printf("\n");

		// 前序:124##5#6##37###
		PrintFunStr("PreOrderTraverse2: ");
		binary_tree.PreOrderTraverse1(binary_tree.GetRoot());
		printf("\n");

		// 前序:124##5#6##37###
		PrintFunStr("PreOrderTraverse3: ");
		binary_tree.PreOrderTraverse3(binary_tree.GetRoot());
		printf("\n");

		// 前序:124##5#6##37###
		PrintFunStr("PreOrderTraverse4: ");
		binary_tree.PreOrderTraverse4(binary_tree.GetRoot());
		printf("\n");


		// 中序:#4#2#5#6#1#7#3#
		PrintFunStr("InOrderTraverse1: ");
		binary_tree.InOrderTraverse1(binary_tree.GetRoot());
		printf("\n");

		// 中序:#4#2#5#6#1#7#3#
		PrintFunStr("InOrderTraverse2: ");
		binary_tree.InOrderTraverse2(binary_tree.GetRoot());
		printf("\n");

		// 中序:#4#2#5#6#1#7#3#
		PrintFunStr("InOrderTraverse3: ");
		binary_tree.InOrderTraverse3(binary_tree.GetRoot());
		printf("\n");

		// 中序:#4#2#5#6#1#7#3#
		PrintFunStr("InOrderTraverse4: ");
		binary_tree.InOrderTraverse4(binary_tree.GetRoot());
		printf("\n");

		// 後序:##4###652##7#31
		PrintFunStr("PostOrderTraverse1: ");
		binary_tree.PostOrderTraverse1(binary_tree.GetRoot());
		printf("\n");

		// 後序:##4###652##7#31
		PrintFunStr("PostOrderTraverse2: ");
		binary_tree.PostOrderTraverse2(binary_tree.GetRoot());
		printf("\n");

		// 後序:##4###652##7#31
		PrintFunStr("PostOrderTraverse3: ");
		binary_tree.PostOrderTraverse3(binary_tree.GetRoot());
		printf("\n");

		// 後序:##4###652##7#31
		PrintFunStr("PostOrderTraverse4: ");
		binary_tree.PostOrderTraverse4(binary_tree.GetRoot());
		printf("\n");

		// 層級遍歷:##4###652##7#31 廣度優先遍歷
		PrintFunStr("LevelTraverse1: ");
		binary_tree.LevelTraverse1(binary_tree.GetRoot());
		printf("\n");

		// 求節點數:
		PrintFunStr("GetNodeNum1: ");
		int num = binary_tree.GetNodeNum1(binary_tree.GetRoot());
		printf("%d\n", num);

		// 求深度:
		PrintFunStr("GetDepth1: ");
		int height = binary_tree.GetDepth1(binary_tree.GetRoot());
		printf("%d\n", height);

		printf("");
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章