树
定义:由 n 个有限结点组成一个具有层次关系的集合。
-
常见树:二叉树、红黑树、B树等
-
树的术语:
结点:树中存储 数据变量以及指向其他结点的变量等 的一个对象双亲结点(父结点),孩子结点(子结点):若一个结点 A 含有指向其他结点的属性,则这个结点 A 称为其他结点的双亲结点或者父结点;而其他结点则称为结点 A 的孩子结点或者子结点
兄弟结点:具有相同的父结点的结点互称为兄弟结点
根:当树只有一个结点时,该结点为根结点;如果含有多个结点,则没有父结点的结点即为树的根
子树:树中将 根结点以外的结点 当作 “根结点”的“树”
边:连接两个结点的直线,在树的代码中表示为指向其他结点的一个变量
结点的度:该结点的子结点的个数(等于该结点的边的条数)
树的度:该树的 所有结点的度 中的最大值 就是树的度
层次(层):从根开始,一般根为第一层,根的子结点为第二层,以此类推;
树的高度(深度):树的最大层;
堂兄弟结点:父结点在同一层的结点(不是同一个父结点)互为堂兄弟结点
叶子结点(终端结点):度为0的结点,即没有子结点的结点
分支结点(非终端结点):度不为0的结点
祖先:从根结点到该结点的父结点所经分支上的所有结点
子孙:以某结点为根的子树中的任一结点都是该结点的子孙
有序树:结点要按某种先后顺序设置位置的树
无序树:结点位置不需要按照先后顺序设置的树森林:由m(m>=0)棵互不相交的树组成的集合称为森林,每棵树删除了根结点就会变成森林
-
应用场景:文件系统及数据库的索引、哈夫曼编码、括号匹配、linux的进程调度、海量数据并发查询、统计和排序大量字符串等
二叉树
-
概念:每个结点最多只有两个子结点的树;
-
特殊分类:
- 完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
- 满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
- 平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
- 二叉排序树(二叉查找树、二叉搜索树):它是一颗空树,或者具有以下特点:根结点的值大于左子树每一个结点的值,小于右子树每一个结点的值。若要插入与根结点值相等的结点,则将其插入到右子树,或根据需要将其插入到左子树。
-
例题:
-
前序遍历
递归算法:
(1)若二叉树为空,则返回空;
(2)访问根结点;
(3)前序遍历左子树;
(4)前序遍历右子树;非递归算法:
使用辅助栈 s 和辅助指针 p 来实现。
(1)将指针 p 指向二叉树的根结点;
(2)当辅助栈 s 或指针 p 不为空时,执行循环体;
(3)先访问当前结点 p ,并将其压入栈 s 中;
(4)令 p 指向其左孩子;
(5)重复(3)、(4),直到 p 为空为止;
(6)从栈 s 中弹出栈顶元素,并将 p 指向此元素的右孩子;
(7)重复(3)~(6),直到 p 与 s 都为空,遍历结束;使用二叉链表存储的二叉树(孩子兄弟表示法)的前序遍历非递归算法,步骤和非递归算法类似。
-
中序遍历
递归算法:
(1)若二叉树为空,则返回空
(2)中序遍历左子树
(3)访问根结点
(4)中序遍历右子树非递归算法:
(1)将指针p指向二叉树的根结点,当辅助栈 s 或指针 p 不为空时,执行循环体
(2)将p压入栈s中,并令p指向其左孩子
(3)重复执行(2),直到p为空
(4)将栈顶元素弹出,并令p指向此元素
(5)访问当前结点p,并将p指向其右孩子。
(6)重复执行(2)~(5),直到p和s都为空,遍历结束 -
后序遍历
递归算法:
(1)若二叉树为空,则返回空
(2)后序遍历左子树
(3)后序遍历右子树
(4)访问根结点非递归算法:
后序遍历的非递归算法与前两种遍历不同,后序遍历可能会出现从右子树返回后再重复访问右子树的情况,所以使用一个与栈s同步的标识栈tag来区分,这里采用0代表从左子树返回,1代表从右子树返回。
(1)将指针p指向根结点,当辅助栈s或指针p不为空时,执行循环体
(2)将p压入栈s中,将0压入栈tag中,并令p指向其左孩子
(3)重复执行(2),直到p为空
(4)如果tag栈中的栈顶元素为1,将栈s的栈顶元素弹出,并访问此结点,同时要弹出栈tag的栈顶元素,跳至步骤(6)
(5)如果tag栈中的栈顶元素为0,则将p指向栈s的栈顶元素的右孩子,同时弹出栈tag的栈顶元素,再向tag压入一个1,代表访问到此结点时,已经是从右子树返回了
(6)重复执行(2)~(5),直到p与s皆为空。遍历结束。 -
层序遍历
递归算法:
循环+递归void PrintNodeAtLevel(BiTree T,int level){ // 空树或层级不合理 if (NULL == T || level < 1 ) return; if (1 == level){ cout << T->data << " "; return; } // 左子树的 level - 1 级 PrintNodeAtLevel(T->leftChild, level - 1); // 右子树的 level - 1 级 PrintNodeAtLevel(T->rightChild, level - 1); } void LevelTraverse(BiTree T){ if (NULL == T) return; int depth = Depth(T); int i; for (i = 1; i <= depth; i++){ PrintNodeAtLevel(T, i); cout << endl; } }
非递归算法:
使用队列
(1)将根结点入队
(2)若队列不为空,则执行(3)(4)(5),直到队列为空,结束遍历
(3)访问队头
(4)判断队头结点是否有左右孩子,有则入队
(5)队头结点出队 -
二叉树的深度
递归算法:
(1)声明一个变量deep来记录深度,若二叉树不为空,则执行(2)(3)(4);
(2)获取并记录左子树的深度;
(3)获取并记录右子树的深度;
(4)比较左右子树的深度,取较大者+1,即是以当前结点为根的二叉树的深度;
(5)返回二叉树的深度deep;
-