1. 程式人生 > >二叉樹的儲存方式和遍歷方式

二叉樹的儲存方式和遍歷方式

二叉樹:

二叉樹的每個節點至多有兩個子樹。如這個二叉樹,其中1,2有兩個子樹,3只有左子樹,5有右子樹,4,6,7沒有子樹。


二叉樹有兩種儲存方式:

第一種,陣列表示。用陣列儲存方式就是用一組連續的儲存單元儲存二叉樹的資料元素。

                                    

兩顆樹分別用陣列表示為:

                                                  

用下標就可以直接找到那個節點,也可以找這個節點的左孩子2n+1,右孩子2n+2.找父節點(n-1)/2

但是陣列也有缺陷,比如第二個就會浪費儲存空間。

若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。完全二叉樹就適合用陣列表示。

滿二叉樹就是除了葉結點外每一個結點都有左右子葉且葉結點都處在最底層的二叉樹。也適合用陣列表示。

完全二叉樹不是滿二叉樹,滿二叉樹就是完全二叉樹。

第二種,連結串列儲存表示。其中又有二叉連結串列結構和三叉連結串列結構。


左圖就是二叉連結串列結構,右圖是三叉連結串列結構。

二叉連結串列裡有leftchild指標,rightchild指標和資料。三叉連結串列裡不僅有leftchild指標,rightchild指標和資料還有parent指標,能找到父節點。

二叉的遍歷方式有四種:

第一種:前序遍歷。先訪問根節點,再訪問左子樹,最後訪問右子樹。

第二種:中序遍歷。先訪問左子樹,再訪問根節點,最後訪問右子樹。

第三種:後序遍歷。先訪問左子樹,再訪問右子樹,最後訪問根節點。

第四種:層序遍歷。一層層節點依次遍歷。   


這顆二叉樹的前序遍歷:1-2-4-5-7-3-6

中序遍歷:4-2-5-7-1-6-3

後序遍歷:4-7-5-2-6-3-1

層序遍歷:1-2-3-4-5-6-7

程式碼實現:

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
#include <queue>
template <class T>
struct BinaryTreeNode
{
	T _data;
	BinaryTreeNode<T>* _leftchild;
	BinaryTreeNode<T>* _rightchild;

	BinaryTreeNode(const T& x)//建構函式
		:_data(x)
		, _leftchild(NULL)
		, _rightchild(NULL)
	{}
};

template <class T>
class BinaryTree
{
	typedef BinaryTreeNode<T> Node;
public:
	BinaryTree()//無參建構函式
		:_root(NULL)
	{}
	BinaryTree(T* a, size_t n, const T& invalid = T())//建構函式
	{
		size_t index = 0;
		_root = _GreateTree(a, n, invalid, index);
	}
	BinaryTree(const BinaryTree<T>& t)//拷貝構造
	{
		_root = _copy(t._root);
	}
	BinaryTree<T>& operator=(const BinaryTree<T>& t)
	{
		if (this != &t)
		{
			Node* newRoot = _copy(t._root);
			_Destroy(_root);
			_root = newRoot;
		}
		return *this;
	}
	~BinaryTree()//解構函式
	{
	void _Destory();
	}
	void PrevOrder()//前序遍歷遞迴
	{
		_PrevOrder(_root);
		cout << endl;
	}
	void InOrder()//中序遍歷遞迴
	{
		_InOrder(_root);
		cout << endl;
	}
	void PostOrder()//後序遍歷遞迴
	{
		_PostOrder(_root);
		cout << endl;
	}
	void LeveIOrder()//層序遍歷,運用佇列,以當前層帶下一層
	{
		queue<Node*> q;
		if (_root)
			q.push(_root);
		while (!q.empty())//佇列不為空
		{
			Node* front = q.front();//取隊頭
			cout << front->_data << " ";
			q.pop();//刪除隊頭
			if (front->_leftchild)
				q.push(front->_leftchild);
			if (front->_rightchild)
				q.push(front->_rightchild);
		}
		cout << endl;
	}
	size_t Size()//節點的個數
	{
		return _Size(_root);
	}
	size_t Depth()//樹的深度
	{
		return _Depth(_root);
	}
	size_t LeafSize()//葉子節點的個數
	{
		return _LeafSize(_root);
	}
	size_t GetKLevel(size_t k)//第k層節點的個數
	{
		assert(k > 0);
		return _GetKLevel(_root, k);
	}
	Node* Find(const T& x)//找節點x
	{
		return _Find(_root, x);
	}
protected:
	Node* _copy(Node* root)//拷貝
	{
		if (root == NULL)
			return NULL;
		Node* newNode = new Node(root->_data);
		newNode->_leftchild = _copy(root->_leftchild);
		newNode->_rightchild =_copy(root->_rightchild);
		return newNode;
	}
	void _Destroy(Node* root)//銷燬
	{
		if (root == NULL)
			return;
		_Destroy(root->_leftchild);
		_Destroy(root->_rightchild);
		delete root;
	}
	Node* _Find(Node* root, const T& x)//找節點x的呼叫函式
	{
		if (root == NULL)
			return NULL;
		if (root->_data == x)
			return root;
		Node* tmp = _Find(root->_leftchild, x);
		if (tmp)
			return tmp;
		return _Find(root->_rightchild, x);
	}
	size_t _GetKLevel(Node* root,size_t k)//第k層節點的呼叫函式
	{
		if (root == NULL)
			return 0;
		if (k == 1)
		{
			return 1;
		}
		return _GetKLevel(root->_leftchild, k - 1) + _GetKLevel(root->_rightchild, k - 1);
	}
	size_t _LeafSize(Node* root)//葉子節點的呼叫函式
	{
		if (root == NULL)
			return 0;
		if (root->_leftchild == NULL && root->_rightchild == NULL)
		{
			return 1;
		}
		return _LeafSize(root->_leftchild) + _LeafSize(root->_rightchild);
	}
	size_t _Depth(Node* root)//樹的深度呼叫函式
	{
		if (root == NULL)
			return 0;
		int left = _Depth(root->_leftchild);
		int right = _Depth(root->_rightchild);
		return left > right ? left + 1: right + 1;
	}
	size_t _Size(Node* root)//節點的個數
	{
		if (root == NULL)
			return 0;
		return (_Size(root->_leftchild) + _Size(root->_rightchild))+1;
	}
	void _PostOrder(Node* root)//後序遍歷呼叫函式
	{
		if (root == NULL)
			return;
		_PostOrder(root->_leftchild);
		_PostOrder(root->_rightchild);
		cout << root->_data << " ";
	}
	void _InOrder(Node* root)//中序遍歷的呼叫函式
	{
		if (root == NULL)
			return;
		_InOrder(root->_leftchild);
		cout << root->_data << " ";
		_InOrder(root->_rightchild);
	}
	void _PrevOrder(Node* root)//前序遍歷的呼叫函式
	{
		if (root == NULL)
			return;
		cout << root->_data << " ";
		_PrevOrder(root->_leftchild);
		_PrevOrder(root->_rightchild);
	}
	Node* _GreateTree(T* a, size_t n, const T& invalid, size_t& index)//建構函式的呼叫函式
	{
		Node* root = NULL;
		if ((index< n) && (a[index] != invalid))
		{
			root = new Node(a[index]);
			root->_leftchild = _GreateTree(a, n, invalid, ++index);
			root->_rightchild = _GreateTree(a, n, invalid, ++index);
		}
		return root;
	}
protected:
	Node* _root;
};

void BinaryTreeTest()//測試
{
	int a[] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 };
	BinaryTree<int> t1(a, sizeof(a) / sizeof(a[0]), '#');
	t1.PrevOrder();
	t1.LeveIOrder();
	t1.InOrder();
	t1.PostOrder();
	cout << "節點的個數:" << t1.Size() << endl;
	cout << "深度:" << t1.Depth() << endl;
	cout << "葉子節點的個數:" << t1.LeafSize() << endl;
	cout << "第k層:" << t1.GetKLevel(2) << endl;
	BinaryTree<int> t2(t1);
	t2.PostOrder();
	BinaryTree<int> t3;
	t3 = t1;
}

int main()
{
	BinaryTreeTest();
	return 0;
}


相關推薦

的三種方式(遞迴、非遞迴Morris

二叉樹遍歷是二叉樹的最基本的操作,其實現方式主要有三種: 遞迴遍歷 非遞迴遍歷 Morris遍歷 遞迴遍歷的實現非常容易,非遞迴實現需要用到棧。而Morris演算法可能很多人都不太熟悉,其強大之處在於只需要使用O(1)的空間就能實現對二叉樹O(n)時間的

的四種方式:遞迴、非遞迴+棧、Morris(後序非遞迴還有一種單棧雙棧的不同版本)

本文參考: 參考文章1 參考文章2 程式碼中加入了一些自己的理解 /* 二叉樹的四種遍歷方式 */ #include <iostream> #include <stack> using namespace std; // 二叉樹

線索的構建------小甲魚數據結構算法

-- tag typedef pre == 約定 cnblogs amp scan #include <stdio.h> #include <stdlib.h> typedef char ElemType; // 線索存儲標誌位 // Link

的建立

指示 post 完成 結構 namespace eno else cout treenode 二叉樹是十分重要的數據結構,主要用來存放數據,並且方便查找等操作,在很多地方有廣泛的應用。 今天主要寫的最基本的二叉樹,後續會繼續寫線索二叉樹,二叉排序樹,平衡二叉樹等。 二叉樹的

的建立(遞迴建樹&層序建樹)

#include<stdio.h>#include <cstdlib>#include <iostream>#include <stack>#include<queue>using namespace std; //二叉樹定義typedef cha

的建立(遞歸建樹&層序建樹)

str use namespace lib empty tof troy 輸入 turn #include<stdio.h>#include <cstdlib>#include <iostream>#include <stack&g

資料結構作業——————的三種方式

資料結構作業: 二叉樹的建立 三種遍歷方式 L:遍歷左子樹 D:訪問根節點 R:遍歷右子樹 DLR:先序遍歷 LDR:中序遍歷 LRD:後序遍歷 #include<bits/stdc++.h> using namespace std

【資料結構】的建立(非遞迴)

該程式使用的是遞迴地建立方法,以及非遞迴的遍歷演算法 執行環境:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild

資料結構——的四種方式

首先來說一下哪四種遍歷方式:前中後、層序遍歷,四種遍歷方式詳解,注意這篇部落格用的是STL,但是遍歷思想與遞迴基本一致,體會那種思想即可。 前中後序的遞迴區別主要在於輸出的語句究竟該寫在哪裡,這個主要看節點是在什麼位置輸出,如果第一個輸出就在最前面,如果第二個輸出,就在左節點後面。。。以此類

【演算法模板】的三種方式,以及根據兩種方式建樹

前言:今年九月份的PAT考試就栽在這“兩種遍歷建樹”上了,剛好沒看,剛好考到。作為自己的遺憾,今日碼完,貼在這裡留個紀念,希望能給自己警醒與警鐘。 簡要概括: 1、二叉樹的三種遍歷方式分別是 先序(先根)遍歷PreOrder,中序(中根)遍歷InOrder,後序(後根

java——的四種方式

按從左到右的思維習慣主要有四種: 前、中、後序遍歷主要看你把根節點放在什麼時候遍歷,放在第一個遍歷然後依次遍歷左子樹、右子樹(前序遍歷),先遍歷左子樹、遍歷完了再來遍歷根節點、然後遍歷右子樹,把根節點的遍歷放中間就是(中序遍歷),把根節點放最後遍歷的就是後序遍歷

的構建

(1)下面二叉樹的構建是通過一個數組來構造二叉樹 [1,2,3,4,5,6,7,8,9]{ [1, 2, 3, 4, 5, 6, 7, 8, 9 ]}[1,2,3,4,5,6,7,8,9] 將陣列元素轉換成n個Node節點(n為陣列大小,這裡是9)。 用Lin

的四種方式

二叉樹是一種很常見的資料結構,其結構如下圖: 下面接受他的四種遍歷方式: 先序(先根)遍歷:即先訪問根節點,再訪問左孩子和右孩子 中序遍歷: 後序遍歷: 層次遍歷:按照所在層數,從下往上遍歷 前提:這裡先給出測試中的二叉樹結構,如下圖所示 該

的三種方式總結

最近學了二叉樹的三種遍歷方式,即前序遍歷,中序遍歷,後序遍歷三種,仔細思索後,在此簡單總結一下。 一.二叉樹示意圖 假設有一顆二叉樹如下: 二.遍歷分析 每一顆二叉樹由根節點,左子樹,右子樹三個部分

線索化的演算法

前言: 關於二叉線索樹有什麼作用,為什麼要建立這裡就不介紹。因為時間問題,只是總結一下線索化和遍歷的演算法。這個演算法和前面總結過的非遞迴法遍歷二叉樹都算是資料結構裡面難度係數比棧,佇列,線性表那些大的演算法。限定時間內要寫出來還是有些難度,最好是能有所總結,

資料結構——的建立(遞迴建樹&層序建樹)

資料結構作業模板存檔 #include<stdio.h> #include <cstdlib> #include <iostream> #include <stack> #include<queue&g

C++類實現的構建

#include<iostream> #include<fstream> #include<string.h> using namespace std; /*

簡單的建立

#include<stdio.h> #include<stdlib.h> //定義節點 typedef struct BiNode{ char data; struct BiNode *lch; st

用C++實現的三種方式

- 非遞迴實現程式碼: #include<stdio.h> #include<stdlib.h> #include"data_structure.h" //建立一棵二叉樹 BTree create_tree() { BTree

的幾種方式

package com.sys.binarytreetest.binary; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.