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

(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. 【搜索】POJ-3187 枚举全排列

    一.题目 Description FJ and his cows enjoy playing a mental game. They write down the numbers from 1 to ...

  2. json反序列化对象

    这个是同事研究的wcf中中根据type类型反序列化json的示例 /// <summary> /// json转对象 /// </summary> /// <param ...

  3. DPDK flow_filtering 源码阅读

    代码部分 main.c /*- * BSD LICENSE * * Copyright 2017 Mellanox. * * Redistribution and use in source and ...

  4. 团队作业7——第二次项目冲刺(Beta版本12.07——12.08)

    1.当天站立式会议照片 本次会议在5号公寓3楼召开,本次会议内容:①:熟悉每个人想做的模块.②:根据项目要求还没做的完成. 2.每个人的工作 经过会议讨论后确定了每个人的分工 组员 任务 陈福鹏 实现 ...

  5. Anaconda 下libsvm的安装

    方法一. 利用VS生成动态库的安装    详细可参考这篇博文进行操作:https://blog.csdn.net/jeryjeryjery/article/details/72628255 方法二. ...

  6. PAT 甲级 1064 Complete Binary Search Tree

    https://pintia.cn/problem-sets/994805342720868352/problems/994805407749357568 A Binary Search Tree ( ...

  7. 初期测评 E 迷障

    https://vjudge.net/contest/240302#problem/E 通过悬崖的yifenfei,又面临着幽谷的考验—— 幽谷周围瘴气弥漫,静的可怕,隐约可见地上堆满了骷髅.由于此处 ...

  8. PHP学习心得2

    对于PHP的语法结构,刚开始真的很不习惯,真搞不懂为什么每个变量之前都要加个“$”符号,每个语句写完之后都必须加上“分号”来表示此句已经结束,还有,PHP对字母的大小写是敏感的,写的时候一定要注意大小 ...

  9. 看懂Qt源代码-Qt源码的对象数据存储

    第一次看Qt源代码的人都会被其代码所迷惑,经常会看到代码中的d_ptr成员.d_func(函数)和Q_DECLARE_PRIVATE等奇怪的宏,总是让人一头雾水,下面这篇文章转自http://www. ...

  10. spring远程服务知识梳理

    序:本文主要是总结和归纳spring的远程服务相关知识,可作为入门学习笔记.写博客目的也是为了进行知识梳理,便于以后查看.本文主要参考资料 spring 实战第三版 本文主要讨论内容如下: 远程调度概 ...