首先来说一下什么是左式堆:

A:左式堆是专门用来解优先队列合并的麻烦(任意二叉堆的合并都必须重新合并,O(N)的时间)。

  左式堆的性质:

  1.定义零路经长:节点从没有两个两个儿子节点的路经长,把NULL定义为-1

  2.堆性性质(x的键值比x左右两个儿子节点的键值要大或者要小)

  3.堆中的每一个节点x,左儿子的零路经长至少与右儿子的零路经长一样长。

  4.节点的距离等于右节点的距离+1.

  引理:

    若左式堆的距离定义为一定值,则节点数最少的左式堆是完全二叉堆。

  定理:

    若左式堆的距离为k,则这棵树最少有2^(k+1)-1个节点(由引理推出)

B:左式堆的特殊操作:(Merge)

  左式堆一般以结构体定义,结构体要有3个区域:键值区,零路经长区,左右节点区

  左式堆和二叉堆的其他操作都是类似的,但是其核心操作都是围绕Merge展开的。

  Merge例程:

  

 LEFTIST_NODE *Merge1(LEFTIST_NODE *const s1, LEFTIST_NODE *const s2)//以最小堆为例
 {
     if (s1->left == NULL) s1->left = s2;
     else
     {
         s1->right = Merge2(s1->right, s2);
         if (s1->left->zeroh < s1->right->zeroh)//如果违反零节点定理,我们交换节点
         {
             LEFTIST_NODE *tmp = s1->left;
             s1->left = s1->right; s1->right = tmp;
         }
         s1->zeroh = s1->right->zeroh + ;//因为这个时候右节点的零节点路经长总是小于左节点的,零点路径长总是取最小的
     }
     return s1;
 }

 LEFTIST_NODE *Merge2(LEFTIST_NODE *const s1, LEFTIST_NODE *const s2)
 {
     if (s1 == NULL)
         return s2;
     else if (s2 == NULL)
         return s1;
     else if (s1->elements < s2->elements)
         return Merge1(s1, s2);
     else//s1->elements < s2->elements
         return Merge1(s2, s1);
 }

  有了Merge,一切都变得很简单了

  Insert操作:把Insert的值单独列为一个新的节点,然后Merge即可。

  DeleteMin(Max)操作:把根节点的左右子堆合并,并且清除根节点。

C:左式堆的应用,数字序列(就是POJ 3666)

  POJ 3666那题,可以用左式堆来做。(这一题只用求不下降序列)

  那么怎么做呢?这一题可以这么思考:

  我们把泥土划分为一个一个区间:比如[q[i]-q[i+1]-1],[q[i+1],q[i+2]-2]....这样的话,每一个区间的最小价值,就是区间内的所有值改为该区间的中位数,每个区间的中位数是上升的,即可

  不过这样说有点先入为主了,我们想一下这样做为什么对。

  其实也用不着多严格的证明,我们可以这样看

      我们来看这样一个图,假设一个区间的数的分布就是这样的,其实这个价值点就是到任意蓝色轴的距离,那么这个区间所有点的最小值什么时候到最小?没错,就是到中点的时候。

  把这个结论推广到全序列,那就是保证当每个区间的中位数是递增的,然后最小值就是对应区间的数变成对应中位数所需要的价值之和。

  那么怎么编程呢?左式堆简直就是为这一题设的!

  我们可以弄一个这样的堆,堆的最大值是这个区间的中位数,也就是堆只管理区间的一半(当然要另外开一个区域记录区间的实际大小)。

  我们把每一个节点当做新的堆,如果序列是递增的,我们就把一个一个节点当做区间,并且不断压入栈,如果出现新入栈的节点的数值(也就是新的区间中位数,只有一个节点当然是节点的键值就是中位数了),那么我们就合并堆,直到合并到栈中的根的键值(中位数)是递增即可,同时,我们合并的时候,因为堆的信息只保留区间一半(包括中位数),也就是(len[k]+1)/2,如果出现新的堆和旧的堆合并,且(len[k]+1)/2+(len_new+1)/2>(len[k]+len_new+1)/2的时候(最多新的堆只会比要求大1),那么直接弹出堆的最大根,弹出后的堆根键值刚好就是符合条件的中位数。

  参考:http://blog.csdn.net/iaccepted/article/details/6748038

     http://m.blog.csdn.net/blog/u013595779/44004041

  代码:

  

 #include <iostream>
 #include <functional>
 #include <algorithm>
 #define NullNode -1
 #define MAX_N 2001

 using namespace std;

 typedef int Position;
 typedef struct _leftistheap
 {
     int value;
     Position left, right;
     int Npl;
 }Left_Heap;
 Position Merge1(Position, Position, Left_Heap *);
 Position Merge2(Position, Position, Left_Heap *);

 static int len[MAX_N];
 static int road[MAX_N];
 Position stack[MAX_N];//中点栈组
 Left_Heap Increase_Set[MAX_N];

 long long Search(const int, Left_Heap *);
 void Swap(Left_Heap *, Position);

 int main(void)//O(nlogn)处理3666
 {
     int n;
     long long ans1;
     while (~scanf("%d", &n))
     {
         memset(Increase_Set, -, sizeof(Increase_Set));
         ; i < n; i++)
         {
             scanf("%d", &road[i]);
             Increase_Set[i].value = road[i];
             Increase_Set[i].Npl = ;
         }
         ans1 = Search(n, Increase_Set);
         printf("%lld\n", ans1);
     }
     ;
 }

 Position Merge1(Position H1, Position H2, Left_Heap *Node_Set)
 {
     if (H1 == NullNode)
         return H2;
     else if (H2 == NullNode)
         return H1;
     else if (Node_Set[H1].value >= Node_Set[H2].value)//注意符号!!!
         return Merge2(H1, H2, Node_Set);
     else
         return Merge2(H2, H1, Node_Set);
 }

 Position Merge2(Position H1, Position H2, Left_Heap *Node_Set)
 {
     if (Node_Set[H1].left == NullNode)
         Node_Set[H1].left = H2;
     else
     {
         Node_Set[H1].right = Merge1(Node_Set[H1].right, H2, Node_Set);
         if (Node_Set[Node_Set[H1].left].Npl < Node_Set[Node_Set[H1].right].Npl)
             Swap(Node_Set, H1);
         Node_Set[H1].Npl = Node_Set[Node_Set[H1].right].Npl + ;//不能用pos2了,已经变了
     }
     return H1;
 }

 void Swap(Left_Heap *Node_Set, Position x)
 {
     Node_Set[x].left ^= Node_Set[x].right;
     Node_Set[x].right ^= Node_Set[x].left;
     Node_Set[x].left ^= Node_Set[x].right;
 }

 long long Search(const int n, Left_Heap *Node_Set)
 {
     memset(len, , sizeof(len));
     , sum_node_tmp, pos, k;
     ;

     ; i < n; i++)
     {
         sum_node_tmp = ; pos = i;
          && Node_Set[stack[top - ]].value > Node_Set[pos].value)
             //左式堆只储存左半树的信息,也就是以中位数为最大的最大堆
         {
             pos = Merge1(stack[top - ], pos, Node_Set);//合并成一棵新的堆,现在新的堆的堆头就是新的区间中位数
             ] + ) /  + (sum_node_tmp + ) /  > (len[top - ] + sum_node_tmp + ) / )
                 //如果比保留长度大(而且只会大1,则弹出最大节点)
                 pos = Merge1(Node_Set[pos].left, Node_Set[pos].right, Node_Set);
             sum_node_tmp += len[--top];
         }
         len[top] = sum_node_tmp;
         stack[top++] = pos;
     }

     , j = ; i < top; i++)
     {
         k = Node_Set[stack[i]].value;
         while (len[i]--)
             ans += abs(road[j++] - k);
     }
     return ans;
 }

  这是0(nlogn)的算法,优化程度立竿见影

Heap:左式堆的应用例(任意序列变单调性最小价值)的更多相关文章

  1. My集合框架第六弹 左式堆

    左式堆(Leftist Heaps)又称作最左堆.左倾堆.左式堆作为堆的一种,保留了堆的一些属性. 第1,左式堆仍然以二叉树的形式构建: 第2,左式堆的任意结点的值比其子树任意结点值均小(最小堆的特性 ...

  2. heap size eclipse 堆内存

    可以根据eclipse 或 myeclipse heapstats 使用情况调整堆内存大小,heap size 设置,-vmargs-Xms256-Xmx1024 ,其中Xms表示初始值,Xmx表示最 ...

  3. STL中heap算法(堆算法)

     ①push_heap算法 以下是push_heap算法的实现细节.该函数接收两个迭代器,用来表现一个heap底部容器(vector)的头尾,而且新元素已经插入究竟部的最尾端. template ...

  4. Heap(data structure)——堆(数据结构)(源自维基百科)

    源地址:http://en.wikipedia.org/wiki/Heap_%28data_structure%29 在计算机科学领域,堆是指一个特定的基于数结构的数据结构,其必须满足堆属性: 如果A ...

  5. 黄源河《左偏树的应用》——数字序列(Baltic 2004)

    这道题哪里都找不到. [问题描述] 给定一个整数序列a1, a2, … , an,求一个不下降序列b1 ≤ b2 ≤ … ≤ bn,使得数列{ai}和{bi}的各项之差的绝对值之和 |a1 - b1| ...

  6. 用C++的类做三种优先队列的实现

    学过数据结构的都知道优先队列这种东西,普通的队列是依据入队顺序,先入队的先出队,而优先队列则是依照键值,键值越大(或越小),就越先出队. 所以,优先队列基本支持push,pop,empty,size, ...

  7. DP:Making the Grade(POJ 3666)

     聪明的修路方案 题目大意:就是农夫要修一条路,现在要求这条路要么就是上升的,要么就是下降的,总代价为∑|a[i]-b[i]|,求代价最低的修路方案, (0 ≤ β≤ 1,000,000,000) , ...

  8. 采用TL026等构成的宽带ALC放大器电路图

    Building a Differential Amplifier An op-amp with no feedback is already a differential amplifier, am ...

  9. java中堆栈(stack)和堆(heap)(还在问静态变量放哪里,局部变量放哪里,静态区在哪里.....进来)

    (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编 译时就可以给 ...

随机推荐

  1. 端盘子的服务生到月薪一万五的IT精英,你能相信吗

    一直以来,我都觉得自己不是一个有故事的人. 以前的我,是个乖宝宝,对父母言听计从,特别内向,甚至一度感觉到自卑.不上学之后,我干过送货员,去工地除泥搬砖,当过油漆工,去过工厂,还去饭店当过端盘子的服务 ...

  2. .NET Core性能测试组件BenchmarkDotNet 支持.NET Framework Mono

    .NET Core 超强性能测试组件BenchmarkDotNet 支持Full .NET Framework, .NET Core (RTM), Mono. BenchmarkDotNet支持 C# ...

  3. kettle中参数和变量的区别

    图一: 图二: 何时使用'?'何事使用${}应当根据情况: 在图二中使用的是${}因为此时没有"作为参数的字段",所以只能用el表达式直接获取其值,在图一中有"作为参数的 ...

  4. unity3d UGUI多语言

    从Foundation插件中抽离出的多语言.原理很简单,给Text绑定key,在程序开始时设置本地语言即可. 目录结构: LanguageEditor.cs:自定义编辑器: LanguageServi ...

  5. java-JDBC登录注册代码

    登录: public static void main(String[] args) throws Exception{ Scanner sc = new Scanner(System.in); Sy ...

  6. 设计一个程序,程序中有三个类,Triangle,Lader,Circle。

    //此程序写出三个类,triangle,lader,circle:其中triangle类具有类型为double的a,b,c边以及周长,面积属性, //具有周长,面积以及修改三边的功能,还有判断能否构成 ...

  7. AIX用chsec命令修改快捷修改配置文件

    前言 AIX的所有配置设置通过一个命令来进行更改配置文件中的键-值对,以达到修改配置的目的.如:group/user/limits/passwd等等 命令格式 chsec [-f file] [-s ...

  8. 解决SQL server 2014 修改表中的字段,无法保存的问题。

    修改PROJECT表中的字段,保存时,弹出上面的窗体,无法保存. 解决方法为:[工具]->[选项]->[设计器]中,去掉“阻止保存要求重新创建表的更改”前的勾选.

  9. Windows魔法堂:解决“由于启动计算机时出现页面文件配置问题.......”

    一.前言 昨晚终于在VirtualBox中安装好Win7了,但在系统启动后弹出窗报“由于启动计算机时出现页面文件配置问题.......”,于是度娘一下.以下记录以供日后查阅. 二.原因 网上说的是在使 ...

  10. UESTC 878 温泉旅馆 --性质+枚举

    设FA为A的牌中数字异或和,FB为B的. 则有性质: ans = (所有的(A&B=0)个数 + (FA=FB且A&B=0)的个数)/2.即所有的FA>FB的个数(除2是因为这里 ...