ディレクトリ
以前、我々はそうでリニアテーブル構造、スタック、キュー、話していました。今日は非線形のテーブル構造ツリーについて話しています。データ構造の線形形式よりもこのツリーデータ構造がはるかに複雑で、内容はより多くのだろう、我々は最初の木で始まる(木)から開始します。
@
ツリー(木)
ツリー構造は、その枝、剥離特性との間のデータ要素を提示し、非線形構造です。
木の1.定義
ツリー(木)Tが有限の組のN(≧0)ノードであり、n = 0の場合、Tが空の木と呼ばれ、そうでない場合、空でないツリーT:
(1)唯一の特定のノードが、それが根(ルート)ノードと呼ばない先行ノード、ではない;
(2)残りのノードは、M(m≥0)互いに素なサブセットに分割することができるT1、T2、...各サブセットは、それがルートとして(サブツリー)サブツリー呼び出しツリー自体であるTmを、。
注:再帰的なツリーの定義は、「そこに木の木。」再帰的定義ツリーは、ツリーの固有の特性を明らかにしました
2.何がツリー構造であります
「木」とは何ですか?より良い定義は、直感的に図示されていません。だから私は、図中の木「ツリー」を描きました。あなたはどのような特性を持っているこれらの「木」を見て?
あなたは気づいている、私たちは本当に現実の生活「木」のように、このデータの「木」の構造
3.なぜツリー構造を使用します
注文したアレイでは、あなたはすぐに特定の値を見つけることが、規則的な配列に新しいデータ項目を挿入することができ、あなたが最初の位置に新しいデータ項目を見つけ、その後にデータ項目よりも大きな新しいデータ項目必要があります同じトークンを削除し、新しいデータ入力のための部屋を作るために移動した後、このような動きは非常に時間がかかります。明らかに、挿入、削除、および削除操作の多くは、ない規則的な配列の選択を行うに場合。一方、リストがすばやく追加および削除データ項目を、しかし、リスト内の項目を見つけることは容易ではないことができ、あなたがデータ項目を見つけるまでリストにアクセスするには、各データ項目のゼロからスタートする必要があり、このプロセスは非常に遅いです。このツリーデータ構造、としてだけでなく、高速リスト挿入と削除などが、またすぐに注文した配列として検索します
一般的なツリー4.用語
接合は、 -複数のデータ要素を含み、ツリーポイントのそれらのサブブランチ
の-ノードはサブツリーの数持つ
の最大次数でツリーノード-ツリーを
リーフ-ゼロノードである
ブランチノードが(非末端ノード) -ノードがゼロ度でない
子と親-子ツリーノードは、ルートノードの子と呼ばれ、それに応じて、親ノードは子と呼ばれる
兄弟- -と子の親
の祖先と子孫-ブランチ上のすべてのノードのノードへのルートから貫通。従って、ノードをルートとするサブツリーのノードのいずれかは、ノードの子孫と呼ばれます。
レベルのノード-ノードがルートレベル、ルート・ノード・レベル1を定義し始め、その子のレベルは2、......ノード
の同じ層における親ノード-いとこ
ツリーの深さ-最大ツリーノードの階層が
注文し、順序付けられていない木のツリー-ツリーのサブツリーは、各ノードが配列(即ち、位置交換しない)ように左から右にした場合、ツリーが呼び出され順序木、そうでない場合は、既知の障害ツリー。
森林--mの限定セット(m≥0)木ばらばらツリー
ここでは、ツリーは次のバイナリーツリー(二分木)の話、について話します
バイナリーツリー(二分木)
ツリー構造は変化しますが、最も一般的に使用され、通常は最も一般的なツリーはバイナリツリーで、バイナリツリーです。各ノードのバイナリツリーは、ほとんどの2人の子供で、左の子と右の子ノードがあっています。バイナリツリーは、そこに二つの特別な木であり、完全なバイナリツリーの完全なバイナリツリーです。完全なバイナリツリーは、完全なバイナリツリーは特殊なケースです。
1。定義とバイナリ木の特性
二叉树的定义:
二叉树(Binary Tree)是n(n≥0)个结点的有限集合BT,它或者是空集,或者由一个根结点和两棵分别称为左子树和右子树的互不相交的二叉树组成 。
————————————
二叉树的特点:
每个结点至多有二棵子树(即不存在度大于2的结点);二叉树的子树有左、右之分,且其次序不能任意颠倒。
2.几种特殊形式的二叉树
1、满二叉树:
定义:深度为k且有2k-1个结点的二叉树,称为满二叉树。
特点:每一层上的结点数都是最大结点数
2、完全二叉树:
定义:
深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称为完全二叉树
特点:
特点一 : 叶子结点只可能在层次最大的两层上出现;
特点二 : 对任一结点,若其右分支下子孙的最大层次为l,则其左分支下子孙的最大层次必为l 或l+1
建议看图对应文字综合理解
代码创建二叉树:
首先,创建一个节点Node类
package demo5;
/*
* 节(结)点类
*/
public class Node {
//节点的权
int value;
//左儿子(左节点)
Node leftNode;
//右儿子(右节点)
Node rightNode;
//构造函数,初始化的时候就给二叉树赋上权值
public Node(int value) {
this.value=value;
}
//设置左儿子(左节点)
public void setLeftNode(Node leftNode) {
this.leftNode = leftNode;
}
//设置右儿子(右节点)
public void setRightNode(Node rightNode) {
this.rightNode = rightNode;
}
接着创建一个二叉树BinaryTree 类
package demo5;
/*
* 二叉树Class
*/
public class BinaryTree {
//根节点root
Node root;
//设置根节点
public void setRoot(Node root) {
this.root = root;
}
//获取根节点
public Node getRoot() {
return root;
}
}
最后创建TestBinaryTree 类(该类主要是main方法用来测试)来创建一个二叉树
package demo5;
public class TestBinaryTree {
public static void main(String[] args) {
//创建一颗树
BinaryTree binTree = new BinaryTree();
//创建一个根节点
Node root = new Node(1);
//把根节点赋给树
binTree.setRoot(root);
//创建一个左节点
Node rootL = new Node(2);
//把新创建的节点设置为根节点的子节点
root.setLeftNode(rootL);
//创建一个右节点
Node rootR = new Node(3);
//把新创建的节点设置为根节点的子节点
root.setRightNode(rootR);
//为第二层的左节点创建两个子节点
rootL.setLeftNode(new Node(4));
rootL.setRightNode(new Node(5));
//为第二层的右节点创建两个子节点
rootR.setLeftNode(new Node(6));
rootR.setRightNode(new Node(7));
}
}
下面将会讲的遍历、查找节点、删除节点都将围绕这三个类开展
不难看出创建好的二叉树如下(画的不好,还望各位见谅):
3.二叉树的两种存储方式
二叉树既可以用链式存储,也可以用数组顺序存储。数组顺序存储的方式比较适合完全二叉树,其他类型的二叉树用数组存储会比较浪费存储空间,所以链式存储更合适。
我们先来看比较简单、直观的链式存储法。
接着是基于数组的顺序存储法(该例子是一棵完全二叉树)
上面例子是一棵完全二叉树,所以仅仅“浪费”了一个下标为0的存储位置。如果是非完全二叉树,则会浪费比较多的数组存储空间,如下。
还记得堆和堆排序吗,堆其实就是一种完全二叉树,最常用的存储方式就是数组。
4.二叉树的遍历
前面我讲了二叉树的基本定义和存储方法,现在我们来看二叉树中非常重要的操作,二叉树的遍历。这也是非常常见的面试题。
经典遍历的方法有三种,前序遍历、中序遍历和后序遍历
前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
中序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。
后序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。
我想,睿智的你已经想到了二叉树的前、中、后序遍历就是一个递归的过程。比如,前序遍历,其实就是先打印根节点,然后再递归地打印左子树,最后递归地打印右子树。
在之前创建好的二叉树代码之上,我们来使用这三种方法遍历一下~
依旧是在Node节点类上添加方法:可以看出遍历方法都是用的递归思想
package demo5;
/*
* 节(结)点类
*/
public class Node {
//===================================开始 遍历========================================
//前序遍历
public void frontShow() {
//先遍历当前节点的内容
System.out.println(value);
//左节点
if(leftNode!=null) {
leftNode.frontShow();
}
//右节点
if(rightNode!=null) {
rightNode.frontShow();
}
}
//中序遍历
public void midShow() {
//左子节点
if(leftNode!=null) {
leftNode.midShow();
}
//当前节点
System.out.println(value);
//右子节点
if(rightNode!=null) {
rightNode.midShow();
}
}
//后序遍历
public void afterShow() {
//左子节点
if(leftNode!=null) {
leftNode.afterShow();
}
//右子节点
if(rightNode!=null) {
rightNode.afterShow();
}
//当前节点
System.out.println(value);
}
}
然后依旧是在二叉树BinaryTree 类上添加方法,并且添加的方法调用Node类中的遍历方法
package demo5;
/*
* 二叉树Class
*/
public class BinaryTree {
public void frontShow() {
if(root!=null) {
//调用节点类Node中的前序遍历frontShow()方法
root.frontShow();
}
}
public void midShow() {
if(root!=null) {
//调用节点类Node中的中序遍历midShow()方法
root.midShow();
}
}
public void afterShow() {
if(root!=null) {
//调用节点类Node中的后序遍历afterShow()方法
root.afterShow();
}
}
}
依旧是在TestBinaryTree类中测试
package demo5;
public class TestBinaryTree {
public static void main(String[] args) {
//前序遍历树
binTree.frontShow();
System.out.println("===============");
//中序遍历
binTree.midShow();
System.out.println("===============");
//后序遍历
binTree.afterShow();
System.out.println("===============");
//前序查找
Node result = binTree.frontSearch(5);
System.out.println(result);
}
如果递归理解的不是很透,我可以分享一个学习的小方法:我建议各位可以这样断点调试,一步一步调,思维跟上,仔细推敲每一步的运行相信我,你会重新认识到递归!(像下面这样贴个图再一步一步断点思维更加清晰)
贴一下我断点对递归的分析,希望对你有一定的帮助~
二叉树遍历的递归实现思路自然、简单,易于理解,但执行效率较低。为了提高程序的执行效率,可以显式的设置栈,写出相应的非递归遍历算法。非递归的遍历算法可以根据递归算法的执行过程写出。至于代码可以尝试去写一写,这也是一种提升!具体的非递归算法主要流程图贴在下面了:
二叉树遍历算法分析:
二叉树遍历算法中的基本操作是访问根结点,不论按哪种次序遍历,都要访问所有的结点,对含n个结点的二叉树,其时间复杂度均为O(n)。所需辅助空间为遍历过程中所需的栈空间,最多等于二叉树的深度k乘以每个结点所需空间数,最坏情况下树的深度为结点的个数n,因此,其空间复杂度也为O(n)。
5.二叉树中节点的查找与删除
刚才讲到二叉树的三种金典遍历放法,那么节点的查找同样是可以效仿的,分别叫做前序查找、中序查找以及后序查找,下面代码只以前序查找为例,三者查找方法思路类似~
至于删除节点,有三种情况:
1、如果删除的是根节点,那么二叉树就完全被删了
2、如果删除的是双亲节点,那么该双亲节点以及他下面的所有子节点所构成的子树将被删除
3、如果删除的是叶子节点,那么就直接删除该叶子节点
那么,我把完整的三个类给贴出来(包含创建、遍历、查找、删除)
依旧是Node节点类
package demo5;
/*
* 节(结)点类
*/
public class Node {
//节点的权
int value;
//左儿子
Node leftNode;
//右儿子
Node rightNode;
//构造函数,初始化的时候就给二叉树赋上权值
public Node(int value) {
this.value=value;
}
//设置左儿子
public void setLeftNode(Node leftNode) {
this.leftNode = leftNode;
}
//设置右儿子
public void setRightNode(Node rightNode) {
this.rightNode = rightNode;
}
//前序遍历
public void frontShow() {
//先遍历当前节点的内容
System.out.println(value);
//左节点
if(leftNode!=null) {
leftNode.frontShow();
}
//右节点
if(rightNode!=null) {
rightNode.frontShow();
}
}
//中序遍历
public void midShow() {
//左子节点
if(leftNode!=null) {
leftNode.midShow();
}
//当前节点
System.out.println(value);
//右子节点
if(rightNode!=null) {
rightNode.midShow();
}
}
//后序遍历
public void afterShow() {
//左子节点
if(leftNode!=null) {
leftNode.afterShow();
}
//右子节点
if(rightNode!=null) {
rightNode.afterShow();
}
//当前节点
System.out.println(value);
}
//前序查找
public Node frontSearch(int i) {
Node target=null;
//对比当前节点的值
if(this.value==i) {
return this;
//当前节点的值不是要查找的节点
}else {
//查找左儿子
if(leftNode!=null) {
//有可能可以查到,也可以查不到,查不到的话,target还是一个null
target = leftNode.frontSearch(i);
}
//如果不为空,说明在左儿子中已经找到
if(target!=null) {
return target;
}
//查找右儿子
if(rightNode!=null) {
target=rightNode.frontSearch(i);
}
}
return target;
}
//删除一个子树
public void delete(int i) {
Node parent = this;
//判断左儿子
if(parent.leftNode!=null&&parent.leftNode.value==i) {
parent.leftNode=null;
return;
}
//判断右儿子
if(parent.rightNode!=null&&parent.rightNode.value==i) {
parent.rightNode=null;
return;
}
//递归检查并删除左儿子
parent=leftNode;
if(parent!=null) {
parent.delete(i);
}
//递归检查并删除右儿子
parent=rightNode;
if(parent!=null) {
parent.delete(i);
}
}
}
依旧是BinaryTree 二叉树类
package demo5;
/*
* 二叉树Class
*/
public class BinaryTree {
//根节点root
Node root;
//设置根节点
public void setRoot(Node root) {
this.root = root;
}
//获取根节点
public Node getRoot() {
return root;
}
public void frontShow() {
if(root!=null) {
//调用节点类Node中的前序遍历frontShow()方法
root.frontShow();
}
}
public void midShow() {
if(root!=null) {
//调用节点类Node中的中序遍历midShow()方法
root.midShow();
}
}
public void afterShow() {
if(root!=null) {
//调用节点类Node中的后序遍历afterShow()方法
root.afterShow();
}
}
//查找节点i
public Node frontSearch(int i) {
return root.frontSearch(i);
}
//删除节点i
public void delete(int i) {
if(root.value==i) {
root=null;
}else {
root.delete(i);
}
}
}
それでもTestBinaryTreeテストクラス
package demo5;
public class TestBinaryTree {
public static void main(String[] args) {
//创建一颗树
BinaryTree binTree = new BinaryTree();
//创建一个根节点
Node root = new Node(1);
//把根节点赋给树
binTree.setRoot(root);
//创建一个左节点
Node rootL = new Node(2);
//把新创建的节点设置为根节点的子节点
root.setLeftNode(rootL);
//创建一个右节点
Node rootR = new Node(3);
//把新创建的节点设置为根节点的子节点
root.setRightNode(rootR);
//为第二层的左节点创建两个子节点
rootL.setLeftNode(new Node(4));
rootL.setRightNode(new Node(5));
//为第二层的右节点创建两个子节点
rootR.setLeftNode(new Node(6));
rootR.setRightNode(new Node(7));
//前序遍历树
binTree.frontShow();
System.out.println("===============");
//中序遍历
binTree.midShow();
System.out.println("===============");
//后序遍历
binTree.afterShow();
System.out.println("===============");
//前序查找
Node result = binTree.frontSearch(5);
System.out.println(result);
System.out.println("===============");
//删除一个子树
binTree.delete(4);
binTree.frontShow();
}
}
ここで、合計する、私たちは、木を非線形テーブルのデータ構造を学びます。根、葉ノード、親ノード、子ノード、兄弟の高さだけでなく、ノード、深さ、層、および木の高さなど:木について、いくつかの一般的に使用され、あなたがあること、知っておく必要がある概念があります。通常、最も一般的なツリーはバイナリツリーです。各ノードのバイナリツリーは、ほとんどの2人の子供であり、左の子と右の子ノードでした。バイナリツリーは、そこに二つの特別な木であり、完全なバイナリツリーの完全なバイナリツリーです。完全なバイナリツリーの完全なバイナリツリーは特殊なケースです。バイナリツリーは、チェーンが格納されてもよく、それはアレイ配列に格納されてもよいです。完全なバイナリツリーより適し順次でストレージアレイは、ストレージの他のタイプの配列を持つバイナリツリーは、より多くのストレージスペースの無駄になります。また、内のバイナリツリー非常に重要な操作である時に、動作順序トラバーサルの後、前の時間の複雑さを横断するには、O(n)は、あなたが理解し、実装するために再帰的なコードを使用する必要があります。
少しでも、この記事を参考にした場合、〜、あなたに聖歌賞賛を選ぶあなたに感謝して下さい
私は良いポット友人ああを言うために来ている...あなたは技術、憧れ技術、技術の追求を探るために、公共の数に焦点を当てることを歓迎します...