【數據結構】普通二叉樹的實現

一、問題概述

?????? 樹是n個有限個數據的集合,形如:


?????? 它像不像倒著的樹呢?我們把它看成是一種數據結構----樹。它的第一個節點稱作樹的根,最底下的那些節點稱作樹的葉子。

?????? 我們今天所要研究的是二叉樹,即父節點最多只有兩個孩子(左孩子和右孩子)。



二叉樹還有兩種特殊的結構,分為完全二叉樹和滿二叉樹。

如:


滿二叉樹:高度為N的滿二叉樹有2^N-1個節點。

完全二叉樹:高度為N,前N-1層為滿二叉樹,第N層為連續的葉子節點。

二叉樹有四種遍歷方式:

前序遍歷(根左右),中序遍歷(左根右),后序遍歷(左右根),層序遍歷(從上往下,從左往右)。

那么,如何實現一個二叉樹的數據結構呢?

二、數據結構的設計

???????在這里,我們采取鏈表的方式,即創建節點,將節點與節點鏈接起來的方式實現二叉樹。

節點的結構:


(1)將要創建的節點的數據部分存儲到數組里,然后創建節點。讀取數組,我們將指針指向空的部分當作是非法字符,在這里非法字符是‘#’;

(2)如果不是非法字符,則創建節點并鏈接到二叉樹的根上,按照先序遍歷的方式先創建根,再創建根的左,最后創建根的右。

(3)創建完成后,對二叉樹進行相應的操作。如:求葉子節點數,第k層節點數,四種遍歷方式,遞歸和非遞歸實現等。

三、實現代碼

//BinaryTree.h

#pragma once
#include<assert.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;template<typename T>
struct BinaryTreeNode  //創建節點
{T _data;BinaryTreeNode<T> *_left;BinaryTreeNode<T> *_right;BinaryTreeNode(const T& data):_data(data), _left(NULL), _right(NULL){}
};template<typename T>
class BinaryTree
{typedef BinaryTreeNode<T> Node;
public:BinaryTree():_root(NULL){}BinaryTree(T* arr,size_t size,const T& invalid = T()){assert(arr);size_t index = 0;_root = CreateTree(arr,size,invalid,index);}BinaryTree(BinaryTree<T> &t){assert(t._root);_root = _Copy(t._root);}//傳統寫法/*BinaryTree<T>& operator=(BinaryTree<T>& t){if (this != &t){Node* tmp = _Copy(t._root);_root = _Destroy(_root);_root = tmp;}return *this;}*///現代寫法BinaryTree<T>& operator=(BinaryTree<T>& t){if (this != &t){BinaryTree<T> tmp(t);std::swap(tmp._root, _root);}return *this;}~BinaryTree(){if (_root){_root = _Destroy(_root);}}
public:size_t Size(){return _Size(_root);}size_t Depth(){return _Depth(_root);}void PreOrder(){_PreOrder(_root);cout << endl;}void InOrder(){_InOrder(_root);cout << endl;}void PostOrder(){_PostOrder(_root);cout << endl;}void LevelOrder(){_LevelOrder(_root);cout << endl;}Node* Find(const T& x){return _Find(_root,x);}public://創建二叉樹Node* CreateTree(T* arr, size_t size, const T& invalid,size_t& index){Node* root = NULL;if (index < size){if (arr[index] != invalid){root = new Node(arr[index]);root->_left = CreateTree(arr, size, invalid, ++index);root->_right = CreateTree(arr, size, invalid, ++index);}}return root;}//拷貝二叉樹Node* _Copy(Node* root){Node* cur = NULL;if (root){cur = new Node(root->_data);cur->_left = _Copy(root->_left);cur->_right = _Copy(root->_right);}return cur;}//釋放二叉樹節點Node* _Destroy(Node* root){if (root != NULL){root->_left = _Destroy(root->_left);root->_right = _Destroy(root->_right);delete root;root = NULL;}return root;}//遞歸求解二叉樹節點的個數size_t _Size(Node* root)     {if (root == NULL)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}//二叉樹的深度求解size_t _Depth(Node* root){size_t maxDepth = 1;if (root){size_t depth = 1;if (root->_left)             //左不為空則遍歷左樹的深度{depth += _Depth(root->_left);}if (depth > maxDepth){maxDepth = depth;}if (root->_right)          //右不為空則在左樹的深度基礎上+1{depth = _Depth(root->_right) + 1;}if (depth > maxDepth){maxDepth = depth;}}return maxDepth;}//二叉樹前序遍歷的遞歸實現void _PreOrder(Node* root){if (root){cout << root->_data << " ";_PreOrder(root->_left);_PreOrder(root->_right);}}//二叉樹中序遍歷的遞歸實現void _InOrder(Node* root){if (root){_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}}//二叉樹后序遍歷的遞歸實現void _PostOrder(Node* root){if (root){_PostOrder(root->_left);_PostOrder(root->_right);cout << root->_data << " ";}}//二叉樹層序遍歷的實現void _LevelOrder(Node* root){queue<Node*> q;if (root)q.push(root);while (!q.empty()){Node* front = q.front();cout << front->_data << " ";q.pop();if (front->_left){q.push(front->_left);}if (front->_right){q.push(front->_right);}}}//二叉樹中查找某個值的節點Node* _Find(Node* root,const T& data){Node* cur = root;if(root == NULL)return NULL;if(root->_data == data)         //找到則返回節點return root;Node* ret = _Find(cur->_left,data);if(ret == NULL){ret = _Find(cur->_right,data);}return ret;}
public:void PreOrderNonR(){_PreOrderNonR(_root);cout<<endl;}void InOrderNonR(){_InOrderNonR(_root);cout<<endl;}void PostOrderNonR(){_PostOrderNonR(_root);cout<<endl;}
public://二叉樹前序遍歷的非遞歸實現void _PreOrderNonR(Node* root){Node* cur = root;stack<Node*> s;while(!s.empty() || cur){while(cur){cout<<cur->_data<<" ";s.push(cur);cur = cur->_left;}Node* top = s.top();s.pop();cur = top->_right;}}//二叉樹中序遍歷的非遞歸實現void _InOrderNonR(Node* root){Node* cur = root;stack<Node*> s;while(!s.empty() || cur){while(cur){s.push(cur);cur = cur->_left;}Node* top = s.top();cout<<top->_data<<" ";s.pop();cur = top->_right;}}//二叉樹后序遍歷的非遞歸實現void _PostOrderNonR(Node* root){Node* cur = root;stack<Node*> s;Node* prev = NULL;while(!s.empty() || cur){while(cur){s.push(cur);cur = cur->_left;}Node* top = s.top();if(top->_right == NULL || top->_right == prev){cout<<top->_data<<" ";prev = top;s.pop();}else{cur = top->_right;}}}
public:size_t GetKLevelSize(size_t k){assert(_root);size_t level = 1;size_t count = 0;_GetKLevelSize(_root,k,level,count);return count;}//獲取第k層節點的個數(當k等于層數level時,count++,否則分別遍歷左樹和右樹)void _GetKLevelSize(Node* root,size_t k,size_t level,size_t& count){if(root == NULL)return;if(k == level){count++;return;}_GetKLevelSize(root->_left,k,level+1,count);_GetKLevelSize(root->_right,k,level+1,count);}size_t GetLeafSize(){size_t count = 0;_GetLeafSize(_root,count);return count;}//獲取葉子節點(當節點的左樹為空且右樹為空時,即葉子數加1)void _GetLeafSize(Node* root,size_t& count){if(root == NULL)return;if(root->_left == NULL && root->_right == NULL){count++;return;}_GetLeafSize(root->_left,count);_GetLeafSize(root->_right,count);}size_t SizePrev(){size_t count = 0;_SizePrev(_root,count);return count;}//用引用的方法獲取二叉數節點的個數(需要入棧)void _SizePrev(Node* root,size_t& count){if(root == NULL)return;Node* cur = root;stack<Node*> s;while(!s.empty() || cur){while(cur){s.push(cur);count++;cur = cur->_left;}Node* top = s.top();s.pop();cur = top->_right;}}
private:Node* _root;
};void FunTest()
{int arr[] = {1,2,3,'#','#',4,'#','#',5,6};int arr1[] = { 1, 2,'#', 3, '#', '#', 4, 5,'#',6 ,'#', 7,'#','#',8};BinaryTree<int> b1(arr,sizeof(arr)/sizeof(arr[0]),'#');BinaryTree<int> b6(arr1, sizeof(arr1) / sizeof(arr1[0]), '#');BinaryTree<int> b2(b1);BinaryTree<int> b3;b3 = b2;cout << b1.Size() << endl;cout << b2.Size() << endl;cout << b3.Size() << endl;cout << b1.Depth() << endl;cout << b6.Depth() << endl;cout<<"b1:遞歸先序遍歷->";b1.PreOrder();cout<<"b1:遞歸中序遍歷->";b1.InOrder();cout<<"b1:遞歸后序遍歷->";b1.PostOrder();cout<<"b1:層序遍歷->";b1.LevelOrder();cout<<"b1:非遞歸先序遍歷->";b1.PreOrderNonR();cout<<"b1:非遞歸中序遍歷->";b1.InOrderNonR();cout<<"b1:非遞歸后序遍歷->";b1.PostOrderNonR();cout<<"Find(4)?"<<endl;cout<<b1.Find(4)->_data<<endl;cout<<"GetLeafSize:"<<b1.GetLeafSize()<<endl;cout<<"_SizePrev:"<<b1.SizePrev()<<endl;cout<<"b6:遞歸先序遍歷->";b6.PreOrder();cout<<"b6:遞歸中序遍歷->";b6.InOrder();cout<<"b6:遞歸后序遍歷->";b6.PostOrder();cout<<"b6:遞歸層序遍歷->";b6.LevelOrder();cout<<"第三層節點數:"<<b6.GetKLevelSize(3)<<endl;cout<<"第四層節點數:"<<b6.GetKLevelSize(4)<<endl;cout<<"第五層節點數:"<<b6.GetKLevelSize(5)<<endl;cout<<"GetLeafSize:"<<b6.GetLeafSize()<<endl;cout<<"_SizePrev:"<<b6.SizePrev()<<endl;
}

//BinaryTree.cpp

#include<iostream>
using namespace std;
#include"BinaryTree.h"
int main()
{FunTest();return 0;
}

四、運行結果



前一篇廣義表的數據結構和二叉樹的數據結構也有一些類似哦。大家可以看看噠^-^



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/382769.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/382769.shtml
英文地址,請注明出處:http://en.pswp.cn/news/382769.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

windows 下 安裝mysql 出現 “ ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’ (using password

這個問題是在Windows下安裝MySQL服務時遇到的&#xff0c;使用MySQl綠色版進行安裝的&#xff0c;安裝完成后&#xff0c;連接到MySQL服務時輸入命令 “ mysql -uroot -p ” &#xff0c;因為時第一次登錄&#xff0c;未設置密碼&#xff0c;直接回車&#xff0c;就遇到了這個問…

setitimer用法說明

函數原型&#xff1a; int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value) 函數作用&#xff1a; 可用來實現延時和定時的功能 頭文件&#xff1a; #include <sys/time.h> 參數詳解 用一把&#xff1a;一個例子 #include &…

哈希表(閉散列、拉鏈法--哈希桶)

哈希表&#xff0c;也稱散列表&#xff0c;是一種通過key值來直接訪問在內存中的存儲的數據結構。它通過一個關鍵值的函數&#xff08;被稱為散列函數&#xff09;將所需的數據映射到表中的位置來訪問數據。 關于哈希表&#xff0c;主要為以下幾個方面&#xff1a; 一、哈希表…

僵尸進程的產生和SIGCHLD信號

核心句子 子進程在終止時會給父進程發SIGCHLD信號,該信號的默認處理動作是忽略,父進程可以自 定義SIGCHLD信號的處理函數。 僵尸進程的產生&#xff1a; #include "head.h" #include <unistd.h> #include <signal.h>int main() {key_t key ftok(&quo…

windows 通過批處理 修改環境變量

echo off setlocal enabledelayedexpansion set remain%path% ::待查找字符串 set toAddD:\ffmpeg2\ffmpeg-4.1.3-win64-shared\bin ::標記,默認沒有重復 set findedfalse :loop for /f "tokens1* delims;" %%a in ("%remain%") do (::如果找到相同的了if…

海量數據處理--位圖(BitMap)

對于海量數據這個詞&#xff0c;大家不難理解吧。主要是針對給定的數據量特別大&#xff0c;占用內存特別大的情況。那么和位圖有什么關系呢。看下面一個騰訊的海量數據的例子吧。 例&#xff1a;給40億個不重復的無符號整數&#xff0c;沒排過序。給一個無符號整數&#xff0…

ps命令與top命令參數意義詳解

文章目錄1.ps -l2.ps aux3.top面試經常被問道&#xff0c;特別是top。1.ps -l 參數解釋F代表這個程序旗標 (process flags)&#xff0c;說明這個程序的總結權限&#xff0c;常見號碼有&#xff1a;o 若為 4 表示此程序的權限為 root &#xff1b;o 若為 1 則表示此子程序僅進行…

python 打包成exe 程序的方法. 轉

轉自 https://blog.csdn.net/lzy98/article/details/83246281

哈希拓展--布隆過濾器

一、問題概述 布隆過濾器是由布隆提出來的&#xff0c;是由一個很長的二進制序列和一系列的映射函數組成。主要用于檢測一個元素是否在一個集合中。當然在設計計算機軟件時&#xff0c;我們也經常會判斷一個元素是否在一個集合中。比如&#xff1a;在字處理軟件中&#xff0c;…

開啟一個新的命令行窗口

1&#xff0c;start cmd /k echo Hello, World!2&#xff0c;start cmd /C pause區別是第二種執行完畢以后&#xff0c;新開的窗口會自動關閉&#xff0c;第一種則不會

C語言中 \r, \n, \b

\r\n 和 \n 區別 &#xff08;重新排版整理&#xff09; \r回車符\n換行符計算機還沒有出現之前&#xff0c;有一種叫做電傳打字機&#xff08;Teletype Model 33&#xff09;的玩意&#xff0c;每秒鐘可以打10個字符。但是它有一個問題&#xff0c;就是打完一行換行的時候&am…

排序(Sort)--【一】

排序&#xff0c;對于大家再熟悉不過了吧。我們之前在學習c語言的時候接觸過的冒泡排序&#xff0c;選擇排序等。今天給大家介紹兩種新的排序。 1、直接插入排序 升序排列&#xff1a;將第一個數確定好&#xff0c;從下標為1的數開始插入&#xff0c;如果插入的數比前一個數大…

用python os.system 執行 批處理的時候, 出現的一些問題

如果 在一個py文件里面 , 假設用 三條語句 os.system(a.bat) os.system(b.bat) os.system(c.bat)這樣的話 只會最后一條生效.

wait()和waitpid()的參數解析

進程的等待 #include <sys/types.h> #include <sys/wait.h> wait(),waitpid()區別&#xff1a; 在一個子進程終止前&#xff0c;wait使其調用者阻塞&#xff0c;而waitpid有一個選項&#xff0c;可使調用者不阻塞;waitpid()并不等待在其調用之后的第一個終止的…

快速排序--全集

快速排序&#xff1a;一聽名字就知道這種排序很快的&#xff0c;是吧&#xff1f;沒錯&#xff0c;它是一種效率比較高的排序算法。 快速排序采用的是分治的思想。 比如&#xff0c;將一串數中的一個元素作為基準&#xff0c;然后將比它小的數排在它的左邊&#xff0c;比它大…

task_struct結構體查找

網上有很多解析task_struct結構體的文章&#xff0c;可是都沒有說這個結構體到底在哪里&#xff1f; 這個結構體位于頭文件 shced.h cd / find -name sched.h 顯示結果如下 注意只有 位于內核中的include 才是正確的。 /usr/src/kernels/2.6.32-431.el6.i686/include/linux…

使用python 創建快捷方式

import os import pythoncom from win32com.shell import shell from win32com.shell import shellcondef set_shortcut(): # 如無需特別設置圖標&#xff0c;則可去掉iconname參數try:filename r"D:\AppServ\timer\win_cron_zq\timer.exe" # 要創建快捷方式的文件…

python 各個模塊的簡單介紹 轉載

轉自 https://www.jianshu.com/p/f8c43e25c02e

鬧鐘函數alarm()的解釋與實踐

alarm 定義 也稱為鬧鐘函數&#xff0c;它可以在進程中設置一個定時器&#xff0c;當定時器指定的時間到時&#xff0c;它向進程發送SIGALRM信號。可以設置忽略或者不捕獲此信號&#xff0c;如果采用默認方式其動作是終止調用該alarm函數的進程。 #include "head.h&quo…

Linux下如何設置權限讓用戶只刪除自己的文件(粘滯位)

之前我們知道如何針對用戶和用戶組來設置文件權限。通常是用三個八進制來設置權限的&#xff0c;這里我要說的是&#xff0c;其實是由四個八進制表示的。其中第一個八進制我們通常是忽略的。第二個到第四個是對應于SUID,SGID,sticky-bit。 SUID&#xff1a;設置了SUID 位的文件…