《数据结构与算法》整理-二叉树

整理张铭老师《数据结构与算法》笔记

5.二叉树

5.1 二叉树概念

结点的有限集合 ,空集或一个根结点及两颗互不相交的左右子树的二叉树组成的结合
满二叉树、完全二叉树、扩充二叉树

5.2 二叉树的抽象数据类型

遍历二叉树

5.2.1 深度优先遍历二叉树

1)前序法
2)中序法
3)后序法
深度优先遍历二叉树(递归)

template<class T>
void BinaryTree<T>::DepthOrder(BinaryTreeNode<T>*root)
{
  if(root!=NULL){
          Visit(root);  //前序
          Depthorder(root->leftchild());  //递归访问左子树
          Visit(root);   //中序
          Depthorder(root->rightchild());  //递归访问右子树
          Visit(root);   //后序
          }
}       

DFS遍历二叉树的非递归算法
1)非递归前序遍历二叉树思想

  • 遇到一个结点,访问该结点,并把此结点的非空右结点推入栈中,然后下降去遍历左子树;
  • 遍历完左子树后,从栈顶拖出一个结点,并按它的右链接指示再去遍历该结点的右子树结构。
template<class T> 
void BinaryTree<T>::PreOrderWithoutRecusion(BinaryTreeNode<T>*root{
using std::stack;//使用STL中的stack
stack<BinaryTreeNode<T>*aStack;
BinaryTreeNode<T>*pointer=root;
aStack.push(NULL)//栈底监视哨
while(pointer){  //或者!aStack.empty()
    Visit(point->value()); //访问当前结点
    if (pointer->rightchild()!=NULL) //右孩子入栈
        aStack.push(pointer->rightchild());
    if (pointer->leftchild()!=NULL)
        pointer = pointer->leftchild(); //左路下降
    else {    //左子树访问完毕,转向访问右子树
        pointer = aStack.top();
        aStack.pop();  //栈顶元素退栈 
        }
   }

2)非递归中序遍历二叉树思想

  • 遇到一个结点,把它推入栈中遍历其左子树
  • 遍历完左子树,从栈顶托出该结点并访问之,按照其右链地址遍历该结点的右子树
template<class T> void
BinaryTree<T>::PreOrderWithoutRecusion(BinaryTreeNode<T>*root {
    using std::stack;   // 使用STL中的stack
    stack<BinaryTreeNode<T>*>aStack;
    BinaryTreeNode<T>*pointer = root;
    while (!aStack.empty()||pointer){
       if(pointer) {
        Visit(pointer->value());  //前序访问点
        aStack.push(pointer);     //当前结点地址入栈
        pointer=pointer->leftchild(); //当前链接结构指向左孩子
        }
        else {  //左子树访问完毕,转向访问右子树
        pointer = aStack.top();
        aStack.pop();     //栈顶元素退栈
        Visit(pointer->value());  //访问当前结点
        pointer=pointer->rightchild(); //当前链接结构指向右孩子
        }
     }
 }

3)非递归后序遍历二叉树思想

  • 给栈中元素加上特征位
enum Tags{Left,Right};   //定义枚举类型标志位
template<class T>
class StackElement  {  //栈元素的定义
public:
     BinaryTreeNode<T>*pointer;  //指向二叉树结点的指针
     Tags tag;  //标志位
}
template<class T> 
void BinaryTree<T>::PreOrderWithoutRecusion
(BinaryTreeNode<T>*root{
using std::stack;//使用STL中的stack
stack<BinaryTreeNode<T>*aStack;
BinaryTreeNode<T>*pointer=root;
aStack.push(NULL)//栈底监视哨
while (!aStack.empty()||pointer) {
   while(pointer != NULL){  //沿非空指针压栈,并左路下降
   element.pointer = pointer; element.tag = Left;
   aStack.push(element);     //把标志位为Left的结点压入栈
   pointer = pointer->leftchild();
}
element = aStack.top();aStack.pop(); //获得栈顶元素,并退栈
pointer=element.pointer;
if(element.tag==Left){   //如果从左子树回来
   element.tag = Right; aStack.push(element);//置标志为Right
   pointer=pointer->rightchild();
   }
   else {  //如果从右子树回来
     Visit(pointer->value());  //访问当前结点
     pointer=NULL;     //置point指针为空,以继续弹栈 
     }
   }
}
   


深搜算法的时间代价分析
各种遍历中,每个结点都被访问且只被访问一次,时间代价为O(n)
深搜算法的空间代价分析
深搜:栈的深度与树的高度有关 ;最好O(logn),最坏O(n)

5.2.2 宽度优先遍历二叉树

从二叉树的的第0层(根结点)开始,自上至下逐层遍历;在同一层中,按从左到右的顺序对结点逐一访问。

template<class T> 
void BinaryTree<T>::LevelOrder(BinaryTreeNode<T>*root{
   using std::queue;//使用STL中的队列
   queue<BinaryTreeNode<T>*aQueue;
   BinaryTreeNode<T>*pointer=root;   //保存输入参数
   if (pointer)aQueue.push(pointer); //根结点入队列
   while(!aQueue.empty()) {   //队列非空
    pointer = aQueue.front();//取队列首结点
    aQueue.pop();//当前结点出队列
    Visit(pointer->value());
    if(pointer->leftchild())
       aQueue.push(pointer->leftchild()); //左子树进队列
    if(pointer->rightchild())
       aQueue.push(pointer->rightchild()); //右子树进队列

宽搜算法的时间代价分析
各种遍历中,每个结点都被访问且只被访问一次,时间代价为O(n)
宽搜算法的空间代价分析
宽搜:与树的最大宽度有关 ;最好O(1),最坏O(n)

5.3 二叉树的存储结构

  • 二叉链表
  • 三叉链表

5.4 二叉树搜索树(BST)

  • 或者是一颗空树;

  • 或者是具有下列性质的二叉树:
    对于任何一个结点,设其值为K,该结点的左子树(若不空)的任意一个结点的值都小于K;该结点的右子树(若不空)的任意一个结点的值都大于K;且它的左右子树也分别为BST
    性质

  • 性质:中序遍历是正序的
    BST删除(值替换)

void BinarySearchTree<T>:::removehelp(BinaryTreeNode<T>*&rt,const T val){
  if (rt==NULL)cout<<val<<"is not in the tree.\n":
  else if (val<rt->value())
    removehelp(rt->leftchild(),val);
  else if (val>rt->value())
    removehelp(rt->leftchild(),val);
  else {
    BinaryTreeNode<T>*temp=rt;
    if(rt->leftchild()==NULL) rt=rt->rightchild();
    else if(rt->rightchild()==NULL) rt=rt->rightchild();
    else {
        temp = deletemin(rt->rightchild());
        rt->setValue(temp->value());
        }
     delete temp;
     }
 }

找rt右子树中最小结点,并删除

template<class T>
BinarySearchTree<T>:::deletemin(BinaryTreeNode<T>*&rt){
  if (rt->leftchild()!=NULL)
     return deletemin(rt->leftchild());
  else {  //找到右子树中最小,删除
     BinaryTreeNode<T>*temp=rt;
     rt=rt->rightchild();
     return temp;
     }
 }

5.5 堆与优先队列

堆:

  • 完全二叉树的层次序列,可以用数组表示
  • 最小堆是一个关键码序列
  • 堆中存储的数是局部有序的,堆不唯一(结点的值与其孩子的之间存在限制,任何一个结点与其兄弟之间没有直接的限制)
  • 从逻辑角度看,堆实际上是一种树形结构
5.5.1 堆的类定义

1)对最小堆用筛选法SiftDown调整

template<class T>
void MinHeap<T>::SiftDown(int position){
    int i=position;   //标识父结点
    int j=2*i+1;      //标识关键值较小的子结点
    Ttemp=heapArray[i]; //保存父结点
    while (j<CurrentSize) {
       if((j<CurrentSize)&&(heapArray[j]>heapArray[j+1]))
         j++;//j指向数值较小的子结点
       if(temp>heapArray[j] {
         heapArray[i]=heapArray[j];
         i=j;
         j=2*j+1 ; //向下继续
       }
       else break;
     }
     heapArray[i]=temp;
 }

2)对最小堆用筛选法SiftUp向上调整

template<class T>
void MinHeap<T>::SiftUp(int position){
// 从position向上调整,使序列成为堆
    int tempppos=position;
    Ttemp=heapArray[temppos];
    while((temppos>0)&&(heapArray[parent(temppos)]>temp)) {
      heapArray[temppos]=heapArray[parent(temppos)];
         temppos=parent(temppos);
    }
    heapArray[temppos]=temp;//找到最终位置
 }
5.5.2 建最小堆过程

从第一个分支结点heapArray[CurrentSize/2-1] 开始,自底向上逐步把以子树调整成堆

tempplate<class T>
void MinHeap<T>::BuildHeap()
{
   //反复调用筛选函数
   for (int i=CurrentSize/2-1; i>=0; i--)
      SiftDown(i);
 }
5.5.3 最小堆插入新元素
template<class T>
bool MinHeap<T>::Insert(const T&newNode)
// 向堆中插入新元素newNode
{
    if(CurrentSize==MaxSize)   //堆空间已满
       return false;
    heapArray[CurrentSize]=newNode;
    SiftUp(CurrentSize);  //向上调整
    CurrentSize++;
}
5.5.4最小堆删除元素操作
template<class T>
bool MinHeap<T>::Remove(int pos,T&node) {
   if((pos<0)||(pos>=CurrentSize))
       return false;
   Ttemp=heapArray[pos];
   heapArray[pos]=heapArray[--CurrentSize];
   if(heapArray[parent(pos)]>heapArray[pos])
      SiftUp(pos);  //向上筛
   else SiftDown(pos);  //向下筛
   node=temp;
   return ture;
}
5.5.5 建堆效率分析

最小堆操作效率 ,建堆算法时间代价O(n)
堆有logn层深,插入删除平均时间代价和最差时间代价都是O(logn)

5.5.6 优先队列

堆可以用于实现优先队列
优先队列:根据需要释放具有最大(小)值的对象

5.6 Huffman树及其应用

  • 计算机二进制编码
  • 等长编码
  • 空间效率
5.6.1 数据压缩和不等长编码

1)Huffman树与前缀编码
Huffman编码将代码与字符相联系

  • 不等长编码
  • 代码长度取决于对应字符的相对使用频率或“权”
5.6.2 建立Huffman编码树

权越大的叶结点离根越近

  • 首先,按照“权”将字符排成一列,接着拿走权值最小的两个字符,再将他们标记为Huffman树的树叶,将这两个树叶标为一个分支结点的两个孩子,而该结点的权为两树叶的权之和。
  • 将所得权放回序列,使“权”的顺序保持
  • 重复直至序列处理完毕
5.6.3 Huffman树应用

1) 频率越大其编码越短
2)译码:从左至右逐位判别代码串,直至确定一个字符

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值