Leetcode数组题目
- 2019 年 11 月 14 日
- 笔记
学习一时爽,一直学习一直爽
本文来自数据结构与算法之美 春节加餐
争哥:https://github.com/wangzheng0822/algo
数组题目

在这里插入图片描述
这不补下春节欠的债
Three Sum(求三数之和)
leetcode的第15题
https://leetcode-cn.com/problems/3sum/
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
对于twosum就是遍历寻找另一个是不是在nums中
class Solution: def threeSum(self, nums: List[int]) -> List[List[int]]: ''' 1、首先我们需要排序 2、循环nums 3、固定一个值,找另外二个值它们和等于 0 4、如果三个数相加等于零则存储到相应的二维数组中 :param nums: :return: ''' result = [] nums.sort() a = len(nums) # 固定最后值 找前面另外二个值它们和等于 0 for i in range(a - 1): # 遍历前面的数组 如果它们和等于 0 for j in range(i + 1, a): if -(nums[i] + nums[j]) in nums[j + 1:]: array = [nums[i], nums[j], -(nums[i] + nums[j])] if array not in result: result.append(array) return result
但是这超时了,o(n2)时间复杂度不行

在这里插入图片描述 这是就有一种很特别的方法,
作者:jyd 链接:https://leetcode-cn.com/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/ 来源:力扣(LeetCode)
解题思路:
- 暴力法搜索为 O(N^3)O(N 3 )
时间复杂度,可通过双指针动态消去无效解来优化效率。
- 双指针法铺垫:先将给定 nums 排序,复杂度为 O(NlogN)O(NlogN)。
双指针法思路:固定 3 个指针中最左(最小)数字的指针 k,双指针 i,j 分设在数组索引 (k, len(nums))(k,len(nums)) 两端,通过双指针交替向中间移动,记录对于每个固定指针 k 的所有满足 nums[k] + nums[i] + nums[j] == 0 的 i,j 组合:
当 nums[k] > 0 时直接break跳出:因为 nums[j] >= nums[i] >= nums[k] > 0,即 33 个数字都大于 00 ,在此固定指针 k 之后不可能再找到结果了。
当 k > 0且nums[k] == nums[k – 1]时即跳过此元素nums[k]:因为已经将 nums[k – 1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。
i,j 分设在数组索引 (k, len(nums))(k,len(nums)) 两端,当i < j时循环计算s = nums[k] + nums[i] + nums[j],并按照以下规则执行双指针移动:
- 当s < 0时,i += 1并跳过所有重复的nums[i];
- 当s > 0时,j -= 1并跳过所有重复的nums[j];
- 当s == 0时,记录组合[k, i, j]至res,执行i += 1和j -= 1并跳过所有重复的nums[i]和nums[j],防止记录到重复组合。
class Solution: def threeSum(self, nums): # 如果列表长度小于3,则条件不成立,返回空列表 if len(nums) < 3: return [] n = len(nums) # 先排序 nums.sort() list_nums = [] # 从列表第一个数开始循环,和它右边选两个数相加,求和 # 所以只能到倒数第三个数为止,因为倒数第二个数右边只有一个数 for i in range(n - 2): # 因为列表已经按从小到大顺序排列了,如果左边数字大于0,则右边一定大于0 # 则三数之和不可能为0 if nums[i] > 0: break # 从第一个元素开始,如果它的左边元素和它相等,则跳过进行下一轮循环 if i >= 1 and nums[i - 1] == nums[i]: continue # 分别从当前元素右边第一个元素和最右边元素开始,计算三个元素之和 left, right = i + 1, n - 1 # 如果左路元素没有和右路元素相交,则继续靠近 while left < right: sums = nums[i] + nums[left] + nums[right] if sums == 0: list_nums.append([nums[i], nums[left], nums[right]]) # 从左边开始移动到最后一个重复元素 while left < right and nums[left] == nums[left + 1]: left += 1 # 从右边开始移动到最后一个重复元素 while left < right and nums[right] == nums[right - 1]: right -= 1 # 跳过重复元素 left, right = left + 1, right - 1 elif sums < 0: left += 1 else: right -= 1 return list_nums
将python改写成Java,思路一样
class Solution { public List<List<Integer>> threeSum(int[] num) { Arrays.sort(num); List<List<Integer>> res = new LinkedList<List<Integer>>(); for (int i = 0; i < num.length-2; i++) { if (i == 0 || (i > 0 && num[i] != num[i-1])) { int left = i+1, right = num.length-1, sum = 0 - num[i]; while (left < right) { if (num[left] + num[right] == sum) { res.add(Arrays.asList(num[i], num[left], num[right])); while (left < right && num[left] == num[left+1]) left++; while (left < right && num[right] == num[right-1]) right--; left++; right--; } else if (num[left] + num[right] < sum) left++; else right--; } } } return res; } }
leetcode 第169题
https://leetcode-cn.com/problems/majority-element/
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3] 输出: 3
示例 2:
输入: [2,2,1,1,1,2,2] 输出: 2
暴力算法
暴力算法遍历整个数组,然后用另一重循环统计每个数字出现的次数。将出现次数比其他数字加起来出现次数还多的元素返回。
class Solution: def majorityElement(self, nums): majority_count = len(nums)//2 for num in nums: count = sum(1 for elem in nums if elem == num) if count > majority_count: return num
但是两次遍历时间复杂度O(n2),超时了
将nums排序,那些众数一定在中间
时间复杂度:O(nlgn)O(nlgn)
用 Python 和 Java 将数组排序开销都为 O(nlgn)O(nlgn) ,它占据了运行的主要时间。
空间复杂度:O(1)O(1)或者O(n)O(n)
class Solution: def majorityElement(self, nums: List[int]) -> int: return sorted(nums)[len(nums)//2]
但是py时间复杂度太大了

在这里插入图片描述
同样的方法用Java写
class Solution { public int majorityElement(int[] nums) { Arrays.sort(nums); return nums[nums.length/2]; } }
速度快了几百倍

在这里插入图片描述
排序法是最好的方法
https://leetcode-cn.com/problems/first-missing-positive/
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0] 输出: 3
示例 2:
输入: [3,4,-1,1] 输出: 2
示例 3:
输入: [7,8,9,11,12] 输出: 1
说明:
你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
return的一定是0到len(nums)中的一个正整数
class Solution(object): def firstMissingPositive(self, nums): for i in range(1,len(nums)+2): if i not in nums: return i
改写成Java,思路一样
class Solution { public int firstMissingPositive(int[] nums) { int len = nums.length; int[] arr = new int[len+2];//定义一个新数组,最小的数字肯定就在新数组的下标中 for (int item : nums) { if (item > 0 && item <= len) { //排除负数和大于输入数组长度的数,因为缺失的正数肯定小于数组的长度+1(这里需要仔细想清楚) arr[item] = 1;//新数组下标为1,则表示有这个数 } } //这一步就简单了,遍历新数组,看看第一个没有的正整数是谁就行了 for (int i = 1; i < len+2; i++) { if (0 == arr[i]) { return i; } } return len+1;//这一步为了应对空数组 } }