twitter面试题之装水问题

据说这是twitter的一个面试题,不过,去年找工作的时候我的一个同学在面试微软的时候也有问到这个问题。很有意思的一个题目,当时我也没有去深究到底怎么实现比较好,前几天在伯乐在线上面看到了这篇文章面试题分析:我的Twitter技术面试失败了, 才知道这个解法原来很简单,只是刚开始没怎么看懂,比较绕。好吧,废话少说,我还是根据自己的理解来说下这个问题,不对之处,还请大家指正。


题目描述:

看下面这个图片,在这个图片里我们有不同高度的墙。这个图片由一个整数数组所代表,数组中每个数是墙的高度。上边的图可以表示为数组[2,5,1,2,3,4,7,7,6],假如开始下雨了,那么墙之间的水坑能够装多少水呢?”

以1×1的方块为单位计算容积。所以,在上边的图中下标为1以左的都会漏掉。下标7以右的也会漏掉。剩下的只有在1和6之间的一坑水,容积是10。如下图所示。

分析:

原文作者最初想法是用极大值考虑,也就是说找到下标2的左右两个极大值,但是这个做法最后作者意识到是错误的。比如下面的情况就不对:

看看这个输入:

如果答案计算的是极大值之间的水,就像这样。

但是答案应该是在两个高塔之间只有一池水:

那其实正确的解法是作者后来灵光一闪想到的,确实看起来很简单。这其实要基于一个事实那就是,你从左往右扫描数组的时候,只要是左边最大值小于右边的数字时,这中间是一定可以存储水的。每次维护一个左墙的最大值,从左向右的扫描过程中遇到大于左边最大值的值,则更新左边最大值,并且不增加存储水的容量,因为在从左往右扫描的过程中,如果遇到的值比左边大,那么存储不了水,如上面例子中从2开始扫描,遇到5的时候,此时存储容量还是0,因为5大于2,这中间存储不了水。从右往左扫描也是一样。
一个基本的解法就是先找到数组最大值,然后从左往右扫描到最大值,计算出水的容量;然后从右往左扫描到最大值,计算出水的容量,最后总的容量就是两部分之和。
描述还是会不怎么清楚,还是以例子来看。以第一个例子[2,5,1,2,3,4,7,7,6]来看,最大值为7,两个7随便选后面一个好了。那么从左往右扫描过程如下:
1)首先设置左边最大值为2,容量设置为0。
2)2小于7,往右继续扫描,遇到5,因为5大于2,所以更新左边最大值为5,此时并不更新存储水的容量,接着往后扫描。
3)扫描到1,因为1比前面的5小,因此现在至少可以存储5-1=4单位水。存储水的容量加4。因为我们知道右边有比左边最大值还大的值,所以肯定是可以至少存储这么多水的。
4)接着扫描到2,还是比5小,存储水的容量增加5-2=3,容量变为4+3=7.
5)继续扫描到3,还是比5小,存储水的容量增加5-3=2,容量变为7+2=9.
6)继续扫描到4,还是比5小,存储水的容量增加5-4=1,容量变为9+1=10.
可以得到左侧存储水的容量为10。同理,从右往左扫描到7,容量为0,因为6小于7,7以右的水全部会漏掉。

      那么一个更优的解法其实是只需要扫描一遍就OK,不需要之前扫描数组找最大值,也就是整合了左右扫描的过程。如果左边值小于右边值,则从左往右扫描,否则从右往左扫描。(附python代码在后面)

代码:

def calculate(testcase):
    p_l = 0
    p_r = len(testcase) - 1
    max_l = testcase[p_l]
    max_r = testcase[p_r]
 
    volume = 0
    while p_r > p_l :
        if max_l < max_r:
            p_l = p_l + 1
            if testcase[p_l] >= max_l:
                max_l = testcase[p_l]
            else:
                volume = volume + (max_l - testcase[p_l])
        else:
            p_r = p_r - 1
            if testcase[p_r] >= max_r:
                max_r = testcase[p_r]
            else:
                volume = volume + (max_r - testcase[p_r])
    return volume

参考资料


    

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一些常见的 Flink 面试题以及答案: 1. 什么是 Flink? Flink 是一个分布式数据流处理框架,它可以在批处理和流处理模式下运行,并提供了高效的状态管理和窗口操作,以及支持 SQL 和 Table API 等高级特性。 2. Flink 的优势是什么? Flink 具有以下优势: - Flink 提供了低延迟、高吞吐量的流处理能力,同时也支持批处理模式; - Flink 提供了高效的状态管理和窗口操作,可以轻松地进行复杂的数据处理; - Flink 提供了 SQL 和 Table API 等高级特性,方便用户进行数据分析和处理; - Flink 支持多种部署模式,可以在本地、集群和云环境中运行。 3. Flink 的核心概念是什么? Flink 的核心概念包括: - 数据流(DataStream):代表无限的数据流,可以进行实时处理; - 数据集(DataSet):代表有限的数据集合,可以进行批处理; - 窗口(Window):用于将无限的数据流切分成有限的数据块,以便进行有限的处理; - 状态(State):用于保存中间计算结果; - 迭代(Iteration):用于迭代计算,例如图计算等; - 触发器(Trigger):用于触发窗口计算; - 并行度(Parallelism):代表任务并行执行的程度。 4. Flink 如何保证数据的 Exactly-Once 语义? Flink 通过使用 Checkpoints 和 Savepoints 两种机制来保证数据的 Exactly-Once 语义。Checkpoints 机制通过定期将状态和数据写入外部存储来实现,以便在发生故障时能够恢复到最近一次的 Checkpoint。Savepoints 机制则允许用户手动创建一个保存点,并将状态和数据写入外部存储,以便在需要时能够回滚到该保存点。 5. Flink 支持哪些数据源和数据接收器? Flink 支持从 Kafka、Hadoop、HDFS、Kinesis、RabbitMQ、Twitter、Socket 等数据源读取数据,并支持将数据输出到 Kafka、Hadoop、HDFS、Elasticsearch、JDBC、Socket 等数据接收器。 6. Flink 的 SQL 和 Table API 如何使用? Flink 的 SQL 和 Table API 可以通过将数据流或数据集转换为 Table,并使用 SQL 或 Table API 进行查询和处理。例如: ```java StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env); DataStream<Tuple2<String, Integer>> dataStream = env.fromElements( new Tuple2<>("a", 1), new Tuple2<>("b", 2), new Tuple2<>("a", 3) ); Table table = tableEnv.fromDataStream(dataStream, "word, count"); Table resultTable = table.groupBy("word").select("word, count.sum as total"); DataStream<Tuple2<Boolean, Row>> resultStream = tableEnv.toRetractStream(resultTable, Row.class); resultStream.print(); env.execute(); ``` 7. Flink 的窗口操作有哪些类型? Flink 的窗口操作包括: - 滚动窗口(Tumbling Window):将数据流按固定大小的窗口切分,不重叠; - 滑动窗口(Sliding Window):将数据流按固定大小的窗口切分,可以重叠; - 会话窗口(Session Window):将数据流按一定时间间隔内的活动时间切分,可以动态调整窗口大小。 8. Flink 如何处理数据倾斜问题? Flink 可以通过以下方式处理数据倾斜问题: - 增大并行度:将任务并行度增加到足够大,可以让数据均匀分布到不同的任务中; - 重分区:通过对数据进行重分区,将数据均匀分布到不同的分区中; - 随机键:对于数据倾斜的键,可以使用一些随机的键来将数据分散到不同的分区中; - 本地聚合:对于数据量较小的键,可以在每个 Task 内部进行本地聚合,以减少数据传输。 9. Flink 如何进行流的 Join 操作? Flink 支持多种流的 Join 操作,包括: - Inner Join:将两个流中的相同键进行 Join 操作; - Left Join:将左侧流中的所有数据与右侧流中的相同键进行 Join 操作; - Right Join:将右侧流中的所有数据与左侧流中的相同键进行 Join 操作; - Full Join:将左侧流和右侧流中的所有数据进行 Join 操作。 10. Flink 如何进行流的 Split 和 Select 操作? Flink 的 Split 和 Select 操作可以将一个数据流拆分成多个数据流,并对每个数据流应用不同的操作。例如: ```java SplitStream<Tuple2<String, Integer>> splitStream = dataStream .split((OutputSelector<Tuple2<String, Integer>>) value -> { List<String> output = new ArrayList<>(); if (value.f1 % 2 == 0) { output.add("even"); } else { output.add("odd"); } return output; }); DataStream<Tuple2<String, Integer>> evenStream = splitStream.select("even"); DataStream<Tuple2<String, Integer>> oddStream = splitStream.select("odd"); ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值