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

(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. HDU 2262 Where is the canteen 期望dp+高斯消元

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2262 Where is the canteen Time Limit: 10000/5000 MS ...

  2. 关于mybatis的思考(1)——mybatis的使用实例

    架构分析 MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架. MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索.MyBatis 可以使用简 ...

  3. 复利计算测试(C语言)

    对我们和复利计算程序,写单元测试. 有哪些场景? 期待的返回值 写测试程序. 运行测试. 测试模块 测试输入 预期结果 运行结果 bug跟踪 计算终值 (本金,年限,利率) 终值     1 (100 ...

  4. 『编程题全队』Alpha 阶段冲刺博客Day2

    1.每日站立式会议 1.会议照片 2.昨天已完成的工作统计 孙志威:   确定了本阶段的团队目标   确定了个人所分配的任务(主要为客户端GUI模块) 孙慧君:    确定了自己的任务,并着手开始环境 ...

  5. 【算法】—— 1到n中减少了一个数,顺序被打乱,找出缺失的数

    问题 有0-n这n+1个数,但是其中丢了一个数,请问如何找出丢了哪个数? 五种方法 1)用1+2+...+n减去当前输入数据的总和.时间复杂度:O(n) 空间复杂度:O(1) [容易溢出] 2)用12 ...

  6. 《编写高质量代码改善JavaScript程序的188个建议》读书笔记

    逗号运算符比较怪异.如    var a =(1,2,3,4);alert(a);// 4      var a = 1,2,3,4;//报错 注意a++和++a的差别,变量在参与运算中不断地变化.v ...

  7. P2605 [ZJOI2010]基站选址

    题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范 ...

  8. LINQ 模糊搜索

    IList<entity> ls = new List<entity>(); ls = (from k in ls where k.Name.Contains("sa ...

  9. 【刷题】BZOJ 1070 [SCOI2007]修车

    Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序,使 ...

  10. BZOJ 3143 游走 | 数学期望 高斯消元

    啊 我永远喜欢期望题 BZOJ 3143 游走 题意 有一个n个点m条边的无向联通图,每条边按1~m编号,从1号点出发,每次随机选择与当前点相连的一条边,走到这条边的另一个端点,一旦走到n号节点就停下 ...