二叉树不仅结构简单、节省内存,更重要是是这种结构有利于对数据的二分处理。之前我们提过,在二叉树的基础上能够派生很多经典的数据结构,也是下面我们将进行讨论的知识点:

(1)   提高数据查找效率的二叉排序树。

(2)   优先队列的最佳存储结构的二叉堆。

(3)   兼具二叉排序树和二叉堆性质的树堆。

(4)   用于算法分析的数据编码的哈夫曼树。

一.   二叉排序树

二叉排序树主要用于高效率查找。查找方法一般有三种:顺序查找、二分查找和二叉排序树查找。二叉排序树又可以分成多种类型。这里不同的查找方法,衡量他们的关键就是查找效率,往往越是复杂的构造二叉排序树,在查找数据的效率方面越优良。

具有以下性质的非空二叉树,称为二叉排序树:

1)     若根节点的左子树不空,则左子树的所有节点值均小于根节点值。

2)     若根节点的右子树不空,则右子树的所有节点值均不小于根节点值。

3)     根节点的左右子树也分别是二叉排序树。

容易看到,根据二叉排序树的构造机制,查找某个元素的效率就取决于所构造的二叉排序树的深度。深度越小,效率越高。二叉排序树有如下三种类型:

i)              普通二叉排序树:边输入边构造的二叉排序树,树的深度取决于输入的序列。

ii)             静态二叉排序树:按照二分查找的方法构造出的二叉排序树,近似丰满,深度约为logn。但是这种树需要离线构建,即输入数据后一次性建树,不方便动态维护。

iii)            平衡树:再插入和删除过程中一直保持左右子树的高度至多相差1的平衡条件,且能够保证树的深度是logn.

Ex1(poj2309):

给出一种无穷满二叉排序树的机制,其叶子节点是无穷的奇数序列:1、3、5……然后1、3的父节点是右儿子序号减左儿子序号.这样能够形成倒数第二层的节点,然后按照同样的机制形成上面那层节点。然后给出一个节点序号x,编程计算以该节点为根的二叉排序树的最小编号以及最大编号。

既然题目给出的是无穷满二叉排序树,我们就能够充分利用二叉排序树和满二叉树的性质。对于以x为根的子树,如果我们知道这棵树的层数k(层数从0开始计数),根据满二叉树的性质,该子树有2^(k+1) – 1个节点。x的左子树的节点编号都是小于x的,x的左子树也是一个满二叉树,节点数是2^k – 1,因此我们能看到,x的左子树的编号区间是[min , x - 1],这个区间含有的整数就是x的左子树的节点数,即有x-1-min+1=2^k – 1成立。即有min = x – 2^k +1.对称的,对于最大标号的节点,是完全一样的分析思路。则有max = x + 2^k – 1.

下面我们需要解决的问题就剩下如何计算根节点为x的树的层数。根据这棵无限二叉排序树的机制,我们容易看到规律,x除以2^k是奇数的时候,k便是层数。我们将整数x视为二进制形式a[n]a[n-1]…a[0],再从按权展开的形式去考察这个整数x,我们反复除以2,发现当遇到二进制形式右侧第一个1的时候,整数x变成了奇数。即我们有这样的结论,二进制数x右侧第一个1所在位置的权值2^k,k便是树的层数。

而这样的2^k我们可以通过位运算x&(-x)快速得到。

参考代码如下:

 #include<iostream>

using namespace std;

long long lowbit(long long x){

  return x & (-x);

}

int main(){

  long long n , x;

  cin >> n;

  for(int i = ;i < n;i++){

      cin >> x;

      cout << x - lowbit(x) +  <<' ' << x + lowbit(x)  - <<endl;

  }

}

二.二叉堆.

二叉堆是一棵满足下列性质的完全二叉树:如果某节点有孩子,则根节点的值都小于孩子节点的值,这样的二叉堆我们称之位小根堆。反之,如果根节点的值都大于孩子节点的值,我们称之为大根堆。

基于二叉堆的性质,我们非常好获得整个堆的最大元素和最小元素,因此二叉堆经常用于优先队列的存储结构。因为优先队列的删除操作正是删除一个线性序列中优先级最大或者最小的元素。

下面我们便开始讨论这样一个数据结构的各种操作。

1)     小根堆的插入操作:

假设我们原本有一个满足二叉堆性质的结构(在程序中我们用线性结构存储这样一个特殊的完全完全二叉树),当需要加入一个新的节点进入小根堆的时候,我们将其加入到小根堆的最后面,然后根据其节点序号(注意和节点权值不一样)来得到其父节点的序号,进而访问其权值,进行比较并进行交换,然后进行迭代操作一直向上走。

Int k = ++top;

Heap[k] = 被插入元素的权值.

While(k > ){//k=0到达根节点,是迭代的终结点.

int t = (k - )/;

if(heap(k )< heap(t)){ 

swap(heap[k] , heap[t])

k = t;

}

else  break;

}

2)     小根堆的删除操作

在堆中删除最小元素,即删除heap[0],然后将二叉堆中节点序号最大的节点移动到根节点,然后从根节点往下进行维护小根堆性质的操作。

   if(top){

         int temp = heap[];

         int k = ;

         heap[k] = heap[top--];

         while(( * k + ) <= top){

            int t = *k + ;

            if(t < top && heap[t + ] < heap[t]) t++;

              if(heap[k] > heap[t]){//当前节点的最小孩子比当前节点的权值小,需要交换

                 swap(heap[k] , heap[t]);

                 k = t;

              }

              else break;

         }

       }

       else output "empty heap"

ex1(zoj2724):

消息队列是操作系统的基础,现在给出两种类型的指令:

1)GET指令:获取消息队列中优先级最高的指令信息(包括指令名称和指令参数)。

2)PUT指令:往消息队列中添加指令,包括指令名称,指令参数和优先值。注意这里优先值越小优先级越大,二者相同时,较早进入消息队列的优先级高。

典型的优先队列的题目,这里我们在用二叉堆实现的时候,需要手写一个函数用于二叉堆元素权值的比较。输入的时候我们为每个节点顺序标号,节点信息我们存储在一个缓存区,而二叉堆用于存储节点序号。在插入删除的时候,我们利用存储的节点序号这一索引,在缓冲区找到对应节点的信息,进行比较。

参考代码如下:

#include<cstdio>

#include<cstring>

#include<algorithm>

using namespace std;

const int maxn =  + ;//60000条指令

const int maxs = ;

struct info{

   char name[maxs];

   int para;

   int pri , t;

}p[maxn];

int heap[maxn];

int top , used;

int compare(int a , int b){//ad额优先级高发返回-1 ,否则返回1

     if(p[a].pri < p[b].pri)  return -;

     if(p[a].pri > p[b].pri)  return  ;

     if(p[a].t   < p[b].t)    return -;                          //优先级相等,看时间戳

     if(p[a].t   > p[b].t)    return ;

}

int main(){

     used = ;

     top = ;//堆尾指针,指向堆数组最后一个元素的后面。

     int cnt = ;

     char s[maxs];

     while(scanf("%s" , s) != EOF){

         getchar();

          if(!strcmp(s , "GET")){//输入GET,进行删除操作

              if(top){

                printf("%s %d\n" , p[heap[]].name , p[heap[]].para);

                int k = ;

                heap[k] = heap[--top];

                while( * k  +  < top){//这里注意取值范围,top是指堆的尾部指针的后面那个一个,因此这里就不取等号

                    int t =  * k + ;

                    if(t < top && compare(heap[t + ] , heap[t] < )) t++;

                    if(t < top && compare(heap[t] , heap[k]) < ){

                        swap(heap[t] , heap[k]);

                        k = t;

                    }

                }

              }

               else printf("EMPTY QUEUE!\n");

           }

          else{

            scanf("%s %d %d" , p[cnt].name , &p[cnt].para , &p[cnt].pri);

            p[cnt].t = cnt;

            int k = top++;

            heap[k] = cnt++;//堆尾放入当前节点序号

            while(k > ){//第一个点不走这个

                  int t = (k - )/;

                  if(compare(heap[k] , heap[t]) < ){

                    swap(heap[k] , heap[t]);

                    k = t;

            }

            else break;

          }

        }

     }

       return ;

}

数据结构编程实验——chapter10-应用经典二叉树编程的更多相关文章

  1. 20145212 实验五《Java网络编程》

    20145212 实验五<Java网络编程> 一.实验内容 1.运行下载的TCP代码,结对进行,一人服务器,一人客户端: 2.利用加解密代码包,编译运行代码,一人加密,一人解密: 3.集成 ...

  2. 20145325张梓靖 实验五 "JAVA的网络编程"

    20145325张梓靖 实验五 "JAVA的网络编程" 实验内容 使用 JVAV语言 进行网络编程 对明文进行加密 设计过程 我完成的是客户端,服务端同伴 20145308刘昊阳 ...

  3. 20145210实验五《Java网络编程》

    20145210实验五<Java网络编程> 实验内容 1.运行下载的TCP代码,结对进行,一人服务器,一人客户端: 2.利用加解密代码包,编译运行代码,一人加密,一人解密: 3.集成代码, ...

  4. 20145237 实验五《Java网络编程》

    20145237 实验五<Java网络编程> 一.实验内容 •1.运行下载的TCP代码,结对进行,一人服务器,一人客户端: •2.利用加解密代码包,编译运行代码,一人加密,一人解密: •3 ...

  5. 20145221 《Java程序设计》实验报告五:网络编程及安全

    20145221 <Java程序设计>实验报告五:网络编程及安全 实验要求 掌握Socket程序的编写 运行TCP代码包,结对进行,一人服务器,一人客户端 掌握密码技术的使用 利用加解密代 ...

  6. 20155229——实验五《 Java网络编程及安全》

    20155229--实验五 Java网络编程及安全 实验内容 实验一: 两人一组结对编程: 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA ...

  7. 20145312 实验五 《Java网络编程》

    20145312 实验五<Java网络编程> 一. 实验内容及要求 实验内容: 运行下载的TCP代码,结对进行,一人服务器,一人客户端: 利用加解密代码包,编译运行代码,一人加密,一人解密 ...

  8. 20145312 实验五《Java网络编程》

    20145312 实验五<Java网络编程> 一. 实验内容及要求 实验内容: 运行下载的TCP代码,结对进行,一人服务器,一人客户端: 利用加解密代码包,编译运行代码,一人加密,一人解密 ...

  9. 20175316 盛茂淞 2018-2019-2 《Java程序设计》实验五 《网络安全与编程》 实验报告

    20175316 盛茂淞 2018-2019-2 <Java程序设计>实验五 <网络安全与编程> 实验报告 一.实验报告封面 课程:Java程序设计 班级:1753班 姓名:盛 ...

随机推荐

  1. 【CS231N】7、卷积神经网络

    一.疑问 1. assignments2 在代码文件FullyConnectedNets.ipynd 中,有代码如下: # Test the affine_forward function num_i ...

  2. PAT 1068 万绿丛中一点红

    https://pintia.cn/problem-sets/994805260223102976/problems/994805265579229184 对于计算机而言,颜色不过是像素点对应的一个 ...

  3. 工作中常用到的Linux命令

    ps: (ps的参数分成basic, list, output, thread, miscellaneous) (basic) -e / -A 显示所有进程 (output) -o 输出指定字段 ls ...

  4. APP接口

    <?phpClass Response{ /*** 返回json数据* @param $code 状态码* @param $message 描述信息* @param $data 数据* @par ...

  5. JS 字符串切割成数组

    var cheLin = "字*符*串" // console.log(cheLin) var array = cheLin.split("*");  arra ...

  6. behavior

    http://www.css88.com/book/css/properties/only-ie/behavior.htm 语法: behavior:<url> | url(#objID) ...

  7. CSU1392(NCPC2013)_Number Trick

    给一个小数X,找个A使得:AX=(A循环左移一位) 首先,假设A为一个满足题目条件的数,有n个数位,且最高位数字为A0. 那么可列出方程:AX=(A-A0*10n-1)*10+A0 ————>& ...

  8. 文件同步工具 lsyncd2.1.6 安装使用问题

    项目有文件实时同步备份的需求,做了一下调查,比较好的解决方法是使用lsyncd工具.这里主要记录一下遇到的问题及解决方法. lsyncd 的相关介绍和对比可见: lsyncd实时同步搭建指南——取代r ...

  9. P4101 [HEOI2014]人人尽说江南好

    题目描述 小 Z 是一个不折不扣的 ZRP(Zealot Round-game Player,回合制游戏狂热玩家),最近他 想起了小时候在江南玩过的一个游戏. 在过去,人们是要边玩游戏边填词的,比如这 ...

  10. 【bzoj4804】欧拉心算 解题报告

    [bzoj4804]欧拉心算 Description 给出一个数字\(N\),计算 \[\sum_{i=1}^n\sum_{j=1}^n \varphi(\gcd(i,j))\] Input 第一行为 ...