树的基本内容

二叉树的存储结构 :
  1. 顺序存储结构 :

    完全二叉树:按从上至下、从左到右顺序存储

    n个结点的完全二叉树的结点父子关系:

    1. 非根结点(序号 i > 1)的父结点的序号是 i / 2;

    2. 结点(序号为 i )的左孩子结点的序号是 2i,(若2 i <= n,否则没有左孩子);

    3. 结点(序号为 i )的右孩子结点的序号是 2i+1,(若2 i +1<= n,否则没有右孩子);

      但是一般不用该方式,空间浪费严重

2.链表存储 :(儿子-兄弟表示法 )

typedef struct tree_node* tree;
struct tree_node{
  	elementtype data;
    tree left;
    tree right;
};
二叉树的遍历 :

1. 递归遍历:

  • 先序遍历:(遍历过程为: ① 访问根结点; ② 先序遍历其左子树; ③ 先序遍历其右子树)
   void preorder_traversal(tree bt){
       if (bt){
           cout << bt->data ;	//无非是该语句位置不同
           preorder_traversal(bt->left);
           preorder_traversal(bt->right);
       }
   }
   //如果想仅输出叶子节点,则仅需对cout语句添加判断if ( !bt->left && !bt->right).
   
   //求二叉树的高度://
   int binary_height(tree bt){
       int hl,hr,maxh;
       if (bt){
           hl=binary_height(bt->left);		//左子树深度
           hr=binary_height(bt->right);		//右子树深度
           maxh=(hl>hr)?hl:hr;		//取左右子树最大深度
           return (maxh+1);	//树高为左右子树最大深度+1
       }
       else return 0;	//空树深度为0
   }
  • 后序遍历:(遍历过程为: ① 后序遍历其左子树; ② 后序遍历其右子树; ③ 访问根结点)
   void postorder_traversal(tree bt){
       if (bt){
           postorder_traversal(bt->left);
           postorder_traversal(bt->right);
           cout << bt->data ;	//^_~
       }
   }
  • 中序遍历 :(遍历过程为: ① 中序遍历其左子树; ② 访问根结点; ③ 中序遍历其右子树)
   void inorder_traversal(tree bt){
       if (bt){
           inorder_traversal(bt->left);
           cout << bt-<data ;	//^_~
           inorder_traversal(bt->right);
       }
   }

2. 非递归遍历:

  • 先序遍历
   void preorder_traversal(tree bt){
       stack s=create_stack(maxsize);	//创建,初始化堆栈s
       while(bt || !is_empty(s)){
           while(bt){
               cout << bt->data;	//打印结点^_~
               push(s,bt);	//一直向左并将沿途结点压入堆栈
               bt=bt->left;
           }
           if (!is_empty){
               bt=pop(s);	//结点弹出堆栈
               bt=bt->right;	//转向右子树
           }
       }
   }
  • 后序遍历
   /*由于后续遍历比较特殊,需要更换思路,如下:
   先把根节点压栈两次,然后出栈,判断是否是第一次出栈,
   若是,存入儿子节点,每个节点压栈两次,每次出栈的时候,进行判断*/
   void postorder_traversal(tree bt){
       stack s=create_stack(maxsize);	//创建,初始化堆栈s
       if (bt){
           push(s,bt);	//根节点压栈两次
           push(s,bt);
           tree temp=top(s);	//top(s)为获取栈顶元素,不出栈
       }
       while (!is_empty(s)){	//根节点第二次出栈后,跳出循环
           temp=pop(s);
           if ( !is_empty(s) && temp==top(s) ){	//栈不空,第一次出栈
               if (temp->right){
                   push(s,temp->right);	//右儿子压栈两次
                   push(s,temp->right);
               }
               if (temp->left){
                   push(s,temp->left);		//左儿子压栈两次
                   push(s,temp->left);
               }
           }
           else cout << temp->data ;	//第二次出栈,输出
       }
   }
  • 中序遍历
   void inorder_traversal(tree bt){
       stack s=create_stack(maxsize);	//创建,初始化堆栈s
    while(bt || !is_empty(s)){
           while(bt){
               push(s,bt);	//一直向左并将沿途结点压入堆栈
               bt=bt->left;
           }
           if (!is_empty){
               bt=pop(s);	//结点弹出堆栈
               cout << bt->data;	//打印结点^_~
               bt=bt->right;	//转向右子树
           }
       }
   }

3. 层序遍历 :

基本过程:先根结点入队,然后从队列中取出一个元素; 访问该元素所指结点;

若该元素所指结点的左、右孩子结点非空,则将其左、右孩子的指针顺序入队。

     void levelorder_traversal(tree bt){
         queue q=create_queue();
      if (bt) add(q,bt);
         while(!is_empty(q)){
          bt=delete(q);
             cout << bt->data ;
             if (bt->left) add(q,bt->left);
             if (bt->right) add(q,bt->right);
         }
     }
二叉树同构判别:
int isomorphism(tree a,tree b){
    if ( !a && !b ) return 1;	//两个都空
    if ( (!a&&b) || (a&&!b) ) return 0;	//	一个空,一个不空
    if (a->data!=b->data) return 0;	//都不空,但数据不同
    if ( !a->left && !b->left ) return(a->right,b->right);	//左子树都空,判断右子树
    
    //左子树数据相同,判断右子树
    if ( (a->left&&b->left) && (a->left->data==b->left->data) )	
        return ( isomorphism(a->left,b->left) && isomorphism(a->right,b->right) );
    //左子树一个空,一个不空;或者左子树数据不同;交换左右子树,进行判断
    else
        return ( isomorphism(a->left,b->right) && isomorphism(a->rght,b->left) );
}
数据查找:

1. 静态查找:集合是固定的 ,没有插入和删除操作,只有查找

  • 顺序查找
struct table{
    int length;
    elementtype element[length+1];
};
int sequential_find(elementtype x,struct table* tb){
     /*在表tb[1]~tb[n]中查找关键字为x的数据元素*/ 
    int i;
    tb->element[0]=x;//建立哨兵
    for (i=tb->length;tb->element[i]!=x;i--);
    return i;
}
//顺序查找算法的时间复杂度为O(n)
  • 二分查找:(数据必须有序存放,比如从小到大)
struct table{
    int length;
    elementtype element[length+1];
};
int binary_find(elementtype x,struct table* tb){
    int left=0,mid,right=tb->length;//初始化左右边界
    while(left<=right){
        mid=(left+right)/2;//计算中间元素坐标
        if (tb->element[mid]<x) left=mid+1;//调整左边界
        else if (tb->element[mid]>x) right=mid-1;//调整右边界
        else return mid;//找到,返回x的坐标位置
    }
    return -1;//未找到
}
//注意在调整边界时,不能left=mid或者right=mid,这样做在某些情况下会让while成为死循环
//二分查找算法具有对数的时间复杂度O(logN)

2. 动态查找:集合是动态变化的 ,除查找,还可能发生插入和删除

二叉搜索树为例:

//结构如下:
typedef struct binary_tree* tree;
struct binary_tree{
    elementtype data;
    tree left;
    tree right;
};

二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:

  1. 非空左子树的所有键值小于其根结点的键值。
  2. 非空右子树的所有键值大于其根结点的键值。
  3. 左、右子树都是二叉搜索树。
  • 查找:(返回x节点,失败返回NULL)

    ------------------查找元素X的位置----------------
    //尾递归:
    tree find(elementtype x,tree bt) 
    { 
    if( !bt ) return NULL; /*查找失败*/ 
    if( x > bt->data )  
        return find(x,bt->right); /*在右子树中继续查找*/ 
    else if( x < bt->data )  
        return find(x,bt->left); /*在左子树中继续查找*/ 
    else 
        return bt; /*查找成功,返回结点的地址*/ 
    } 
    
    //由于非递归函数的执行效率高,下面将尾递归改为迭代:
    tree find(elementtype x,tree bt){
        while(bt){
            if (x>bt->data) bt=bt->right;	/*向右子树中移动,继续查找*/
            else if (x<bt->data) bt=bt->left;	/*向左子树中移动,继续查找*/
            else return bt;	/*查找成功,返回找到结点的地址*/
        }
        return NULL;	/*查找失败*/
    }
    
    ------------------查找最小元素的位置----------------
    tree find_min(tree bt){
        if (bt)
            while (bt->left) bt=bt->left;
        return bt;
    }
    
    ------------------查找最大元素的位置----------------
    tree find_max(tree bt){
        if (bt)
            while (bt->right) bt=bt->right;	//沿右分支继续查找,直到最右叶结点
        return bt;
    }
    
  • 插入:(返回结果树根节点)

    tree insert(elementtype x,tree bt){
        if (!bt){	/*若原树为空,生成并返回一个结点的二叉搜索树*/
            bt=(tree)malloc(sizeof(struct binary_tree));
            bt->data=x;
            bt->left=bt->right=NULL;
            return bt;
        }
        else{	/*开始找要插入元素的位置*/
            tree top=bt;
            int flag=0;	//判断在left插入还是right插入
            tree temp=NULL;	//记录父节点
            while (bt){
                temp=bt;
                if (x>bt->data){	//右子树寻找插入位置
                    bt=bt->right;
                    flag=1;
                }
                else if (x<bt->data){	//左子树寻找插入位置
                    bt=bt->left;
                    flag=0;
                }
                else return true;	//x已存在,直接返回插入成功
            }
            if (flag){	//flag==1,在temp的right下新建节点
                temp->right=(tree)malloc(sizeof(struct binary_tree));
                temp->right->data=x;
                temp->rigth->left=temp->right->right=NULL;
            }
            else{	//flag==0,在temp的left下新建节点
                temp->left=(tree)malloc(sizeof(struct binary_tree));
                temp->left->data=x;
                temp->left->left=temp->left->right=NULL;
            }
            return top;
        }
    }
    
  • 删除:(返回结果树根节点)

    //递归:
    tree delete(elementtype x,tree bt){
        if (!bt) reeturn NULL;	//空树
        else if (x<bt->data) bt->left=delete(x,bt->left);//左子树递归删除
        else if (x>bt->data) bt->right=delete(x,bt->right);//右子树递归删除 
        else{	//找到要删除的结点
            tree temp=NULL;
            if (bt->left && bt->right){	//待删除节点有左,右子树
                //在右子树中找最小的元素填充删除结点
                temp=find_min(bt->right);
                bt->data=temp->data;
                bt->right=delete(bt->data,bt->right);
            }
            else{	//待删除节点仅有左子树,或仅有右子树,或无子节点
                temp=bt;
                if (bt->left)	//有左节点
                    bt=bt->left;
                else	//有右节点,或无子节点
                    bt=bt->right;
                free(temp);
            }
        }
        return bt;
    }
    //非递归:(稍微复杂一些)
    tree delete(elementtype x,tree bt){
      if (!bt) reeturn NULL;	//空树
        
        tree top=bt;	//记录根节点
        tree parent=bt;	//记录父节点
        while(bt){	//寻找x位置
            if (x>bt->data){
                parent=bt;
                bt=bt->right;
            }
            else if (x<bt->data){
                parent=bt;
                bt=bt->left;
            }
            else break;	//找到,退出循环
        }
        if (!bt) return top;	//未找到
        
        if (bt->left && bt->right){	//待删除节点有左,右子树
            tree temp=bt->right;	//在右子树中找最小的元素填充删除结点
            while(temp->left){
                parent=temp;
                temp=temp->left;
            }
            bt->data=temp->data;
            if (temp==parent->right)//说明待删除节点的右子树为斜右二叉树(包括一个节点情况)
                parent->right=temp->right;
            else
                parent->left=temp->right;//待删除节点的右子节点有左子树
            free(temp);
            return top;
        }
        else{	//待删除节点仅有左子树,或仅有右子树,或无子节点
            tree temp=bt;
            if (parent==bt){	//删除根节点(包括最后一个节点情况)
                if (bt->left) bt=bt->left;
                else bt=bt->right;
                free(temp);
                return bt;
            }
            else{	//根节点不变
                if (bt==parent->left){
                    if (bt->left) parent->left=bt->left;
            		else parent-left=bt->right;
                }
                else{
                    if (bt->left) parent->right=bt->left;
            		else parent->right=bt->right;
                }
            	free(temp);
                return top;
            }
        }
    }
    
平衡二叉树:

平衡二叉树 (Balanced Binary Tree) (AVL树) :
空树,或者任一结点左、右子树高度差的绝对值不超过1

设 n h 高 度 为 h 的 平 衡 二 叉 树 的 最 少 结 点 数 。 结 点 数 最 少 时 : n h = n h − 1 + n h − 2 + 1 设n_h高度为h的平衡二叉树的最少结点数。结点数最少时: n_h = n_{h-1} + n_{h-2} + 1 nhhnh=nh1+nh2+1

typedef struct avl_tree* tree;
struct avl_tree{
    tree left;	//指向左子树
    tree right;	//指向右子树
    elementtype data;	//结点数据
    int height;	//树高
};

int max(int a, int b)
{
	return a > b ? a : b;
}

tree left_rotation(tree a){	//左单旋
/* 注意:a必须有一个左子结点b */
/* 将a与b做左单旋,更新a与b的高度,返回新的根结点b */
    tree b=a->left;
    a->left=b->right;
    b->right=a;
    a->height=max(a->left->height,a->right->height)+1;
    b->height=max(b->left->height,b->right->height)+1;
    return b;
}

tree right_rotation(tree a){	//右单旋
    /* 把left,right对换即可 */
    tree b=a->right;
    a->right=b->left;
    b->left=a;
    a->height=max(a->left->height,a->right->height)+1;
    b->height=max(b->left->height,b->right->height)+1;
    return b;
}

tree left_right_rotation(tree a){	//左-右双旋
/* 注意:a必须有一个左子结点b,且b必须有一个右子结点c */
/* 将a、b与c做两次单旋,返回新的根结点c */
    
    a->left=right_rotation(a->left);	//将B与C做右单旋,C被返回
    return left_rotation(a);	//将A与C做左单旋,C被返回
}

tree right_left_rotation(tree a){	//右-左双旋
    /* 同理,把left,right对换即可 */
    a->right=left_rotation(a->right);
    return right_rotation(a);
}

/* 如果不使用递归,将会使程序变得非常复杂,比如:
插入时需要判断在left还是right,插入后还需要判断是否仍是平衡二叉树,
而判断又需求树高,除此之外,还需要记录“麻烦节点”和“发现者”,因此下面采用递归实现:*/
tree insert(elementtype x,tree bt){
    if (!bt){	//若插入空树,则新建包含一个结点的树
        bt=(tree)malloc(sizeof(struct avl_tree));
        bt->height=0;
        bt->left=bt->right=NULL;
        bt->data=x;
        return bt;	//插入空树结束
    }
    else if (x < bt->data){
        bt->left=insert(x,bt->left);	//插入bt的左子树
        if (bt->left->height - bt->right->height == 2){	//如果需要左旋
            if (x < bt->left->data) bt=left_rotation(bt);	//左单旋
            else bt=bt->left_right_rotation(bt);	//左-右双旋
        }
    }
    else if (x > bt->data){	//插入bt的右子树
		bt->right=insert(bt->right);
		if (bt->right->height - bt->left->height == 2){	//如果需要右旋
            if (x > bt->right->data) bt=right_rotation(bt);	//右单旋
			else bt=right_left_rotation(T);	//右-左双旋
        }
	}
	//else x==bt->data,无须操作
    
	bt->height=max(bt->left,bt->right)+1;	//更新树高

	return bt;
    }
}
堆:

优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是 依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。

堆的两个特性 :

结构性:用数组表示的完全二叉树;

有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

“最大堆(MaxHeap)”,也称“大顶堆”:最大值

“最小堆(MinHeap)”,也称“小顶堆” :最小值

下面以最大堆(数组实现)为例,实现基本操作:

typedef struct priority_queue* heap;
struct priority_queue(int max){
    elementtype* data;
    int size;
    int capacity;
};
  • 创建:

    heap create_maxheap(int max){
        heap h=(heap)malloc(sizeof(struct priority_queue));
        h->data=(elementtype*)malloc(sizeof(elementtype)*(max+1));
        h->size=0;
        h->capacity=max;
    }
    
  • 插入:

    int is_full(heap h){	//判断堆是否满
        return (h->size==h->capacity);
    }
    bool insert(elementtype x,heap h){
        if (is_full(h)) return false;	//最大堆已满
        int i=++(h->size);	//取插入位置,当前容量+1
        while (i>1 && x>h->data[i/2]){	//未到达堆顶,且大于i的父亲节点
            h->data[i]=h->data[i/2];	//相当于给x腾出空间
            i/=2;
        }
        h->data[i]=x;	//插入x
        return true;
    }
    
  • 删除:

    int is_empty(heap h){	//判断堆是否空
        return (h->size==0);
    }
    elementtype delete_max(heap h){	//删除堆顶元素并返回
        if (is_empty(h)) return false;	//堆空
        elementtype top=h->data[0];	//记录堆顶元素,待return
        elementtype temp=h->data[(h->size)--];	//记录最后一个元素,当前容量-1
        int parent=1,child;
        for (; parent*2<=h->size; parent=child){
            child=2*parent;
            if ( (child!=h->size) && (h->data[child]<h->data[child+1]) )
                child++;	//找到最大子节点(其中第一个条件判断是否有右儿子)
            if (temp>=h->data[child]) break;	//让temp补位
            else h->data[parent]=h->data[child];	//给temp腾出位置
        }
        h->data[parent]=temp;	//temp插入
        return top;
    }
    
哈夫曼树与哈夫曼编码:

哈夫曼树的定义:​
带权路径长度 ( W P L ) (WPL) (WPL):设二叉树有n个叶子结点,每个叶子结点带有权值 w k w_k wk,从根结点到每个叶子结点的长度为 l k l_k lk,则每个叶子结点的带权路径长度之和就是: W P L = ∑ k = 1 n w k l k WPL=\sum_{k=1}^n w_kl_k WPL=k=1nwklk
最优二叉树或哈夫曼树: W P L WPL WPL最小的二叉树​

哈夫曼树的构造 :
每次把权值最小的两棵二叉树合并

typedef struct TreeNode *HuffmanTree;
struct TreeNode{
    int Weight;
    HuffmanTree Left, Right;
} 
HuffmanTree Huffman( MinHeap H ) {
    /* 假设H->Size个权值已经存在H->Elements[]->Weight里 */   
    int i;  
    HuffmanTree T; 
    BuildMinHeap(H); /*将H->Elements[]按权值调整为最小堆*/     
    for (i = 1; i < H->Size; i++) { /*做H->Size-1次合并*/         
        T = malloc( sizeof( struct TreeNode) ); /*建立新结点*/         
        T->Left = DeleteMin(H);  /*从最小堆中删除一个结点,作为新T的左子结点*/
        T->Right = DeleteMin(H); /*从最小堆中删除一个结点,作为新T的右子结点*/         
        T->Weight = T->Left->Weight+T->Right->Weight;/*计算新权值*/         
        Insert( H, T ); /*将新T插入最小堆*/   
    }   
    T = DeleteMin(H);   
    return T; 
}
//转自https://www.icourse163.org/learn/ZJU-93001?tid=1206471203#/learn/content?type=detail&id=1211167094&cid=1213729254
集合及运算:
typedef struct{
    elementtype data;
    int parent;
}gather;

int find(elementtype x,gather s[]){	//在数组s中查找值为x的元素所属的集合
    int i=0;
    while(i<maxsize && s[i].data!=x) i++;
    if (i>=maxsize) return -1;	//未找到
    while(s[i].parent>=0) i=s[i].parent;
    return i;	//找到x所属集合,返回树根结点在数组s中的下标 
}

int combination(elementtype x,elementtype y,gather s[]){
    int a=find(x,s);
    int b=find(y,s);
    if (a!=b){
        if (-s[a].parent >= -s[b].parent){//如果集合a比较大,集合b并入集合a
            s[a].parent+=s[b].parent;
            s[b].parent=a;
        }
        else{
            s[b].parent+=s[a].parent;//如果集合b比较大,集合a并入集合b
            s[a].parent=b;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值