电梯算法题:
高志大厦因等电梯人数太多,现规定电梯上升只能停一层,大家下电梯再各个步行到自己楼层,求停哪一层所有人步行层数总和最少。
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;
}
}