以下仅为个人刷题笔记记录,如有错误,敬请指正,勿喷。
数组部分刷题记录
一、数组的遍历
question:485
给定一个二进制数组 nums , 计算其中最大连续 1 的个数。
示例1:
输入:nums = [1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
示例二:
输入:nums = [1,0,1,1,0,1]
输出:2
answer:
设置一个temp变量统计当前连续1的个数。当遇到0时,比较当前1的个数 与之前最大值。
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int len=nums.length;
int max=0;
if(len<1){return max;}
int temp=0;
for(int i=0;i<len;i++){
if(nums[i]==1){
temp+=1;
}else{
max=Math.max(max,temp);
temp=0;
}
}
max=Math.max(max,temp);
return max;
}
}
question:495
answer:
用ans记录总秒数,expired记录每次攻击结束的时间。
class Solution {
public int findPoisonedDuration(int[] timeSeries, int duration) {
int ans = 0;
int expired = 0;
for (int i = 0; i < timeSeries.length; ++i) {
//如果当前处于未中毒状态,持续中毒时间加duration;
if (timeSeries[i] >= expired) {
ans += duration;
} else {
//如果当前处于中毒状态,增加的持续时间为 timeSeries[i] + duration - expired;
//timeSeries[i] + duration - expired 为当前timeSeries[i]增加的持续中毒时间
ans += timeSeries[i] + duration - expired;
}
expired = timeSeries[i] + duration;
}
return ans;
}
}
question 414
answer:
对数组排序,再反转,即递减排序
再找到第三大的数字
用tag作为第几个数的标记标记字段
class Solution {
public int thirdMax(int[] nums) {
//对nums排序
Arrays.sort(nums);
reverse(nums);
for(int i=1,tag=1;i<nums.length;i++){
if(nums[i]!=nums[i-1]&& ++tag==3){
return nums[i];
}
}
return nums[0];
}
public void reverse(int[] nums){
int left=0;
int right=nums.length-1;
while(left<right){
int tem=nums[left];
nums[left]=nums[right];
nums[right]=tem;
left++;
right--;
}
}
}
answer2:
//力扣题解
class Solution {
public int thirdMax(int[] nums) {
//TreeSet表示递增的有序数组
TreeSet<Integer> s = new TreeSet<Integer>();
for (int num : nums) {
s.add(num);
if (s.size() > 3) {
s.remove(s.first());
}
}
return s.size() == 3 ? s.first() : s.last();
}
}
question 628
answer:
如果都是正整数:最大乘积为三个最大正整数
如果都是负整数:三大最大负整数
如果正负都有
负数<2 取整数
负数>2 :最小的两个负数*最大正数 or 三个最大正数
综上 找 3个最大的 or 2最小和1个最大;
在这里插入代码片
class Solution {
public int maximumProduct(int[] nums) {
int max1=Integer.MIN_VALUE,max2=Integer.MIN_VALUE,max3=Integer.MIN_VALUE;
int min1=Integer.MAX_VALUE,min2=Integer.MAX_VALUE;
for(int num:nums){
//先找小的
if(num<min1){
min2=min1;
min1=num;
}else if(num<min2){
min2=num;
}
//找大的
if(num>max1){
max3=max2;
max2=max1;
max1=num;
}else if(num>max2){
max3=max2;
max2=num;
}else if(num>max3){
max3=num;
}
}
return Math.max(max1*max2*max3,min1*min2*max1);
}
}
二、统计数组中的元素
question 645
answer:
用Hash表,将所有的元素,以及其出现的次数 存入hash表中,再判断每一个数字出现的次数
class Solution {
public int[] findErrorNums(int[] nums) {
int len=nums.length;
int[] arr=new int[2];
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
for(int i=1;i<=len;i++){
int count=map.getOrDefault(i,0);
if(count==2){
arr[0]=i;
}else if(count==0){
arr[1]=i;
}
}
return arr;
}
}
question 697
answer:
该题是求,出现次数最多的数字中,求其下标距离最近的一组。
所以考虑从数组中的数字出现的次数,第一次出现的下表,和最后一次出现的下表 入手
使用哈希表实现该功能,每一个数映射到一个长度为 3 的数组,数组中的三个元素分别代表这个数出现的次数、这个数在原数组中第一次出现的位置和这个数在原数组中最后一次出现的位置
class Solution {
public int findShortestSubArray(int[] nums) {
int len=nums.length;
Map<Integer,int[]> map=new HashMap<Integer,int[]>();
for(int i=0;i<len;i++){
if(map.containsKey(nums[i])){
map.get(nums[i])[0]++;//数量
map.get(nums[i])[2]=i;//最后一次出现的位置
}else{
map.put(nums[i],new int[]{1,i,i});//第一次出现
}
}
int maxNum=0,minLen=0;
//对map遍历
for(Map.Entry<Integer,int[]> entry:map.entrySet()){
int[] arr=entry.getValue();
if(maxNum<arr[0]){
maxNum=arr[0];
minLen=arr[2]-arr[1]+1;
}else if(maxNum==arr[0]){
if(minLen>arr[2]-arr[1]+1){
minLen=arr[2]-arr[1]+1;
}
}
}
return minLen;
}
}
question 448
这个题,管解思路绝了!!!!
由于nums 的数字范围均在 [1,n]中,我们可以利用这一范围 之外 的数字,来表达「是否存在」的含义。
具体来说,遍历 nums,每遇到一个数 x,就让nums[x−1] 增加 n。由于nums 中所有数均在[1,n]中,增加以后,这些数必然大于 n。最后我们遍历nums,若nums[i] 未大于 n,就说明没有遇到过 数i+1。这样我们就找到了缺失的数字。
注意,当我们遍历到某个位置时,其中的数可能已经被增加过,因此需要对n 取模来还原出它本来的值。
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
int n = nums.length;
for (int num : nums) {
//x是递增排列时 num 对应的下标
int x = (num - 1) % n;
nums[x] += n;
}
List<Integer> ret = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
//该下标中缺少本该对应的数 i+1
if (nums[i] <= n) {
ret.add(i + 1);
}
}
return ret;
}
}
question 442
answer : 同448
首先将出现的元素作为数组的下标,每出现一次就在对应下标的数 加上一个数组的长度,然后再
再筛选出数组中的值在(2n,3n]之间,就是出现两次的数据了,如果要出现三次
就筛选(3n,4n]就行了
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res=new ArrayList<>();
int len=nums.length;
for(int num:nums){
//用数组的数据作为下标;
nums[(num-1)%len]=nums[(num-1)%len]+len;
}
//重复两次再 (2n,3n]之间 [1,n]的范围,不包括0,但包括n
for(int i=0;i<len;i++){
if(nums[i]>2*len&&nums[i]<=3*len){
res.add(i+1);
}
}
return res;
}
}
question 41
answer:
感觉也是 448/442那意思
我们对数组进行遍历,对于遍历到的数 x,如果它在 [1, N] 的范围内,
那么就将数组中的第 x-1 个位置(注意:数组下标从 0 开始)打上「标记」。
在遍历结束之后,如果所有的位置都被打上了标记,那么答案是 N+1,否则答案是最小的没有打上标记的位置加 1;
如何打标记嘞
由于我们只在意 [1, N] 中的数,因此我们可以先对数组进行遍历,把不在 [1, N] 范围内的数修改成任意一个大于 N 的数(例如 N+1)。
这样一来,数组中的所有数就都是正数了,因此我们就可以将「标记」表示为「负号」。
class Solution {
public int firstMissingPositive(int[] nums) {
//只在意[1,n]的数字
//不在[1,n]的数,设置成大于n的
int n = nums.length;
for (int i = 0; i < n; ++i) {
if (nums[i] <= 0) {
nums[i] = n + 1;
}
}
//如果nums[i]在[1,n],将其值置负
for (int i = 0; i < n; ++i) {
int num = Math.abs(nums[i]);
if (num <= n) {
nums[num - 1] = -Math.abs(nums[num - 1]);
}
}
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
return n + 1;
}
}
管解提供的替换思路也很好
我们可以对数组进行一次遍历,对于遍历到的数 x=nums[i],如果 x∈[1,N],我们就知道 x 应当出现在数组中的 x−1 的位置,因此交换 nums[i] 和 nums[x−1],这样 x就出现在了正确的位置。在完成交换后,新的 nums[i] 可能还在 [1,N] 的范围内,我们需要继续进行交换操作
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; ++i) {
while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
int temp = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = temp;
}
}
//此时所有元素应该在对应位置,不在对应位置的第一个数,就是我们要找的数
for (int i = 0; i < n; ++i) {
if (nums[i] != i + 1) {
return i + 1;
}
}
return n + 1;
}
}
question 274
??????
answer:
如果当前 H 指数为 h(论文篇数) 并且在遍历过程中找到当前值 hcitations[i]>h,则说明我们找到了一篇被引用了至少 h+1次的论文,所以将现有的 h 值加 1。继续遍历直到 h 无法继续增大。最后返回 h 作为最终答案。
class Solution {
public int hIndex(int[] citations) {
Arrays.sort(citations);
int len=citations.length;
for(int i=0;i<len;i++){
int h=len-i;
if(h<=citations[i]){
return h;
}
}
return 0;
}
}
三、数组的改变、移动
question 453
answer:
每次操作会使得n-1个数增加1 == 使1个数 减1
class Solution {
public int minMoves(int[] nums) {
//先找到最小值
int len=nums.length;
int min=Integer.MAX_VALUE;
for(int num:nums){
if(num<min){
min=num;
}
}
int sum=0;
for(int num:nums){
sum=sum+(num-min);
}
return sum;
}
}
question 665
answer:
分几种情况
1:前前个数字存在 并 大与当前数字(2) [3,4,2,6] 则改当前数字为 前一个数字的值
2:前前数字不存在[4,2,3] 改前一个数字的值 为当前 数
3:前前数字数字小于当前数字[1,3,2,5] ,[-1,4,2,3]改前一个数字的值 为当前数
lass Solution {
public boolean checkPossibility(int[] nums) {
int len=nums.length;
int tag=0;
for(int i=1;i<len&&tag<2;i++){
if(nums[i]>=nums[i-1]){
continue;
}
tag++;
//前前个数字比当前数字打,改当前数字
if(i>=2&&nums[i-2]>nums[i]){
nums[i]=nums[i-1];
}else{
//改前个数字
nums[i-1]=nums[i];
}
}
return tag<2;
}
}
question 283
answer1
借用一个下标index,先遍历数组,先只存储不为零的数,再补齐
lass Solution {
public void moveZeroes(int[] nums) {
int index = 0;
for (int num : nums) {
if (num != 0) {
nums[index++] = num;
}
}
for (int i = index; i < nums.length; i++) {
nums[i] = 0;
}
}
}
answer2:管解 用的滑动窗口,感觉滑动窗口思维还需锻炼呐
//自己写的滑动窗口 right找第一个为0的下标,left 找right之后不为0的下表,交换。
class Solution {
public void moveZeroes(int[] nums) {
int len=nums.length;
int right=0,left=0;
for(int i=0;i<len;i++){
//right指向第一个为0的数
if(nums[i]!=0){
continue;
}
right=i;
//left指向right之后第一个不为0的数
while(i<len&&nums[i]==0){
i++;
}
if(i<len){
left=i;
i=right;
}else{
break;
}
int temp=nums[right];
nums[right]=nums[left];
nums[left]=temp;
}
}
}
//管解的滑动窗口,厉害
//使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
//右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
//注意到以下性质:
//左指针左边均为非零数;
//右指针左边直到左指针处均为零。
//因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0;
while (right < n) {
if (nums[right] != 0) {
swap(nums, left, right);
left++;
}
right++;
}
}
public void swap(int[] nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
四、二维数组及滚动数组
question 118
answer:
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> res =new ArrayList<>();
for(int i=0;i<numRows;i++){
List<Integer> arr=new ArrayList<>();
for(int j=0;j<=i;j++){
if(j==0||j==i){
arr.add(1);
}else{
arr.add(res.get(i-1).get(j-1)+res.get(i-1).get(j));
}
}
res.add(arr);
}
return res;
}
}
question 119
answer:
可以类比118
class Solution {
public List<Integer> getRow(int rowIndex) {
List<List<Integer>> res=new ArrayList<>();
for(int i=0;i<=rowIndex;i++){
List<Integer> arr=new ArrayList<>();
for(int j=0;j<=i;j++){
if(j==0||j==i){
arr.add(1);
}else{
arr.add(res.get(i-1).get(j-1)+res.get(i-1).get(j));
}
}
res.add(arr);
}
return res.get(rowIndex);
}
}
或者倒着计算
当前行第 i 项的计算只与上一行第 i−1 项及第 ii 项有关。因此我们可以倒着计算当前行,这样计算到第 i 项时,第 i−1 项仍然是上一行的值。
class Solution {
public List<Integer> getRow(int rowIndex) {
List<Integer> row = new ArrayList<Integer>();
row.add(1);
for (int i = 1; i <= rowIndex; ++i) {
row.add(0);
for (int j = i; j > 0; --j) {
row.set(j, row.get(j) + row.get(j - 1));
}
}
return row;
}
}
question 661
answer:
读题读了好久
大概意思为,求该单元格为中心 九个单元格的平均数,边界求周边n 个平均数
class Solution {
public int[][] imageSmoother(int[][] img) {
int m=img.length;
int n=img[0].length;
int[][] res=new int[m][n];
//遍历每一个单元格
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
int sum=0,num=0;
//统计周围九个格
for(int x=i-1;x<=i+1;x++){
for(int y=j-1;y<=j+1;y++){
//判断条件
if(x>=0&&x<m&&y>=0&&y<n){
sum+=img[x][y];
num++;
}
}
}
res[i][j]=sum/num;
}
}
return res;
}
}
question 598
answer:
求交集 最小行
min[ai]*min[bi]
class Solution {
public int maxCount(int m, int n, int[][] ops) {
int row=ops.length;
if(row==0){
return m*n;
}
int max1=Integer.MAX_VALUE;
int max2=Integer.MAX_VALUE;
for(int[] op : ops){
if(op[0]<max1){
max1=op[0];
}
if(op[1]<max2){
max2=op[1];
}
}
return max1*max2;
}
}
question 419
answer 遇到X时 把其所在的行列 紧邻中值为X 的单元格 都设置为 . 计数加一
class Solution {
public int countBattleships(char[][] board) {
int m=board.length;
int n=board[0].length;
int count=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(board[i][j]=='X'){
board[i][j]='.';
for(int x=i+1;x<m&&board[x][j]=='X';x++){
board[x][j]='.';
}
for(int y=j+1;y<n&&board[i][y]=='X';y++){
board[i][y]='.';
}
count++;
}
}
}
return count;
}
}
五、数组的旋转
question 189
answer:
先整体翻转,再翻转左边,再翻转右边。
class Solution {
public void rotate(int[] nums, int k) {
int len=nums.length;
k=k%len;
reverse(nums,0,len-1);
reverse(nums,0,k-1);
reverse(nums,k,len-1);
}
public void reverse(int[] nums,int i,int j){
while(i<j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
i++;
j--;
}
}
}
question 396
answer : f(n)=f(n-1)-nums[n-1]*n+sum
F(1)= A[0]*0 +A[1]*1 +A[2]*2 + ... A[n-2]*(n-2) + A[n-1]*(n-1)
F(2)= A[n-1]*0+ A[0]*1 +A[1]*2 +A[2]*3 + ... A[n-2]*(n-1)
F(2)-F(1)= -A[n-1]*(n-1) + A[0] + A[1] + ... A[n-2]
= -A[n-1]*n +sum
可以看到每次F(n)和F(n-1)之间的差距就是-A[n-1]*n +sum
class Solution {
public int maxRotateFunction(int[] nums) {
int len=nums.length;
int max=0;
int sum=0;
//f(n)=f(n-1)-nums[n-1]*n+sum
//先求f(0),sum
for(int i=0;i<len;i++){
sum+=nums[i];
max=max+i*nums[i];
}
int temp=max;
for(int i=1;i<len;i++){
int lastNum=nums[len-i];
temp=temp-lastNum*len+sum;
if(temp>max){
max=temp;
}
}
return max;
}
}