树-上

查找

根据某个给定关键字K,从集合R中找出关键字与K相同的记录

  • 静态查找:集合记录是固定的->没有插入和删除操作,只有查找
  • 动态查找:集合中记录是动态变化的->除查找,还可能发生插入和删除

静态查找

  • 顺序查找,时间复杂度为O(n)
int SequentialSearch(StaticTable *Tbl,ElementType K){
      //在表Tbl[1]~Tb1[n]中查找关键字为K的数据元素
      int i;
      Tb1->Element[0]=K;//建立哨兵,让数组下标为0的位置的值为K
      for(i=Tb1->Length;Tb1->Element[i]!=K;i--);
      return i;//查找成功返回所在位置下标,不成功返回0
  • 二分查找,时间复杂度为O(logN)

假设n个数据元素的关键字满足有序并且是连续存放就可以进行二分查找

int BinarySearch(StaticTable *Tbl,ElementType K){
      //在表Tbl中查找关键字为K的数据元素
      int left,right,mid,NoFound=-1;
      left=1;                          //初始左边界
      right=Tbl->Length;               //初始右边界
      while(left<=right){
            mid=(left+right)/2;        //计算中间元素坐标
            if(K<Tbl->Element[mid])      right=mid-1;//调整右边界
            else if(K>Tbl->Element[mid]) left=mid+1; //调整左边界
            else return mid;             //查找成功,返回数据元素的小标
      }
      return NotFound;                   //查找不成功,返回-1
}

二分查找判定树

  • 判定树上每个结点需要的查找次数为该结点所在的层数
  • 查找成功时查找次数不会超过判定树的深度
  • n个结点的判断树的深度为[log2(n)]+1
  • 平均查找次数(ASL) 每一层的结点个数乘以层数

树的定义

n个结点构成的有限集合 当n=0时称为空树

  • 对于任一棵非空树(n>0) 具备以下性质:
  1. 树中有一个称为“根”的特殊结点,用r表示;
  2. 其余结点可分为m(m>0个互不相交的有限集T1,,Tn,其中每个集合本身又是一棵树,称为原来树的“子树”
  3. 子树互不相交
  4. 除了根结点外,每个结点有且仅有一个父结点
  5. 一棵N个结点的树有N-1条边

树的基本用语

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

二叉树及存储结构

  • 二叉树的定义

一个有穷的结点的集合 这个集合可以为空;若不为空,则它是由根结点和称为其左子树T[L]和右子树T[R]的两个不相交的二叉树组成

  • 二叉树有左右之分

特殊的二叉树

  • 斜二叉树
  • 完美二叉树/满二叉树
  • 完全二叉树

有n个结点的二叉树,对树中结点按从上至下,从左到右的顺序进行编号,编号为i(1<=i<=n)结点与满二叉树中编号为i结点在二叉树中位置相同 完全二叉树是完美二叉树的子集,而且是连续不断的那种

二叉树的几个重要性质

  • 一个二叉树第i层的最大结点数为:2^(i-1),i>=1;
  • 深度为K的二叉树有最大结点总数为:2^(k)-1,k>=1
  • 对任何非空二叉树T,若n[0]表示叶结点的个数,n[2]是度为2的非叶结点个数,那么两者满足关系n[0]=n[2]+1

二叉树的抽象数据类型定义

  • 操作集
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):层次遍历,从上到下,从左到右

二叉树的存储结构

顺序存储结构

  • 完全二叉树

按从上至下,从左到右的顺序存储n个结点的完全二叉树的结点父子关系

  • 一般二叉树采用上述结构会造成很大的空间浪费

链表存储

typedef struct TreeNode *BinTree;
typedef BinTree Position;
struct TreeNode{
      ElementType Data;
      BinTree Left;
      BinTree Right;
};

二叉树遍历

先序遍历中序遍历后序遍历其实差距不大 访问根结点的顺序不一样

  • 递归遍历
//先序遍历
//1.访问根结点
//2.先序遍历左子树
//3.先序遍历右子树
void PreOrderTraversal(BinTree BT){
      if(BT){
            printf("%d",BT->Data);
            PreOrderTraversal(BT->Left);
            PreOrederTraversal(BT->Right);
      }
}

//中序遍历
//1.中序遍历左子树
//2.访问根结点
//3.中序遍历右子树
void InOrderTraversal(BinTree BT){
      if(BT){
            InOrderTraversal(BT->Left);
            printf("%d",BT->Data);
            InOrderTraversal(BT->Right);

//后序遍历
//1.后序遍历左子树
//2.后序遍历右子树
//3.访问结点
void PostOrderTraversal(BinTree BT){
      if(BT){
            PostOrderTraversal(BT->left);
            PostOrderTraversal(BT->Right);
            printf("%d",BT->Data);
      }
}
  • 先序中序后序遍历过程:遍历过程中经过结点的路线一样,只是访问各结点的时机不同

非递归遍历算法

  • 中序遍历非递归算法
  • 遇到一个结点,就把它压栈,并去遍历它的左子树
  • 当左子树遍历结束后,从栈顶弹出这个结点并访问它
  • 然后按其右指针再去中序遍历该结点的右子树
void InOrderTraversal(BinTree BT){
      BinTree T=BT;
      Stack S = CreatStack(MaxSize);
      while(T||!TsEmpty(s)){
            while(T){         //一直向左并将沿途结点压入堆栈
                  Push(S,T);
                  T=T->Left;
            }
            if(!TsEmpty(s)){
                  T=Pop(S);  //结点弹出堆栈
                  printf("%5d",T->Data);  //打印结点
                  T=T->Right;   //转向右子树
            }
      }
}
  • 先序遍历非递归算法
void InOrderTeaversal(BinTree BT){
      BinTree T BT;
      Stack S=CreatStack(MaxSize);
      while(T||!IsEmpty(S)){
            while(T){
                  Push(S,T);
                  T=T->Left;
            }
            if(!IsEmpty(S)){
                  T=Pop(S);
                  T=T->Right;
            }
      }
}

层序遍历

  • 队列实现:遍历从根结点开始,首先将根结点入队,然后开始执行循环:结点出队,访问该结点,其左右儿子入队
//从队列中取出一个元素
//访问该元素所指结点
//若该元素所指结点的左右孩子结点非空,则将其左右孩子的指针顺序入队
void LevelOrderTraversak ( BinTree BT ){
      Queue Q;BinTree T;
      if ( !BT ) return;
      Q = CreatQueue ( MaxSize );
      AddQ ( Q,BT );
      while ( !IsEmptyQ ( Q ) ){
            T = DeleteQ( Q );
            printf("%d\n",T->Data);
            if (T->Left) AddQ(Q,T->Left);
            if (T->Right)AddQ(Q,T->Right);
      }
}
  • 遍历二叉树的应用,输出二叉树中的叶子结点
void PreOrderPrintLeaves (BinTree BT){
      if(BT){
            if(!BT->Left && !BT->Right){
                  printf("%d",BT->Data);
            PreOrderPrintLeaves (BT->Left);
            PreOrderPrintLeaves (BT->Right);
  • 求二叉树的高度
//Height=max(Hl,Hr)+1
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;
}
  • 二元运算表达式树及其遍历
1. 先序遍历得到前缀表达式
2. 中序遍历得到中缀表达式(得到的中缀表达式可能会改变原表达式的意思)
3. 后序遍历得到后缀表达式
  • 可有中序遍历和其他两种遍历中的任意一种共同确定一个二叉树
1. 根据先序遍历序列第一个结点确定根结点
2. 根据结点在中序遍历序列中分割出左右两个子序列
3. 对左右子树分别递归使用相同的方法继续分解
posted @ 2020-07-23 10:59  小哑师兄  阅读(110)  评论(0编辑  收藏  举报