二叉树学习笔记1

数组、向量、链表都是一种顺序容器,它们提供了按位置访问数据的手段。而很多情况下,我们需要按数据的值来访问元素,而不是它们的位置来访问元素。比如有这样一个数组int num[3]={1,2,3},我们可以非常快速的访问数组中下标为2的数据,也就是说我们知道这个数据的位置,就可以快速访问。有时候我们是不知道元素的位置,但是却知道它的值是多少。假设我们有一个变量,存放在num这个数组中,我们知道它的值为2,却不知道它下标是多少,也就是说不知道它的位置。这个时候再去数组中访问这个元素就比较费劲,就得遍历数组,而且还要保证数组中没有重复的元素。

二叉树在很大程度上解决了这个缺点,二叉树是按值来保存元素,也按值来访问元素。怎么做到呢,和链表一样,二叉树也是由一个个节点组成,不同的是链表用指针将一个个节点串接起来,形成一个链,如果将这个链“拉直”,就像平面中的一条线,是一维的。而二叉树由根节点开始发散,指针分别指向左右两个子节点,像树一样在平面上扩散,是二维的。示意图如下:

和链表一样,二叉树也是由一个个节点构成,显然这一个个节点才是二叉树的基础。在链表中,如果这个链表是单向链表,那么每个节点中就需要包含一个指向后继节点的指针,如果是双向链表,还需要一个指向前驱节点的指针。那么在二叉树的节点中,就需要包含一个指向左子节点和一个指向右子节点的指针,为了方便的遍历二叉树,还需要一个指向父节点的指针,最后,还需要包含当前节点的值。那么一棵最简单的二叉树,示意图是这样的

那么用一个类来实现这个节点,假设这个二叉树中保存的都是int型的数据,为了方便起见,在构造函数中将所有的变量全部初始化为默认值,那么代码如下:

class treeNode
{
public:
	int value;
	treeNode *left;
	treeNode *right;
	treeNode *parent;
	treeNode()
	{
		value = 0;
		left = NULL;
		right = NULL;
		parent = NULL;
	}
};

然后生成这个类的三个实例,分别为node0、node1、node2,作为父节点、左子节点、右子节点。节点值初始化为10、8、14。代码如下

#include <iostream>
using namespace std;
class treeNode
{
public:
	int value;
	treeNode *left;
	treeNode *right;
	treeNode *parent;
	treeNode()
	{
		value = 0;
		left = NULL;
		right = NULL;
		parent = NULL;
	}
};
int main()
{
	treeNode node0, node1, node2;
	node0.value = 10;
	node1.value = 8;
	node2.value = 14;
	node0.left = &node1;		//node0左子节点指针指向node1
	node0.right = &node2;		//node0右子节点指针指向node2
	node1.parent = &node0;		//node1和node2的父节点指针均指向node0
	node2.parent = &node0;
	cout << sizeof(treeNode) << endl;
	cout <<"node0 addr: "<< &node0 << endl;
	cout <<"node1 addr: "<< &node1 << endl;
	cout <<"node2 addr: "<< &node2 << endl;
	cout <<"node0 left node addr: "<< node0.left << endl;
	cout <<"node0 right node addr: "<< node0.right << endl;
	cout <<"node1 parent node addr: "<< node1.parent << endl;
	cout <<"node2 parent node addr: "<< node2.parent << endl;
}

输出结果是这样的

这样我们就可以根据输出画出这三个节点在内存中的结构图

这样就构成了一个最最简单的二叉树,这样如果拿到父节点,就可以任意的访问它的左子节点或者右子节点,比如:

cout << (node0.left)->value << endl;

先拿到父节点,找到指向左子节点的指针,因为拿到的是一个指针,所以就可以用“->”访问左子节点。上图中有一个比较奇怪的地方就是,node0的左子节点指针指向了node1,却感觉指向了node1的value。因为value是类中的第一个成员,地址会跟类的首地址重合,而处理器知道这个left指针的类型是treeNode* ,所以 会从这个首地址向下读取16字节的内容(sizeof(treeNode)=16),这样就会读取这个类中的所有成员。假设我们将left指针强制转换为int* ,那么就会向下读取4个字节的内容,也就是value的值。比如:

cout << *(int *)(node0.left) << endl;

输出是8,原理是我们先拿到node的left指针,强制转换为int* ,再用*运算符读取其中的内容,因为类型转换为int* ,所以处理器向下读取4个字节的内容,也就是8。

到现在为止,虽然构成了一棵最简单的二叉树,但是这种树的作用不是很大。如果想要在二叉树中存储大量的数据,那么就需要定义一种结构特性。那就是左子节点的值总是小于父节点的值,右子节点的值总是大于父节点的值。

这种二叉树叫做二叉搜索树,前面说到的这种结构特性,也是二叉搜索树的精髓所在,最开始的时候说,二叉树是根据值来存储元素的。那么拿到一个新的节点,就先比较这个节点和跟节点的大小关系,如果小于跟节点,就沿左子节点下降,否则反之。按照这样的规则,直到下降到一个空节点,也就是NULL,将这个节点插入到这个位置。查找一个元素的过程也是类似的,假设拿到一个节点,就先跟根节点比较,如果小于跟节点,就沿左子节点查找,否则反之。按照这样的规则,直到查找到这个节点,或者查找到NULL,代表这个节点不在这棵树中。示意图如下:

假设现在新插入一个值为15的节点,那么比较它和12的大小关系,比12大,沿右子节点下降。再比较和16的大小关系,比16小,沿左子节点下降。再比较和14的大小关系,比14大,沿右子节点下降。这时候到达了空节点,也就是NULL。将15作为14的右子节点插入到这棵树。查找过程类似。

显然这种结构特性不能人为的去保持,而是由代码自动保持。与此同时,我们希望有一个完整的二叉搜索树类。对外提供insert和erase接口,用户只管往里面插入数据,不管内部的指针结构。而不是像上面那样,手动生成一个个节点,再调整指针。那样会累死的。那样旨在说明如何由一个个节点构成二叉树以及它们在内存中的结构。以后再学习二叉搜索树的实现。

时间: 2024-10-09 12:22:24

二叉树学习笔记1的相关文章

二叉树学习笔记。

1. 层序构建和先序遍历: 1 public class Tree { 2 public Tree left; 3 public Tree right; 4 public int val; 5 6 public Tree() { 7 8 } 9 10 public Tree(String n) { 11 this.val = Integer.parseInt(n); 12 } 13 14 public Tree createTreeByLevel(String[] var) { 15 if (v

二叉树学习笔记之二叉查找树(BSTree)

二叉查找树即搜索二叉树,或者二叉排序树(BSTree),学习回顾一下有关的知识. >>关于二叉查找树 二叉查找树(Binary Search Tree)是指一棵空树或者具有下列性质的二叉树:1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值:2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值:3. 任意节点的左.右子树也分别为二叉查找树.4. 没有键值相等的节点,这个特征很重要,可以帮助理解二叉排序树的很多操作.二叉查找树具有很高的灵活性,对其优化可

二叉树学习笔记-实现

上一篇文章中,算是初步了解了二叉树是一种怎样的数据结构,也算是有了一个初步的印象.接下来用自己的代码去实现一个二叉搜索树(以下全叫二叉树)类,对外提供常用的接口,比如insert.erase.size.find等等.就像盖房一样,如果说二叉树是一座建筑,那么其中的节点就是一块块砖.要实现二叉树这个类,就必须先实现节点类,假设我们起名为treeNode.在STL标准库中,像一般的数据结构都是模板类,在这里为了方便起见,假设二叉树这个类中保存的数据全是int型. 这个节点类中,需要包含如下的一些成员

二叉树学习笔记之树的旋转

树旋转(Tree rotation)是二叉树中的一种子树调整操作,每一次旋转并不影响对该二叉树进行中序遍历的结果.树旋转通常应用于需要调整树的局部平衡性的场合. >>左旋和右旋 树的旋转有两种基本的操作,即左旋(逆时针方向旋转)和右旋(顺时针方向旋转). 树旋转包括两个不同的方式,分别是左旋转(以P为转轴)和右旋转(以Q为转轴).两种旋转呈镜像,而且互为逆操作. 下图示意了两种树旋转过程中, 子树的初态和终态 +---+ +---+ | Q | | P | +---+ +---+ / \ ri

java学习笔记13--比较器(Comparable、Comparator)

java学习笔记13--比较器(Comparable.Comparator) 分类: JAVA 2013-05-20 23:20 3296人阅读 评论(0) 收藏 举报 Comparable接口的作用 之前Arrays类中存在sort()方法,此方法可以直接对对象数组进行排序. Comparable接口 可以直接使用java.util.Arrays类进行数组的排序操作,但对象所在的类必须实现Comparable接口,用于指定排序接口. Comparable接口的定义如下: public  int

集合类学习笔记

一.概念 集合是存储对象的一种方式.集合中都是存放着地址,方便引用.JDK 1.2版本的时候就有了 二.集合和数组的区别 集合是可变长度,数组是固定长度. 数组可以存储基本数据类型,集合只能存储对象,集合可以存储不同类型的对象. Collection 1.List:元素是有序的,可以重复,有索引 2.Set:元素是无序的,不可以重复,使用hash值排列 三.CURD boolean add(E e); boolean addAll(Collection<? extends E> c); voi

[学习笔记]数据结构与算法

1.排序简单排序:?冒泡排序:将n个数从上往下排列,从第0个数开始依次对前n个.前n-1个.前n-2个数进行比较,保持小数在前大数在后,不符合就交换.在这个过程中,最后一个数始终是最大数.?选择排序:对所有n个.后n-1个.后n-2个依次比较,用一个变量存最小数,一趟比较完成之后,将最小数与所比较数据的第一个数进行交换.在这个过程中,第一个数始终是最小数.?插入排序:从第1个数开始向前扫描比较,小则插入.对于未排序数据,在已排序序列中向前扫描,并找到相应的位置插入.在这个过程中,整个序列局部有序

面向对象先导学习笔记

面向对象先导学习笔记 经过了Python.C语言和数据结构的学习,感觉对于Java这门新学习的语言上手速度明显快了许多.在学习Java的过程中,个人觉得Java的语法规则在很大程度上与C类似,但是在设计程序和具体实现的过程中,更偏向于Python的思路,尤其是对于方法的调用和自带数据结构的使用方面. 数据类型的Java实现 Java自带了大量的数据类型,在完成作业的过程中,个人尝试通过手写二叉树完成问题,但是与Java自带的数据结构相比,无论是在稳定性和运行速度方面都有所欠缺.但是通过自己的摸索

数据结构与算法基础学习笔记

*********************************************            ---算法与数据机结构--- 数据结构:由于计算机技术的发展,需要处理的对象不再是纯粹的数值,还有像字符,表,图像等具有一定结构的数据,需要用好的算法来处理这些数据. 我们把现实中大量而又复杂的问题以特定的数据类型的特定的存储结构保存到主存储器中,以及在此基础上为实现某个功能而执行的相应操作(查找排序),这个相应的操作也叫算法. 数据结构 = 个体 +个体的关系算法 =对存储数据的操