一种高效的可变行高列表行定位算法
列表控件是数据显示时使用的一种常用的控件。
刚发现有网友把这个算法说得更清楚,推荐大家去它的博客看:https://www.jianshu.com/p/76827322f33f
下面是我提供的原始的版本 :)
一般情况下列表中的行是等高的,这种情况下无论列表包含多少行,都能够在O(1)的时间定位到指定行。
但是当显示的内容格式不一致时,使用相等的行高可能就意味着显示空间的浪费,也意味说用户需要更多的滚动操作,影响用户体验。
要实现一个支持可变行高的列表控件,首先要解决的问题就是快速定位列表行。
假定一个列表中的表项按照下面的高度排列:
1,2,3,1,2,3,1,2,3,1,2,3,4,5
可以知道总高度为:33
程序员需要解决从一个随机的[0,32]的值(x)定位到哪一行的问题。
当然最简单的办法就是从第一行开始逐行的数,直到数到的那一行正好包含x,可以知道这个算法的时间复杂度为O(n)。
当n很大时,这个算法基本上不可行。
一行一行的数显然是很浪费时间的,解决的办法就是一段一段的数。
要实现分段数,一个前提就是我们需要为这些数据提前建立好索引表。对于上面序列假定以3个元素一组为单位建立索引表就可以获得:(6),(6),(6),(6),(9),如此一来,要定位一行,我们最多需要数5+3次就能找到一行。
对于数据量比较少的情况,可能上述分组方法就能解决问题了。
但是对于数据量更大的情况如何处理呢?方法很简单,那就是分组再分组,直到最后所有的分组数据形成一棵索引树。树上每一个结点代表该结点下所有子节点的高度和。
通过构造索引树,无论多少数据量,都可以在O(log(n))的时间定位到任意行。
另外,对于大量数据,我们可能在初始化的时候并不知道总数有多少,而是在显示到哪一行时再通过计算获得。
对于这种情况,我们需要动态更新索引树。更新过程也很简单,当一行更新高度时,只需要找到该行所在的叶节点,更新叶节点高度,再逐级更新父节点即可,时间复杂度同样是O(log(n))。
具体实现可以参考SOUI的ListView中用于可变行高支持的类:SListViewItemLocatorFlex