电梯算法题

电梯算法题:

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

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;
    }
}

猜你喜欢

转载自blog.csdn.net/lchpersonal521/article/details/52351121