剑指offer-链表&数组

1.圆圈中最后剩下的数

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
 
如果没有小朋友,请返回-1
解:
用数组模拟循环链表即可。
 1 class Solution:
 2     def LastRemaining_Solution(self, n, m):
 3         # write code here
 4         if not n:
 5             return -1
 6         cycle = list(range(n))
 7         start = 0
 8         while len(cycle) > 1:
 9             toremove = (start + m - 1) % len(cycle)
10             cycle.pop(toremove)
11             start = toremove
12         return cycle[0]
View Code

2. 数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

解:

用 hashset 做。

 1 class Solution:
 2     # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
 3     # 函数返回True/False
 4     def duplicate(self, numbers, duplication):
 5         # write code here
 6         if not numbers:
 7             return False
 8         hashset = set()
 9         for num in numbers:
10             if num in hashset:
11                 duplication[0] = num
12                 return True
13             hashset.add(num)
14         return False
View Code

不用辅助空间,比较巧妙。因为要返回的是任意一个重复的数字,把当前序列当成是一个下标和下标对应值是相同的数组;

遍历数组,判断当前位 i 的值和下标是否相等:

  1. 若相等,则遍历下一位; 

  2. 若不等,则将当前位置i上的元素和a[i]位置上的元素比较:若它们相等,则成功!若不等,则将它们两交换。换完之后a[i]位置上的值和它的下标是对应的,但i位置上的元素和下标并不一定对应;重复2.的操作直到当前位置i的值也为i

  3. 将i向后移一位,再重复2.

注意,这样找到的并不是一定是第一个重复的数字

 1 class Solution:
 2     # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
 3     # 函数返回True/False
 4     def duplicate(self, numbers, duplication):
 5         # write code here
 6         if not numbers:
 7             return False
 8         # 判断数组元素是否都在0~n-1
 9         n = len(numbers)
10         for i in range(n):
11             if numbers[i] < 0 or numbers[i] > n-1:
12                 return False
13         
14         for i in range(n):
15             while i != numbers[i]:
16                 if numbers[i] == numbers[numbers[i]]:
17                     duplication[0] = numbers[i]
18                     return True
19                 tmp = numbers[i]
20                 numbers[i] = numbers[numbers[i]]
21                 numbers[tmp] = tmp
22         return False
View Code

 

3. 构建乘积数组

给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

解:

这题思路还是比较巧妙的,看个图就懂了。先算下三角的连乘,然后倒过来把剩下那部分也乘进去。

 1 class Solution:
 2     def multiply(self, A):
 3         if not A:
 4             return []
 5         n = len(A)
 6         B_left = [1]*n
 7         B_right = [1]*n
 8         for i in range(1, n):
 9             B_left[i] = B_left[i-1]*A[i-1]
10             B_right[-i-1] = B_right[-i]*A[-i]
11         for i in range(n):
12             B_left[i] *= B_right[i]
13         return B_left
View Code

4. 二维数组中的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解:

第一种思路,能保证每一行都是有序的,按行二分查找。

 1 class Solution:
 2     # array 二维列表
 3     def Find(self, target, array):
 4         # write code here
 5         if not array:
 6             return False
 7         m, n = len(array), len(array[0])
 8         
 9         def binarySearch(target, nums):
10             if not nums:
11                 return False
12             left, right = 0, len(nums)-1
13             while left <= right:
14                 mid = left + (right-left)//2
15                 if nums[mid] == target:
16                     return True
17                 elif nums[mid] > target:
18                     right = mid - 1
19                 else:
20                     left = mid + 1
21             return False
22         
23         for i in range(m):
24             if binarySearch(target, array[i]):
25                 return True
26         return False
View Code

  

另一种思路,考虑到左下角的元素,往右更大,往上更小。以左下角为起点搜索,如果比target大就向上,如果比target小就向右

 1 class Solution:
 2     # array 二维列表
 3     def Find(self, target, array):
 4         # write code here
 5         if not array:
 6             return False
 7         m, n = len(array), len(array[0])
 8         i, j = m-1, 0
 9         while i >= 0 and j < n:
10             if array[i][j] == target:
11                 return True
12             elif array[i][j] > target:
13                 i -= 1
14             else:
15                 j += 1
16         return False
View Code

  

5. 连续子数组的最大和

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

解:

这题其实是个基础的dp问题,定义dp[i]表示以a[i]结尾的连续子向量最大和,那么显然dp[i] = max(dp[i-1] + array[i], array[i]),即如果以a[i-1]结尾的连续子向量最大和为正就保留,否则没有在做正贡献,直接从a[i]重新开始。

最后要求的是最大连续子序列的和,可能是以数组中任意位置元素结尾的,所以返回max(dp)

 1 class Solution:
 2     def FindGreatestSumOfSubArray(self, array):
 3         if array is None or len(array)==0:
 4             return 
 5         n = len(array)
 6         dp = [0]*n
 7         dp[0] = array[0]
 8         for i in range(1, n):
 9             dp[i] = max(dp[i-1] + array[i], array[i])
10         return max(dp)
View Code

  

把空间效率再优化下

 1 class Solution:
 2     def FindGreatestSumOfSubArray(self, array):
 3         if not array:
 4             return 0
 5         res = float('-inf')
 6         n = len(array)
 7         sum_ = array[0]
 8         for i in range(1, n):
 9             if sum_ > 0:
10                 sum_ += array[i]
11             else:
12                 sum_ = array[i]
13             res = max(res, sum_)
14         return res
View Code

6. 最小的k个数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

解:

直接快排然后取前k个,O(NlogN)。但是N很大k很小的情况下这样很不划算。

1 class Solution:
2     def GetLeastNumbers_Solution(self, tinput, k):
3         # write code here
4         if not tinput or k > len(tinput):
5             return []
6         tinput.sort()
7         return tinput[:k]
View Code

  

用小顶堆实现始终维护最小的K个数,O(NlogK)

 1 import heapq
 2 
 3 class Solution:
 4     def GetLeastNumbers_Solution(self, tinput, k):
 5         # write code here
 6         if not tinput or k > len(tinput):
 7             return []
 8         n = len(tinput)
 9         heap =[]
10         for i in range(n):
11             heapq.heappush(heap, -tinput[i])
12             if i >= k:
13                 heapq.heappop(heap)
14         res = list(map(lambda x: -x, list(heap)))
15         res.sort()  # 输出要求
16         return res
View Code

7. 和为 S 的连续正整数序列

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输出描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

解:

这题思路还比较巧妙,双指针维护动态窗口,窗口内的和可以用等差数列通项公式计算。如果窗口内数字之和小于目标值,右游标右移(想让窗口和变大);小于目标值,左游标右移(拿掉一个窗口中最小的数)。如果找到一组解,左游标

 1 class Solution:
 2     def FindContinuousSequence(self, tsum):
 3         # write code here
 4         if tsum < 3:
 5             return []
 6         res = []
 7         l, r = 1, 2
 8         target = 2 * tsum
 9         while l < r:
10             window = (l+r)*(r-l+1)
11             if window == target:
12                 res.append(list(range(l, r+1)))
13                 l += 1
14             elif window < target:
15                 r += 1
16             else:
17                 l += 1
18         return res
View Code

  

8. 数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

解:

二分查找 target 的左侧边界,再二分查找右侧边界,求区间长度即可

 1 class Solution:
 2     def GetNumberOfK(self, data, k):
 3         # write code here
 4         start = self.search(data, k)
 5         if start == -1:
 6             return 0
 7         end = self.searchEnd(data, k)
 8         return end - start +1
 9     
10     def search(self, nums, target):
11         if nums == None or len(nums) == 0:
12             return -1
13         low = 0
14         high = len(nums)
15         while low < high:  
16             mid = (low + high) // 2
17             if nums[mid] == target:
18                 high = mid   
19             elif nums[mid] > target:
20                 high = mid
21             elif nums[mid] < target:
22                 low = mid + 1
23         if low == len(nums): 
24             return -1
25         return low if nums[low] == target else -1
26     
27     def searchEnd(self, nums, target):
28         if nums == None or len(nums) == 0:
29             return -1
30         low = 0
31         high = len(nums)
32         while low < high:   
33             mid = (low + high) // 2
34             if nums[mid] == target:
35                 low = mid + 1
36             elif nums[mid] > target:
37                 high = mid
38             elif nums[mid] < target:
39                 low = mid + 1
40         if low == len(nums)+1: 
41             return -1
42         return low-1 if nums[low-1] == target else -1
View Code

  

9. 数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解:

用哈希表统计出现次数

 1 class Solution:
 2     # 返回[a,b] 其中ab是出现一次的两个数字
 3     def FindNumsAppearOnce(self, array):
 4         # write code here
 5         hashmap = dict()
 6         for num in array:
 7             hashmap[num] = hashmap.get(num, 0) + 1
 8             
 9         res = []
10         for key, value in hashmap.items():
11             if value == 1:
12                 res.append(key)
13         return res
View Code

  

假设只出现一次的两个数是A 和 B全部异或一次,结果就是 A、B的异或。找到A和B不同的位数,并以此为基准把原数组分成两部分,各自异或就行了

 1 class Solution:
 2     # 返回[a,b] 其中ab是出现一次的两个数字
 3     def FindNumsAppearOnce(self, array):
 4         # write code here
 5         if not array:
 6             return []
 7         resXor = 0
 8         for num in array:
 9             resXor ^= num
10         
11         indicator = resXor & (-resXor)
12         
13         num1, num2 = 0, 0
14         for num in array:
15             if num & indicator == 0:
16                 num1 ^= num
17             else:
18                 num2 ^= num
19         return [num1, num2]
View Code

10. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字

例如,如果输入如下4 X 4矩阵:

1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 16

则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

解:

每一圈都是一样的,只要搞清楚每一圈的起点就行了。首先需要判断每一步开始是的坐标点是否满足小于行数的一半且小于列数的一半,在最后一圈中,可能出现仅能向右走一行,仅能向右走一行向下走一列,向右走一行向下走一列向左走一行,能走完整一圈,一共四种情况。其中只有能向右走一行必然发生,不必判断,剩余的都需要判断发生条件。

 1 class Solution:
 2     # matrix类型为二维列表,需要返回列表
 3     def printMatrix(self, matrix):
 4         # write code here
 5         if not matrix:
 6             return []
 7         rows, cols = len(matrix), len(matrix[0])
 8         start = 0
 9         res = []
10         while 2*start < rows and 2*start < cols:
11             res += self.printCycle(matrix, rows, cols, start)
12             start += 1
13         return res
14     def printCycle(self, matrix, rows, cols, start):
15         endx = cols - 1 - start
16         endy = rows - 1 - start
17         res = []
18         # 从左到右
19         for i in range(start, endx+1):
20             res.append(matrix[start][i])
21         # 从上到下
22         if start < endy:
23             for i in range(start+1, endy+1):
24                 res.append(matrix[i][endx])
25         # 从右到左
26         if start < endx and start < endy:
27             for i in range(endx-1, start-1, -1):
28                 res.append(matrix[endy][i])
29         # 从下到上
30         if start < endx and start < endy-1:
31             for i in range(endy-1, start, -1):
32                 res.append(matrix[i][start])
33         return res
View Code

  

11. 滑动窗口最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

解:

用双端队列维护一个单调队列,window保存当前窗口中数的下标,window新的头总是当前窗口中最大的那个数。处理前 k 个元素,初始化window。遍历整个数组。在每一步清理双向队列:

  只保留当前滑动窗口中有的元素的索引(因为滑动出去一个,要看一下window中的索引是不是都在i-k到i);

  移除比当前元素小的所有元素,它们不可能是最大的;

  将当前元素添加到双向队列中;

  将 deque[0] 添加到输出中。

 1 class Solution:
 2     def maxInWindows(self, num, size):
 3         # write code here
 4         if not num or not size:
 5             return []
 6         res = []
 7         n = len(num)
 8         window = []  # 存窗口元素的索引
 9         for i in range(n):
10             if i >= size and window[0] <= i-size:
11                 window.pop(0)
12             while window and num[window[-1]] <= num[i]:
13                 window.pop()
14             window.append(i)
15             if i >= size-1:
16                 res.append(num[window[0]])
17         return res
View Code

动态规划,将n个数据分成大小为k的块,定义left[i]为i所处的块的开始位置到i位置的最大值,right[j]为j所处的块的结束位置到j的最大值。那么滑动窗口就两种情况,要么没有跨越两个块,要么跨越了两个块。不管哪种情况,窗口从i到j,窗口最大值为max( right[i], left[j] )

 1 class Solution:
 2     def maxInWindows(self, num, size):
 3         # write code here
 4         if not num or not size:
 5             return []
 6         n = len(num)
 7         left, right = [0]*n, [0]*n
 8         left[0], right[-1] = num[0], num[-1]
 9         for i in range(1, n):
10             if i%size == 0:
11                 left[i] = num[i]
12             else:
13                 left[i] = max(left[i-1], num[i])
14             
15             j = n - 1 - i 
16             if (j+1)%size == 0:
17                 right[j] = num[j]
18             else:
19                 right[j] = max(right[j+1], num[j])
20         dp = []
21         for i in range(n-size+1):
22             dp.append(max(right[i], left[i+size-1]))
23         return dp
View Code

12. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

解:

类二分的方法,不断的收缩两端边界。当然要先判断是否旋转了。

 1 class Solution:
 2     def minNumberInRotateArray(self, rotateArray):
 3         # write code here
 4         if not rotateArray:
 5             return 0
 6         if len(rotateArray)==1 or rotateArray[0] < rotateArray[-1]:
 7             return rotateArray[0]
 8         
 9         n = len(rotateArray)
10         left, right = 0, n-1
11         while left < right:  
12             mid = left + (right-left)//2
13             if rotateArray[mid] > rotateArray[right]:  # 最小数字一定在mid右边
14                 left = mid + 1  # 左闭右开
15             elif rotateArray[mid] < rotateArray[right]:  # 最小数字一定在mid左边
16                 right = mid
17             else:  # 不好判断是在mid左边还是右边,一个一个试
18                 right -= 1
19         return rotateArray[right]
View Code

13.丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解:

一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到,从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到4,6,10, 6,9,15, 10,15,25九个丑数,这种方法会得到重复的丑数,而且题目要求第N个丑数,这样的方法得到的丑数也是无序的。那么我们可以维护三个队列:
(1)丑数数组: 1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(2)丑数数组:1,2
乘以2的队列:4
乘以3的队列:3,6
乘以5的队列:5,10
选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(3)丑数数组:1,2,3
乘以2的队列:4,6
乘以3的队列:6,9
乘以5的队列:5,10,15
选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(4)丑数数组:1,2,3,4
乘以2的队列:6,8
乘以3的队列:6,9,12
乘以5的队列:5,10,15,20
选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(5)丑数数组:1,2,3,4,5
乘以2的队列:6,8,10,
乘以3的队列:6,9,12,15
乘以5的队列:10,15,20,25
选择三个队列头里最小的数6加入丑数数组,但发现有两个队列头都为6,所以弹出两个队列头,同时将12,18,30放入三个队列;
……
实现的时候,没有必要维护三个队列,只需要维护三个队列头(更新指针)即可。
 1 class Solution:
 2     def GetUglyNumber_Solution(self, index):
 3         # write code here
 4         if index < 7:
 5             return index
 6         p2, p3, p5 = 0, 0, 0
 7         arr = [0]*index
 8         arr[0] = 1
 9         for i in range(1, index):
10             arr[i] = min(arr[p2]*2, arr[p3]*3, arr[p5]*5)
11             # 三个if可能进入一个或多个
12             if arr[i] == arr[p2]*2:  # 如果选了乘以2的队列,更新队头指针
13                 p2 += 1
14             if arr[i] == arr[p3]*3:
15                 p3 += 1
16             if arr[i] == arr[p5]*5:
17                 p5 += 1
18         return arr[index-1]
View Code

维护一个堆,每次都把三个可能的丑数push进堆,但取出第i个丑数的时候,要保证取出之后堆顶是更大的丑数

 1 import heapq
 2 class Solution:
 3     def GetUglyNumber_Solution(self, index):
 4         # write code here
 5         if index < 7:
 6             return index
 7         heap = [1]
 8         for _ in range(index):
 9             res = heapq.heappop(heap)
10             while heap and res == heap[0]:
11                 res = heapq.heappop(heap)
12             q2, q3, q5 = res*2, res*3, res*5
13             for num in [q2, q3, q5]:
14                 heapq.heappush(heap, num)
15         return res
View Code
 
14. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共结点。
解:
用哈希表保存一个链表的节点,然后遍历第二个链表即可。 
 1 # class ListNode:
 2 #     def __init__(self, x):
 3 #         self.val = x
 4 #         self.next = None
 5 class Solution:
 6     def FindFirstCommonNode(self, pHead1, pHead2):
 7         # write code here
 8         if not pHead1 or not pHead2:
 9             return None
10         linkedList1Nodes = set()
11         p = pHead1
12         while p:
13             linkedList1Nodes.add(p)
14             p = p.next
15             
16         p = pHead2
17         while p:
18             if p in linkedList1Nodes:
19                 return p
20             p = p.next
21         return None
View Code

如果两个链表有公共节点,那么肯定会各自在某个节点处开始链向同一个节点。假设链表1长度为a+n,链表2长度为b+n,同时出发,p1到达链表1尾部的时候p2走到链表2的a+n处。这时p1再从链表2头部开始,当p2到达尾部时,p1处在链表2的b-a处。将p2从链表1头部开始,如果有公共节点,那么都再走a步就到达公共节点,否则都走a+n步到尾部。

代码及其简洁

 1 # class ListNode:
 2 #     def __init__(self, x):
 3 #         self.val = x
 4 #         self.next = None
 5 class Solution:
 6     def FindFirstCommonNode(self, pHead1, pHead2):
 7         # write code here
 8         if not pHead1 or not pHead2:
 9             return None
10         p1, p2 = pHead1, pHead2
11         while p1 != p2:
12             p1 = p1.next if p1 else pHead2
13             p2 = p2.next if p2 else pHead1
14         return p1
View Code

15. 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

解:

归并排序的变式。先分成最低单位,考虑比较相邻的数字,再合并。合并过程中,出现前面的数组值array[i]大于后面数组值array[j]时;则前面数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i。例如5大于4, 那么5,7都会大于4,逆序对个数增加2

 1 class Solution:
 2     def __init__(self):
 3         self.count = 0
 4 
 5     def InversePairs(self, data):
 6         # write code here
 7         if not data:
 8             return 0%1000000007
 9         self.mergeSort(data)
10         return self.count%1000000007
11     
12     def mergeSort(self, array):
13         if len(array) <= 1:
14             return array
15         num = len(array) // 2
16         left = self.mergeSort(array[:num])
17         right = self.mergeSort(array[num:])
18         # merge
19         l, r = 0, 0
20         result = []
21         while l<len(left) and r<len(right):
22             if left[l] < right[r]:
23                 result.append(left[l])
24                 l += 1
25             else:
26                 result.append(right[r])
27                 r += 1
28                 # 前面的array[i]大于后面的array[j]时,array[i]~array[mid]都比array[j]大,统计逆序对
29                 self.count += len(left) - l
30         if l < len(left):
31             result += left[l:]
32         elif r < len(right):
33             result += right[r:]
34         return result 
View Code

 

16.数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解:

快排取中间位置的数,再遍历一遍统计个数判断,O(NlogN)

哈希表统计,线性复杂度,但需要额外空间

 1 from collections import Counter
 2 class Solution:
 3     def MoreThanHalfNum_Solution(self, numbers):
 4         # write code here
 5         if not numbers:
 6             return 0
 7         n = len(numbers)
 8         count = Counter(numbers)
 9         for k, v in count.items():
10             if v > n//2:
11                 return k
12         return 0
View Code
如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多,对数组同时去掉两个不同的数字,到最后剩下的一个数就是该数字。。在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。
 1 class Solution:
 2     def MoreThanHalfNum_Solution(self, numbers):
 3         # write code here
 4         if not numbers:
 5             return 0
 6         n = len(numbers)
 7         res = numbers[0]
 8         count = 1  
 9         for i in range(n):
10             if count == 0:
11                 res = numbers[i]  # 更新res为当前元素(新取两个数,看看是否相同)
12                 count = 1
13             elif numbers[i] == res:
14                 count += 1  # 相同则计数+1
15             else:
16                 count -= 1  # 不同则计数-1(去掉两个不同的数)
17         
18         count = 0
19         for i in range(n):
20             if numbers[i] == res:
21                 count += 1
22         return res if count > n//2 else 0
View Code
原文地址:https://www.cnblogs.com/chaojunwang-ml/p/11442889.html