数据结构(浙大慕课):二叉树


查找

  • 静态查找:集合是固定的,没有插入和删除操作
  • 动态查找:集合中的记录是动态变化的,可以插入和删除
    静态查找
    方法1:顺序查找
    顺序查找算法的时间复杂度为O(n)
int SequentialSearch(List tb1, ElementType K){
    int i;
    tb1->Element[0] = K; // 建立哨兵
    for(i = tb1->length; tb1->Element[i] != K; i--); //从下往上循环直到K
    return i;  // 成功返回下标,不成功返回0
}

方法2:二分查找算法
二分查找算法具有对数的时间复杂度O(logN)

int BinarySearch ( StaticTable * Tb1, ElementType K) 
{ /*在表Tbl中查找关键字为K的数据元素*/
    int left, right, mid, NoFound=-1;
    left = 1;
    right = Tb1->Length; 
    while ( left <= right ) 
    {
        mid = (left+right)/2;
        if( K < Tb1->Element[mid]) right = mid-1;
        else if( K > Tb1->Element[mid]) left = mid+1;
        else return mid; /*查找成功,返回数据元素的下标*/
    }
return NotFound; /*查找不成功,返回-1*/ 
}

二叉树

树的基本概念

定义:n(n≥0)个结点构成的有限集合

  • 子树是不相交的
  • 除了根结点外,每个结点有且仅有一个父结点
  • 一棵N个结点的树有N - 1 条边
    树的基本术语:
  • 结点的度(Degree):结点的子树个数
  • 树的度:树的所有结点中最大的度数
  • 叶结点(Leaf):度为0的结点
  • 父结点(Parent):有子树的结点是其子树的根结点的父结点
  • 子结点(Child):若A结点是B结点的父结点,则称B结点是A结点的子结点;子结点也称孩子结点
  • 兄弟结点(Sibling):具有同一父结点的各结点彼此是兄弟结点
  • 路径和路径长度:从结点n1到nk的路径为一个结点序列n1 , n2,… , nk, ni是 ni+1的父结点。路径所包含边的个数为路径的长度。
  • 祖先结点(Ancestor):沿树根到某一结点路径上的所有结点都是这个结点的祖先结点。
  • 子孙结点(Descendant):某一结点的子树中的所有结点是这个结点的子孙。
  • 结点的层次(Level):规定根结点在1层,其它任一结点的层数是其父结点的层数加1。
  • 树的深度(Depth):树中所有结点中的最大层次是这棵树的深度。

二叉树的基本概念

特殊二叉树:斜二叉树、完美二叉树(满二叉树)、完全二叉树
在这里插入图片描述
重要性质

  • 一个二叉树第i层的最大节点数为:2i-1,i ≥ 1。
  • 深度为k的二叉树有最大结点总数为:2k- 1,k ≥ 1。
  • 对任何非空二叉树 T,若n0表示叶结点的个数、n2是度为2的非叶结点个数,那么两者满足关系n0 = n2 +1。
    在这里插入图片描述在这里插入图片描述

二叉树的表示及基础操作

二叉树的存储结构

1、顺序存储结构
在这里插入图片描述
在这里插入图片描述

2、二叉树的链表结构

typedef struct TNode *Position;
typedef Position BinTree;	//二叉树类型
struct TNode{				//树结点定义
		ElementType Data;	//节点数据
		BinTree Left;		//指向左子树
		BinTree Right;		//指向右子树

在这里插入图片描述

二叉树操作集

操作集:BT∈ BinTree, Item ∈ ElementType,重要操作有:
1、Boolean IsEmpty( BinTree BT ): 判别BT是否为空;
2、void Traversal( BinTree BT ):遍历,按某顺序访问每个结点;
3、BinTree CreatBinTree( ):创建一个二叉树。

1、void PreOrderTraversal( BinTree BT ):先序----根、左子树、右子树;
2、void InOrderTraversal( BinTree BT ): 中序—左子树、根、右子树;
3、void PostOrderTraversal( BinTree BT ):后序—左子树、右子树、根
4、void LevelOrderTraversal( BinTree BT ):层次遍历,从上到下、从左到右

递归遍历算法:

1、先序遍历:根 -> 左-> 右,第一次碰到节点就输出

void PreOrderTraversal( BinTree BT){
	if( BT ){
		printf("%d", BT -> Data);
		PreOrderTraversal( BT -> Left);
		PreOrderTraversal( BT -> Right);
	}
}

2、中序遍历:左 -> 根-> 右,第二次碰到节点输出

void InOrderTraversal( BinTree BT){
	if( BT ){
		PreOrderTraversal( BT -> Left);
		printf("%d", BT -> Data);
		PreOrderTraversal( BT -> Right);
	}
}

3、后序遍历:左 -> 右-> 根,第三次碰到节点输出

void PostOrderTraversal( BinTree BT){
	if( BT ){
		PreOrderTraversal( BT -> Left);
		PreOrderTraversal( BT -> Right);
		printf("%d", BT -> Data);
	}
}

非递归遍历算法:

1、中序遍历

/*思路:
遇到一个结点,就把它压栈,并去遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个结点并访问它;
然后按其右指针再去中序遍历该结点的右子树。*/
void InOrderTraversal( BinTree BT ){ 
	BinTree T=BT;
	Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
	while( T || !IsEmpty(S) ){
		 while(T){ /*一直向左并将沿途结点压入堆栈*/
			 Push(S,T);
			 T = T->Left;
		 }
		 if(!IsEmpty(S)){	//这个判断其实没必要,因为如果满足while循环内的条件,则此处条件一定符合
			 T = Pop(S); /*结点弹出堆栈*/
			 printf(%5d”, T->Data); /*(访问)打印结点*/
			 T = T->Right; /*转向右子树*/
		 }
	}
}

2、先序遍历

void InOrderTraversal( BinTree BT ){
	BinTree T BT;
	Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
	while( T || !IsEmpty(S) ){
		while(T){ /*一直向左并将沿途结点压入堆栈*/
			printf(%5d”, T->Data); /*(访问)打印结点*/
			Push(S,T);
			T = T->Left;
		}
		 if(!IsEmpty(S)){
			 T = Pop(S); /*结点弹出堆栈*/
			 T = T->Right; /*转向右子树*/
		 }
	}
}

3、后序遍历

/*思路:
在结点中置一个标志位,用于标记访问的次数。
初始化时设为0,第一次访问时设为1,然后继续访问左子树;
第二次访问时设为2,然后继续方位右子树;
第三次访问时即可输出结点的值;
*/
//结点类
struct TreeNode{
    int Data;
    TreeNode *Left, *Right;
    int Flag;//访问次数标志位
};
typedef TreeNode *Tree;

void InOrderTraversal( BinTree BT ){
    BinTree T = BT;
    stack<BinTree> S;
    while( T || !S.empty()){
        if( T ){ //一直向左并将沿途结点压入堆栈
            T -> Flag = 1;
            S.push(T);
            T = T->Left;
        }
        else{
            T = S.top();
            if( T -> Flag == 1 ){
                T -> Flag = 2;
                T = T -> Right;
            }
            else{
                cout << T -> Data << endl;
                S.pop();
                T = NULL;
            }
        }
    }
}

层序遍历
基本过程:首先根节点入队。从队列的头部访问一个元素,访问该元素的左右孩子,若左右孩子非空,则将非空的左孩子或右孩子的指针顺序入列,然后弹出队列头部元素,然后继续向下访问。
在这里插入图片描述
代码

#include<queue>	//直接用队列容器
void LevelOrderTraversal ( int n, BinTree BT ){
	int results[n] = {0};	//n为元素个数,该数组用于存储层序遍历的输出顺序结果
	queue q;
	q.push( BT );
	int i = 0;
	while( !q.empty() ){
		if( q.front() -> Left ) q.push( q.front() -> Left );
		if( q.front() -> Right ) q.push( q.front() -> Right );
		results[ i++ ] = q.front() -> Data;
		q.pop();	//访问完头元素后就将头元素弹出,继续访问下一个元素,直到队列为空
	}
	for( i = 0; i < n; i++) cout << results[ i ] << endl;	//输出层序遍历
}
遍历二叉树的应用

【例1】输出二叉树中的叶子结点

/*思路:
在二叉树的遍历算法中增加检测结点的“左右子树是否都为空”。*/
void PreOrderPrintLeaves( BinTree BT )
{
	 if( BT ) {
		 if ( ! BT-> Left && ! BT -> Right ) printf(%d” , BT->Data );
		 PreOrderPrintLeaves ( BT->Left );
		 PreOrderPrintLeaves ( BT->Right );
	 }
}

【例2】输出二叉树的高度
在这里插入图片描述

int PostOrderGetHeight( BinTree BT ){
	int HL, HR, MaxH;
	 if( BT ) {
		 HL = PostOrderGetHeight(BT->Left); /*求左子树的深度*/
		 HR = PostOrderGetHeight(BT->Right); /*求右子树的深度*/
		 MaxH = (HL > HR)? HL : HR; /*取左右子树较大的深度*/
		 return ( MaxH + 1 ); /*返回树的深度*/
	 }
	 else return 0; /* 空树深度为0 */
}

【例4】由两种遍历序列确定二叉树
在这里插入图片描述

【例3】由两种遍历序列确定二叉树
!关键:两种遍历种必须要有中序遍历才能唯一确定一棵二叉树。
先序和中序遍历序列来确定一棵二叉树
思路:
1、根据先序遍历序列第一个结点确定根结点;
2、根据根结点在中序遍历序列中分割出左右两个子序列
3、对左子树和右子树分别递归使用相同的方法继续分解。

二叉搜索树(二叉排序树/二叉查找树)

二叉搜索树概念及性质:

  • 可以为空
  • 若不为空则满足以下性质
    1、非空左子树的所有键值小于其根结点的键值。
    2、非空右子树的所有键值大于其根结点的键值。
    3、左、右子树都是二叉搜索树。

二叉搜索树操作的特别函数:
1、Position Find( ElementType X, BinTree BST ):从二叉搜索树BST中查找元素X,返回其所在结点的地址;
2、Position FindMin( BinTree BST ):从二叉搜索树BST中查找并返回最小元素所在结点的地址;
3、Position FindMax( BinTree BST ):从二叉搜索树BST中查找并返回最大元素所在结点的地址。
4、BinTree Insert( ElementType X, BinTree BST ):往二叉树插入元素
5、BinTree Delete( ElementType X, BinTree BST ):从二叉树中删除元素

1、查找操作 Find

/*法一:尾递归,执行效率较低*/
Position Find( ElementType X, BinTree BST ){
	if( !BST ) return NULL; /*查找失败*/
	if( X > BST->Data )
		return Find( X, BST->Right ); /*在右子树中继续查找*/
	else if( X < BST->Data )
		return Find( X, BST->Left ); /*在左子树中继续查找*/
	else /* X == BST->Data */
		return BST; /*查找成功,返回结点的找到结点的地址*/
}

/*法二:迭代,比尾递归的执行效率高*/
Position IterFind( ElementType X, BinTree BST ){
	 while( BST ) {
		 if( X > BST->Data ) BST = BST->Right; /*向右子树中移动,继续查找*/
		 else if( X < BST->Data ) BST = BST->Left; /*向左子树中移动,继续查找*/
		 else /* X == BST->Data */ return BST; /*查找成功,返回结点的找到结点的地址*/
	 }
	 return NULL; /*查找失败*/
}

2、查找最小元素操作 FindMin:最大元素一定是在树的最左分枝的端结点上

Position FindMin( BinTree BST ) {
	if( !BST ) return NULL; /*空的二叉搜索树,返回NULL*/
	else if( !BST->Left ) return BST; /*找到最左叶结点并返回*/ 
	else return FindMin( BST->Left ); /*沿左分支继续查找*/ }

3、查找最大元素操作 FindMax:最大元素一定是在树的最右分枝的端结点上

Position FindMax( BinTree BST ) {
	if( BST ){
		while( BST->Right ) BST = BST->Right;/*沿右分支继续查找,直到最右叶结点*/
	}
	return BST;
} 

4、插入操作Insert:将指定元素插入二叉搜索树中

BinTree Insert( BinTree BST, ElementType X ){
    if( !BST ){
        BST = (BinTree)malloc(sizeof(TNode));
        BST -> Data = X;
        BST -> Left = NULL;
        BST -> Right = NULL;
    }
    else if( X < BST -> Data ) BST -> Left = Insert( BST -> Left, X);
    else if( X > BST -> Data ) BST -> Right = Insert( BST -> Right, X);
    return BST;
}

5、删除操作Delete:从二叉搜索树中删除指定元素

/*思路:
不需要真的删除那个结点!只需要删除结点元素,找别的元素将其进行替代即可。
删除原来的元素,将该节点右子树的最左端元素移过来进行替代,然后将该元素也进行删除即可。
*/
BinTree Delete( BinTree BST, ElementType X ){
    BinTree tmp;
    if( !BST ) printf("Not Found\n");
    else{
        if( X < BST -> Data ) BST -> Left = Delete( BST -> Left, X);
        else if( X > BST -> Data ) BST -> Right = Delete( BST -> Right, X);
        else{
            if( BST -> Left && BST -> Right){    //若左右均不空,则找右子树上的最小值替代
                tmp = FindMin(BST -> Right);
                BST -> Data = tmp -> Data;
                BST -> Right = Delete(BST -> Right, tmp -> Data);
            }
            else{
                tmp = BST;
                if( !BST -> Left) BST = BST -> Right;//若左为空,则直接找右儿子替代
                else if( !BST -> Right) BST = BST -> Left;//若右为空,则直接找左儿子替代
            }
        }
    }
    return BST;
}

平衡二叉树(AVL树)

概念:
空树,或任一结点左、右子树高度差的绝对值不超过1
性质:
给定节点数 n 的AVL树的最大高度为O(log2n)
平衡二叉树的调整:
将破坏平衡的节点称为麻烦节点,将被破坏平衡的节点称为发现者。
RR 旋转(右单旋):麻烦节点在发现者右子树的右子树上
LL 旋转(左单旋):麻烦节点在发现者左子树的左边
RL 旋转:麻烦节点在发现者右子树的左边
LR 旋转:麻烦节点在发现者左子树的右边

平衡二叉搜索树的构造代码

typedef struct TreeNode *Tree;
struct TreeNode{
    int data;
    Tree left;
    Tree right;
    int height;
};

int GetHight( Tree tree ){
    int hl, hr;
    if(tree){
        hl = GetHight(tree -> left);
        hr = GetHight(tree -> right);
        tree -> height = max(hl, hr) + 1;
    }
    else return 0;
    return tree -> height;
}

Tree InsertTree( Tree tree, int data ){
    if( !tree ){
        tree = (Tree)malloc(sizeof(struct TreeNode));
        tree -> data = data;
        tree -> left = NULL;
        tree -> right = NULL;
        tree -> height = 0;
    }
    else if(data > tree -> data){
        tree -> right = InsertTree(tree -> right, data);
        if((GetHight(tree -> right) - GetHight(tree -> left)) == 2){
            if( data > tree -> right ->data ) tree = SingleRightRotation(tree);
            else tree = DoubleRightLeftRotation(tree);
        }
    }
    else if(data < tree -> data){
        tree -> left = InsertTree(tree -> left, data);
        if((GetHight(tree -> left) - GetHight(tree -> right)) == 2){
            if( data < tree -> left -> data) tree = SingleLeftRotation(tree);
            else tree = DoubleLeftRightRotation(tree);
        }
    }
    tree -> height = max(GetHight(tree -> left), GetHight(tree -> right)) + 1;
    return tree;
}

Tree SingleLeftRotation ( Tree tree ){
    Tree temp;
    temp = tree -> left;
    tree -> left = temp -> right;//感觉这个没必要,因为如果temp的右边有数据,那么tree早都是不平衡的了
    temp -> right = tree;

    tree -> height = max(GetHight(tree -> left), GetHight(tree -> right)) + 1;
    temp -> height = max(GetHight(temp -> left), tree -> height) + 1;

    return temp;
}

Tree DoubleLeftRightRotation ( Tree tree ){
    tree -> left = SingleRightRotation( tree -> left);
    return SingleLeftRotation( tree );
}

Tree SingleRightRotation ( Tree tree ){
    Tree temp;
    temp = tree -> right;
    tree -> right = temp -> left;
    temp -> left = tree;

    tree -> height = max(GetHight(tree -> left), GetHight(tree -> right)) + 1;
    temp -> height = max(tree -> height, GetHight(temp -> right)) + 1;

    return temp;
}
Tree DoubleRightLeftRotation( Tree tree ){
    tree -> right = SingleLeftRotation( tree -> right);
    return SingleRightRotation( tree );
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容如下: ├─程序例子 │ │ Gauss.cpp │ │ MOVE.C │ │ RUIYUE_3.C │ │ RUIYUE_4.C │ │ │ ├─1概述 │ │ bb_sort.cpp │ │ ex1-9.cpp │ │ ex1-91.cpp │ │ │ ├─2线性表 │ │ ex2-11.cpp │ │ ex2-11new.cpp │ │ linklist.cpp │ │ linklist.h │ │ LINKQUEU.C │ │ linkqueue.cpp │ │ linkqueue.h │ │ list.c │ │ LIST2.C │ │ LIST_S15.C │ │ LIST_SL.C │ │ LIST_SQ.C │ │ LIST_SQ5.C │ │ new2-11.cpp │ │ old2-11.cpp │ │ SQLIST.C │ │ Sqlist.cpp │ │ UNION.CPP │ │ │ ├─3堆栈与队列 │ │ bank_simulation.cpp │ │ conversion.cpp │ │ DMXSTACK.C │ │ hanoi.cpp │ │ MAZE.CPP │ │ QUEUE09.C │ │ SQQUEUE.C │ │ Sqqueue.cpp │ │ sqstack.cpp │ │ stack.c │ │ STK_9.C │ │ 表达式求值.cpp │ │ │ ├─5数组 │ │ array.c │ │ array_ex.cpp │ │ array_test.cpp │ │ DK1.c │ │ DK2.c │ │ SANYANZU.C │ │ │ └─6树 │ bitree.cpp │ BITREE11.C │ bitree2.cpp │ BITREE23.C │ BITREE9.C │ Create_bitree.c │ inorder_thr_tree.cpp │ intreading.cpp │ TING6.C │ TRAVERSE.C │ └─课件 sjjg答疑.txt 数据结构1概述.ppt 数据结构2线性表1.ppt 数据结构3线性表2.ppt 数据结构4堆栈与队列1.ppt 数据结构5堆栈与队列2.ppt 数据结构6串.ppt 数据结构7数组1.ppt 数据结构8数组2.ppt 数据结构9树1.ppt 数据结构A树2.ppt 数据结构B树3.ppt 数据结构C图1.ppt 数据结构D图2.ppt 数据结构E查找.ppt 数据结构F排序.ppt 数据结构文稿14.ppt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值