文章目录
数组
350题 两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。给定两个数组,编写一个函数来计算它们的交集。
示例1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
解题思路:
- 使用map: 遍历第一个数组并存入map中,键为数组的值,值为出现的次数。然后遍历第二个数组,如果map中有这个键,就将键对应的值减一。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Map<Integer, Integer> map = new HashMap();
for(int num : nums1){ //将num1放入map
map.put(num,map.getOrDefault(num,0)+1);
}
int k = 0;
for(int num : nums2){
if(map.containsKey(num)){
if(map.get(num) > 0){
map.put(num,map.getOrDefault(num,0)-1);
nums2[k++] = num; //将满足条件的数放到nums2开头
}
}
}
return Arrays.copyOfRange(nums2, 0, k);
}
}
- 先排序,然后双指针,如果不相等,将元素小的指针右移,相等就一起右移。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int l1 = 0, r1 = nums1.length, l2 = 0, r2 = nums2.length,k = 0;
while(l1 < r1 && l2 < r2){ //如果有一个数组遍历完就可以结束了
if(nums1[l1] < nums2[l2]){
l1++;
}else if(nums1[l1] > nums2[l2]){
l2++;
}else{
nums2[k++] = nums1[l1++]; ///同样用nums2接收,也可以新建一个数组
l2++;
}
}
return Arrays.copyOfRange(nums2,0,k);
}
}
附加: 判断一个数组是不是另一个数组的子集,排序+双指针,解法相似。
14题 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解题思路:
因为要找所有字符公共前缀,那么所有字符的公共前缀肯定小于等于任意两个字符的公共前缀。因此,只需要依次比较任意两个字符的前缀,再拿这个前缀和后面的比,最后得到的字符就是公共前缀。
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs == null || strs.length == 0) return "";
String pre = strs[0]; //pre接收公共前缀的返回结果
for(int i = 1; i < strs.length; i++){
pre = cut(pre,strs[i]);
if(pre.length() == 0) break; //提前结束
}
return pre;
}
public static String cut(String str1, String str2){ //返回任意两个字符的公共前缀
int len = Math.min(str1.length(), str2.length());
int index = 0;
while(index < len && str1.charAt(index) == str2.charAt(index)){
index++;
}
return str1.substring(0,index);
}
}
189题 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解题思路:
- 元素全部反转,再反转前k个,后lengh-k个
- 暴力,每次将最后一个移到前面
- 额外数组
public class Solution { //解法一`在这里插入代码片`
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
}
27题 原地删除
给你一个数组 nums 和一个值 val,你需要原地
移除所有数值等于 val 的元素,并返回移除后数组的新长度。
示例:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
解题思路:
双指针,这里的双指针和上面有一点不一样,让一个指针在数组上面跑,另外一个指针重新从0开始,并且过滤掉一些重复数据。
class Solution {
public int removeElement(int[] nums, int val) {
int j = 0;
for(int i = 0; i < nums.length; i++){
if(nums[i] != val){
nums[j++] = nums[i];
}
}
return j;
}
}
26题 删除排序数组中的重复项
给定一个排序数组
,你需要在原地
删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
示例:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
附加: 前面两道题这种双指针的题还有第283题,都是在原地删除。
解题思路:
和上面那道题相似,去掉重复项,还是使用双指针。
class Solution {
public int removeDuplicates(int[] nums) {
int j = 0;
for(int i = 1; i < nums.length; i++){
if(nums[j] != nums[i]){
nums[++j] = nums[i];
}
}
return j+1;
}
}
66题 加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
示例:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
解题思路:
两种情况:1、除9之外的数字加1。 2、9加1
只需要判断有没有进位并模拟出它的进位方式,如十位数加 11 个位数置为 00,如此循环直到判断没有再进位就退出循环返回结果
然后还有一些特殊情况就是当出现 9999、999999 之类的数字时,循环到最后也需要进位,出现这种情况时需要手动将它进一位。
class Solution {
public int[] plusOne(int[] digits) {
for(int i = digits.length-1; i >=0; i--){
digits[i]++;
digits[i] %= 10;
if(digits[i] != 0) return digits; //如果不为0,表示没有进位,直接返回
}
//如果遍历到了第一位,digits[0] = 0表示任然进位了,那么只需要将数组扩大1,并将第一位置为1即可
digits = new int[digits.length + 1];
digits[0] = 1;
return digits;
}
}
594. 最长和谐子序列
和谐数组是指一个数组里元素的最大值和最小值之间的差别正好是1。
现在,给定一个整数数组,你需要在所有可能的子序列中找到最长的和谐子序列的长度。
示例 1:
输入: [1,3,2,2,5,2,3,7]
输出: 5
原因: 最长的和谐数组是:[3,2,2,2,3].
解题思路:
和谐序列中最大数和最小数只差正好为 1。
public int findLHS(int[] nums) {
Map<Long, Integer> map = new HashMap<>();
for (long num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
int result = 0;
for (long key : map.keySet()) {
if (map.containsKey(key + 1)) {
result = Math.max(result, map.get(key + 1) + map.get(key));
}
}
return result;
}
1题 两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解题思路:
- 暴力法很简单,遍历每个元素 xx,并查找是否存在一个值与 target - xtarget−x 相等的目标元素
- 两遍哈希,一遍哈希(两遍哈希的时候要排除是同一个数加两次的可能)
//一遍哈希
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap();
for(int i = 0; i < nums.length; i++){
int temp = target - nums[i];
if(map.containsKey(temp)){
return new int[]{i,map.get(temp)};
}
map.put(nums[i],i);
}
return null;
}
}
15题 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解题思路:
首先,我们对数组升序排序,采取固定一个数,同时用双指针来查找另外两个数的。
其次,固定下来的数本身就大于 0,那三数之和必然无法等于 0
然后,如果和大于0,那就说明 right 的值太大,需要左移。如果和小于0,那就说明 left 的值太小,需要右移,同时性需要相同的数需要跳过。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList();
for(int i = 0; i < nums.length; i++){
int target = 0 - nums[i];
int l = i + 1;
int r = nums.length - 1;
if(nums[i] > 0) break; //提前判断
if(i == 0 || nums[i] != nums[i-1]){ // 保证和上次枚举的数不同
while(l < r){
if(nums[l] + nums[r] == target){
ans.add(Arrays.asList(nums[i], nums[l], nums[r]));
//跳过相同元素
while(l < r && nums[l] == nums[l+1]) l++;
while(l < r && nums[r] == nums[r-1]) r--;
l++;
r--;
}else if(nums[l] + nums[r] < target){
l++;
}else
r--;
}
}
}
return ans;
}
}
16题 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
解题思路:
和15题很相似,这里需要改变的是:
- 根据 sum = nums[i] + nums[start] + nums[end] 的结果,判断 sum 与目标 target 的距离,如果更近则更新结果 ans。
- 同时判断 sum 与 target 的大小关系,因为数组有序,如果 sum > target 则 end- -,如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果。
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int ans = nums[0] + nums[1] + nums[2];
for(int i=0;i<nums.length;i++) {
int start = i+1, end = nums.length - 1;
while(start < end) {
int sum = nums[start] + nums[end] + nums[i];
if(Math.abs(target - sum) < Math.abs(target - ans))
ans = sum;
if(sum > target)
end--;
else if(sum < target)
start++;
else
return ans;
}
}
return ans;
}
}
6题 Z字打印
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。
示例:
输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"
解题思路:
给了行数,实际就是将这个字符串按行数从上到下,再从下到上放到对应的行上面,最后将这些行的字符串合并。
从左到右迭代 s,将每个字符添加到合适的行。可以使用当前行i
和当前方向flag
这两个变量对合适的行进行跟踪。
class Solution {
public String convert(String s, int numRows) {
if(numRows < 2) return s;
List<StringBuilder> list = new ArrayList();
for(int i = 0; i < numRows; i++){
list.add(new StringBuilder());
}
int i = 0, flag = -1;
for(char c : s.toCharArray()){
list.get(i).append(c);
if(i == 0 || i == numRows - 1) flag = -flag;
i += flag;
}
StringBuilder res = new StringBuilder();
for(StringBuilder row : list) res.append(row);
return res.toString();
}
}
54题 螺旋打印
leet连接 这个题主要用left,right,up,down四个方向来控制,主要考验coding能力。
附加
给定一个无序数组a[]和一个指定值sum,求满足a的和为sum的最长子数组的长度。
输入: a[] = 1 3 5 3 6 8 4 2 2 5
sum = 8
输出: 3
提示:满足的最长子数组为 4 2 2
import java.util.HashMap;
import java.util.Map;
public class _01_Array_maxLength {
public int maxLength(int[] arr, int k) {
if (arr == null || arr.length == 0) {
return 0;
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(0, -1);
int len = 0;
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
if (map.containsKey(sum - k)) {
len = Math.max(i - map.get(sum - k), len);
}
if (!map.containsKey(sum)) {
map.put(sum, i);
}
}
return len;
}
}
有序矩阵
有序矩阵指的是行和列分别有序的矩阵。一般可以利用有序性使用二分查找方法。
1-n分布题型(接下来三题)
645题 错误的集合
集合S
包含从1到n
的整数。不幸的是,因为数据错误,导致集合里面某一个元素复制了成了集合里面的另外一个元素的值,导致集合丢失了一个整数并且有一个元素重复。
给定一个数组nums
代表了集合S
发生错误后的结果。你的任务是首先寻找到重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
示例:
输入: nums = [1,2,2,4]
输出: [2,3]
解题思路:
有一个出现重复的元素,有多种解法
- 暴力,对于1-n的每个数字,都去遍历集合,去看看当前数字是否出现了两次。使用 dupdup 和 missingmissing 记录重复数字和缺失数字。
- 排序,排序
nums
数组后,相等的两个数字将会连续出现。此外,检查相邻的两个数字是否只相差 1 可以找到缺失数字。 - map,检查 11 到 nn 的每个数字在 mapmap 中出现次数。如果一个数字在 mapmap 中没有出现,它就是缺失数字。如果一个数字的出现了两次,它就是重复数字。也可以换成数组
- …
class Solution {
public int[] findErrorNums(int[] nums) {
Map<Integer, Integer> map = new HashMap();
int dup = -1, missing = -1;
for(int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
for(int i = 1; i <= nums.length; i++){
if(map.containsKey(i)){
if(map.get(i) == 2){
dup = i;
}
}else{
missing = i;
}
}
return new int[]{dup, missing};
}
}
448题 找到所有数组中消失的数字
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。找到所有在 [1, n] 范围之间没有出现在数组中的数字。
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[5,6]
解题思路:
这道题可以和上面一样,先放入map,然后遍历1-n来判断。对于1-n的题型还有另外的解法(原地修改):
- 把
|nums[i]|-1
索引位置的元素标记为负数。即nums[|nums[i] |- 1]
* -1。 - 然后遍历数组,若当前数组元素 nums[i] 为负数,说明我们在数组中存在数字 i+1
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> res = new ArrayList();
for(int i = 0; i < nums.length; i++){
int index = Math.abs(nums[i]) - 1;
if(nums[index] > 0) nums[index] *= -1; // 改变nums[i]原来位置上的值为负
}
for(int i = 1; i <= nums.length; i++){ //遍历1-n找不为负数的数字
if(nums[i - 1] > 0){
res.add(i); //添加的是索引
}
}
return res;
}
}
442题 数组中重复的数字
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。找到所有出现两次的元素。
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
解题思路:
这道题可以和上面一样,先放入map,然后遍历1-n来判断。
- 找到数字
i
时,将位置i-1
处的数字翻转为负数。 - 如果位置
i-1
上的数字已经为负,则i是出现两次的数字。
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res = new ArrayList();
for(int i = 0; i < nums.length; i++){
int index = Math.abs(nums[i]) - 1; //这里就是找这个nums[i]应该放在的索引位置
if(nums[index] < 0){
res.add(Math.abs(index + 1)); //这个索引位置加1就是1-n中对应的值
}
nums[index] *= -1;
}
return res;
}
}
287题 寻找重复数
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
解题思路:
这里并没有说1-n的每个数都会出现,也有可能全部是一样的数,那么上面的1-n方法在这里无效。
- 二分查找
- 快慢指针
小结
就目前上面的题来看,解决数组问题的时候先想想以下方法能不能解决:
- map
- 排序+双指针
- 反转
- 数学运算
- list
- 对于1-n类型题的原地修改