Swift 算法实现之数组中出现次数超过一半的数字

596 阅读3分钟

一、概述

这也是剑指 Offer 上的一道很经典的题,对于这道题,我们得分两种情况来讨论:有序和无序。如果数组是有序的,那就非常简单了,数组中间的那个数字一定是我们要求的那个出现次数超过一半的数字。如果数组是无序的,当然可以先将数组进行排序,然后去求中间的那位数字,除此之外是不是还有更简单的方法呢?

题目描述:

一个无序数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字

 

二、实现思路及代码

对于此题可能有多种解法。

解法1:

先将数组进行排序,排过序之后求数组中间的那个数字,该数字就是那个出现次数超过一半的数字。

 

解法2:

借助 hash 表实现,hash 表的键值(Key)为数组中的数字,值(Value)为该数字对应的次数。然后直接遍历整个 hash 表 ,找出每一个数字在对应的位置处出现的次数,输出那个出现次数超过一半的数字即可。

 

解法3:

遍历数组,比较相邻的两个数字,如果这两个数字不相等则同时删除这两个数字(不管是不是我们要查找的那个出现次数超过一半的数字),如果相等则保留这两个数字,从这两个数字中的后一个数字继续往后遍历,最后剩余的数字就是那个出现次数超过一半的数字。

例如下面数组:

[1, 1, 2, 3, 1, 1, 4]

其变化为:

[1, 1, 2, 3, 1, 1, 4] => [1, 1, 2, 3, 1, 1, 4] => [1, 3, 1, 1, 4] => [1, 1, 4] => [1]

则 1 就是那个出现次数超过一半的数字。

 

解法4:

在遍历数组的时候保存两个值:num(用来保存数组中遍历到的某个数字)和count(用来表示当前数字的出现次数),count初始化为1。当我们遍历到数组中下一个数字的时候:

  • 如果下一个数字与之前num保存的数字相同,则count加1;
  • 如果下一个数字与之前num保存的数字不同,则count减1;
  • 每当出现次数count变为0后,用num保存下一个数字,并把count重新设为1。 直到遍历完数组中的所有数字为止。

例如下面数组:

data:[Int] = [1, 1, 2, 1]

(1)开始时num = data[0] = 1,count = 1

(2)遍历到data[1],由于data[1] = num = 1,所以此时num = 1,count = count + 1 = 2

(3)遍历到data[2],由于data[2] = 2 ≠ num,所以此时count = count – 1 = 1,由于count ≠ 0,故此时num不变,即num = 1

(4)遍历到data[3],由于data[3] = num =1,所以此时count = count + 1 = 2,num = 1。遍历结束!

故出现次数超过一半的数字为num= 1

 

比较上面四种解法,解法1需要排序,如果使用效率较高的快速排序的话时间复杂度为O(n·logn)。解法2需要借助 Hash 表,不仅需要O(n)的空间开销还需要设计 hash 函数(当然也可以直接使用 Swift 中的字典)。可以明显发现解法3和解法4要比前两种解法更好一点。解法3和解法4差别不大,基本思想都是类似的,所以接下来使用 Swift 去实现解法4的算法。

上面已经给出了解法4的原理和思想,这里就直接上代码了:

//求数组中出现次数超过一半的数字
func moreHalfNumber(data:[Int]) -> Int {
    var num = 0
    var count = 0
    for i in 0..<data.count {
        if count == 0 {
            num = data[i]
            count = 1
            break
        }
        if num == data[i] {
            count += 1
        } else {
            count -= 1
        }
    }
    return num
}

 

原创文章,转载请注明: 转载自李峰峰博客

本文链接地址: Swift算法实现之数组中出现次数超过一半的数字