电梯算法题

来源:互联网 发布:宾得k3 知乎 编辑:程序博客网 时间:2024/04/30 04:02

电梯算法题:

  高志大厦因等电梯人数太多,现规定电梯上升只能停一层,大家下电梯再各个步行到自己楼层,求停哪一层所有人步行层数总和最少。

input:    int[] floorPersonCount = [ 0, 0, 2, 5, 3, 0 ];   //各楼层工作的人数统计数组ouput:    int bestFloor;            //求出停留在哪一层,大家走楼梯总数最少

解析

  这道题目是我们公司内部有人放到wiki上面去的,看到了很多解答,但是都不怎么好理解,现在将我的想法写出来大家参考下。其实此问题可以转化成树的平衡问题,具体做法看以下示例:
假设电梯只停1至N层,N>1,且第一层的人不需要坐电梯。再假设高志大厦只有5层,每层人数:[0 , 1 , 2 , 3 , 4]; 那么初始状态(电梯在第一层)时可以用下图表示(可以理解为只有左子树的二叉树):
    这里写图片描述

  其中根节点表示电梯停在的楼层,结点的深度表示走到目的楼层所要步行的楼层数,结点的值表示人数。所以这时假如电梯只停在楼层1,那么需要走的楼层数总和:

    0*0 + 1*1 + 2*2 + 3*3 +4*4 = 30

此时我们做一些定义:
val表示结点的值,deep表示结点的深度,结点的权重定义为val*deep,子树的权重定义为子树中所有非根子结点权重之和。
那么对于上图:

    左子树权重:1*1 + 2*2 + 3*3 +4*4 = 30    右子树权重:0    权重差:30

此时我们对上图进行右旋转(切换电梯停止的楼层):
    这里写图片描述
那么:

    左子树权重:2*1 + 3*2 +4*3 = 20    右子树权重: 0    权重差:20

再次旋转:

    这里写图片描述

    左子树权重:3*1+4*2 = 11    右子树权重:1    权重差:10

    这里写图片描述

    左子树权重:4    右子树权重:2*1+1*2 = 4    权重差:0

  仔细观察会发现,左子树权重+右子树权重=需要走的总楼层数。在旋转的过程中,左子树权重在逐渐变小,右子树的权重在逐渐增大,我们要找的其实是这颗树的最佳平衡点,即左子树权重和右子树权重差的绝对值最小的时候,这棵树会达到最佳平衡状态。所以思路很明显就出来了。对于此实例显然电梯停止在第4层这棵树达到了最佳平衡状态(此时左右子树权重差最小,为0)。此时需要走的总楼层数为 4+4 = 8。

代码

package com.test.elevator.fast;/** * 基于数组的电梯算法 *  * <p> * Created by chengli on 2016/11/7. */public class NewElevator {    private static int[] a = {0, 1, 2, 3, 4};    //private static int[] a = {4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 8};    public static void main(String[] args) {        a[0] = 0; /*第一层的人不需要坐电梯*/        int maxRotateNum = a.length;        int[] result = new int[maxRotateNum];        for (int i = 0; i < maxRotateNum; i++) {            int leftW = leftWeight(i + 1);            int rightW = rightWeight(i + 1);            result[i] = Math.abs(leftW - rightW);        }        //TODO 找出result数组中值最小的元素,下标i+1即为最佳停止楼层数,。实际上这里可以做很多优化        //以下只做简单输出,权重最小的即为电梯最佳停放楼层。注意最佳楼层可能不止一个。        for (int i = 0; i < result.length; i++) {            System.out.println(String.format("楼层: %s , 权重差: %s", i + 1, result[i]));        }    }    /**     * @param rootIndex 电梯停止层数     * @return 左子树权重     * 这里每次循环都要重新计算一次,实际上可以做一些优化以提升效率,具体实现这里就不列出了。     */    private static int leftWeight(int rootIndex) {        int weight = 0;        for (int i = 0; i < rootIndex - 1; i++) {            weight += a[i] * (rootIndex - i - 1);        }        return weight;    }    /**     * 计算右子树权重,原理同上     */    private static int rightWeight(int rootIndex) {        int weight = 0;        for (int i = rootIndex; i < a.length; i++) {            weight += a[i] * (i - rootIndex + 1);        }        return weight;    }}
1 0
原创粉丝点击