题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
- 遍历数组,用字典统计数字的频率,一旦某个数字的频数大于数组长度的一半,则返回该数。
- 如果遍历数组过程中没有返回,则说明没有数字频数大于数组长度的一半,返回0。
1 | class Solution: |
用字典的时间效率是O(n),但空间效率不占有,而空间效率是O(1)的算法是存在的。
- 遍历数组时保存两个值,一个是数组中的一个数字,一个是次数。
- 每遍历到一个数字,如果该数字与所保存值相等,次数加1;不相等则次数减去1;如果次数已经为0且不相等,则将保存值更新为该数字,次数置为1。
- 如果存在出现次数超过一半的数字,则必定是最后的保存值。由于是必要条件,所以程序中要确认该值是否真的出现了超过一半的次数,即至少为
(len(numbers)>>1)+1
。
分析原理:数组长度为N,最后的次数值是M,则M<=N,由于次数值一直都是非负数,那么可知有2*(N-M)
个数在加减运算中两两抵消掉了。而出现一半的数字肯定不会全被抵消掉。
1 | class Solution: |
partition函数法。
- 快排的partition函数能够以某个数为基准,将数组内元素按大小分为小于该数和大于等于该数两部分。可以用来将数组分成前k个较小值和其余的值两部分,方法是迭代,将partition函数的输入区间向k和拢。
- 而出现次数超过一半的数,排序之后肯定会在下标
len(numbers)>>1
上出现,因此用partition函数找到该数,然后检查其是否满足条件即可。 - 每次迭代使得partition的输出更接近
len(numbers)>>1
这个下标,直到二者相等。如果有出现次数超过一半的数,则必然是该下标的元素。究竟是不是,也要检查一番才能确定。 - 注意:虽然partition的输出下标左侧的元素肯定要小于该下标所指的元素,但最终
len(numbers)>>1
这个下标左侧的元素却是小于等于该下标所指元素。只是由while True
循环里对s
和t
的更新方式导致的。
1 | class Solution: |