前言
今天是2023年4月19号,大二下了,为之后的就业做准备,开始刷题学习,做笔记打卡,加油!
模板:
题目:
我的思路及解法:
代码:
在这里插入代码片
复杂度分析:
时间复杂度:
空间复杂度:
更优解法:
思路及算法:
代码:
复杂度分析:
时间复杂度:
空间复杂度:
数组篇
题号 | 题目 |
---|---|
1 | 两数之和 |
485 | 最大连续 1 的个数 |
495 | 提莫攻击 |
414 | 第三大的数 |
628 | 三个数的最大乘积 |
645 | 错误的集合 |
697 | 数组的度 |
448 | 找到所有数组中消失的数字 |
442 | 数组中重复的数据 |
41 | 缺失的第一个正数 |
274 | H指数 |
453 | 最小操作次数使数组元素相等 |
665 | |
283 | |
118 | |
119 |
万事开头难,那就以一道简单的题开启刷题之旅吧!
题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
我的思路及解法:我第一下想到就是两重循环,直接暴力枚举,依次遍历每两个元素的组合,把两数之和等于target的下标记录到数组中,返回数组。
代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] arr = new int[2];
for(int i = 0;i<nums.length;i++){
for(int j = i + 1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
arr[0] = i;
arr[1] = j;
return arr;
}
}
}
return arr;
}
}
复杂度分析:
时间复杂度:O(
N
2
N^2
N2),其中N是数组中的元素数量。最坏的情况下数组中的任意两个数都要被匹配一次。
空间复杂度:O(1)
更优解法:哈希表
思路及算法:
注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。
使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N)降低到O(1).
这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; ++i) {
if (hashtable.containsKey(target - nums[i])) {
return new int[]{hashtable.get(target - nums[i]), i};
}
hashtable.put(nums[i], i);
}
return new int[0];
}
}
复杂度分析:
时间复杂度:O(N),其中N是数组中的元素数量。对于每一个元素 x,我们可以 O(1)
地寻找 target - x。
空间复杂度:O(N),其中N是数组中的元素数量,主要为哈希表的开销.
题目:给定一个二进制数组 nums , 计算其中最大连续 1 的个数。
我的思路及解法:直接用一次遍历,设置一个maxCount和max,记录当前出现连续1的个数和最大连续1的个数。当前元素为1时,maxCountt++,当nums[i]为0时,将记录到的连续1个数maxCountt与max比较,count>max的话则更新max,maxCount重置为0,继续遍历。遍历结束,返回最大值max。
代码:
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int maxCount=0;
int max=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==1){
maxCount++;
}else{
max=Math.max(maxCount,max);
maxCount=0;
}
max=Math.max(maxCount,max);
}
return max;
}
}
复杂度分析:
时间复杂度:O(n),其中 n 是数组的长度。需要遍历数组一次。
空间复杂度:O(1)
题目:
在《英雄联盟》的世界中,有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希(编者注:寒冰射手)进入中毒状态。
当提莫攻击艾希,艾希的中毒状态正好持续 duration 秒。
正式地讲,提莫在 t 发起攻击意味着艾希在时间区间 [t, t + duration - 1](含 t 和 t + duration - 1)处于中毒状态。如果提莫在中毒影响结束 前 再次攻击,中毒状态计时器将会 重置 ,在新的攻击之后,中毒影响将会在 duration 秒后结束。
给你一个 非递减 的整数数组 timeSeries ,其中 timeSeries[i] 表示提莫在 timeSeries[i] 秒时对艾希发起攻击,以及一个表示中毒持续时间的整数 duration 。
返回艾希处于中毒状态的 总 秒数。
timeSeries[i]>poisoned
我的思路及解法:
对数组进行一次遍历便能计算出中毒的持续时间
- 如果他当前处于未中毒状态时(timeSeries[i]>poisoned),则此时他中毒的持续时间应增加duration,同时更新此次中毒的结束时间为poisoned=timeSeries[i]+duration。
- 如果他当前处于中毒状态(即上一次中毒的结束时间在此次再中毒时刻之前,timeSeries[i]<poisoned),本次再中毒的结束时间为timeSeries[i]+duration,上一次中毒的结束时间为poisoned,因此本次增加的中毒时间为timeSeries[i]+duration-poisoned,继续更新本次中毒结束时间,poisoned=timeSeries[i]+duration。
- 将每次中毒后增加的持续时间相加即为总中毒时间。
代码:
class Solution {
public int findPoisonedDuration(int[] timeSeries, int duration) {
int toxicTime=0;//中毒总时间
int poisoned=0;//恢复为 未中毒/中毒结束的时间
for(int i=0;i<timeSeries.length;i++){
if(timeSeries[i]>poisoned){
toxicTime+=duration;
poisoned=timeSeries[i]+duration;
}else{
toxicTime+=timeSeries[i]+duration-poisoned;
poisoned=timeSeries[i]+duration;
}
}
return toxicTime;
}
}
复杂度分析:
时间复杂度:O(n),n为数组timeSeries的长度,只需遍历一次数组即可
空间复杂度:O(1)
题目:给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。
我的思路及解法:题目需要排序和去重,所以我第一就想到了用TreeSet集合。TreeSet不包含重复元素,且元素按照自然排序进行排序,这里是升序(如1,2,3,4).
每次添加元素到集合中,集合中的第一个元素是最低的,最后一个元素是最高的。
若添加元素到集合中使集合长度大于3,则每次都删除掉排序后的第一个元素,使集合长度一直保持为3,遍历结束时返回的.first()即第三大元素。
若集合长度小于3,则返回的.last()即最大元素
代码:
class Solution {
public int thirdMax(int[] nums) {
TreeSet<Integer> ts=new TreeSet<Integer>();
for(int i:nums){
ts.add(i);
if(ts.size()>3){
ts.remove(ts.first());
}
}
return ts.size()==3?ts.first():ts.last();
}
}
复杂度分析:
时间复杂度:O(n),n为nums数组的长度
空间复杂度:O(1)
题目:给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。
我的思路及解法:对数组进行从小到大排序
当数组中有正数和负数的时候,要看绝对值的大小,例如-3,-2,-1,1,2,3
负数越小绝对值越大,最大值为max(两个最小负数之积×最大整数,3个最大正数之积)
代码:
class Solution {
public int maximumProduct(int[] nums) {
Arrays.sort(nums);
int n=nums.length;
return Math.max(nums[0]*nums[1]*nums[n-1],nums[n-3]*nums[n-2]*nums[n-1]);
}
}
复杂度分析:
时间复杂度:O(N logN),其中N为数组的长度,排序需要O(N logN)的时间
空间复杂度:O(logN),主要为排序的空间开销
题目:集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
给定一个数组 nums 代表了集合 S 发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
我的思路及解法:找重复元素:遍历数组,前后两个数值相等时该数值即为重复元素。
找缺失元素:由题可知正常情况下数组是连续的。定义两个“指针”,prev=0,tail为数组首元素。通过遍历数组两指针向后顺移。
1<=缺失元素<n:当tail-prev=2时,缺失元素即为tail-1;
缺失元素==n时,即nums[n-1]!=n,缺失元素即为n
代码:
class Solution {
public int[] findErrorNums(int[] nums) {
int i;
int[] errorSet={0,0};
int prev=0;
int tail=0;
Arrays.sort(nums);
for(i=0;i<nums.length;i++){
tail=nums[i];
if(tail==prev){
errorSet[0]=prev;
}
if(tail-prev>1){
errorSet[1]=tail-1;
}
prev=tail;
}
if(nums[i-1]!=i){
errorSet[1]=i;
}
return errorSet;
}
}
复杂度分析:
时间复杂度:O(n logn),其中n是数组num的长度。排序需要O(nlogn)的时间,遍历数组找到错误的集合需要O(n)的时间,所以总时间复杂度为O(nlogn)
空间复杂度:O(logn),其中n是数组num的长度,排序需要O(logn)的空间
题目:给定一个非空且只包含非负数的整数数组 nums,数组的 度 的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
我的思路及解法:很惭愧,这道难度为简单的题我都不会做,想了好久有思路就是不知道怎么用代码写出来,看来还是我的语言基础太脆弱了,一时不知道要用到哪个知识点,看了题解才豁然开朗,顺便复习了一下map的用法。这道题我是学着题解的思路写的,虽然不是自己想出来的但是学习别人的东西,学进了也就是自己了的吧。以下是题解的思路和解法。
代码:
class Solution {
public int findShortestSubArray(int[] nums) {
int max=0;
int minLen=0;
Map<Integer,int[]> map=new HashMap<Integer,int[]>();
for(int i=0;i<nums.length;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});
}
}
Set<Integer> keyset=map.keySet();
for(Integer key:keyset){
if(max<map.get(key)[0]){
max=map.get(key)[0];
minLen=map.get(key)[2]-map.get(key)[1]+1;
}else if(max==map.get(key)[0]&&(map.get(key)[2]-map.get(key)[1]+1)<minLen){
minLen=map.get(key)[2]-map.get(key)[1]+1;
}
}
return minLen;
}
}
复杂度分析:
时间复杂度:O(n),其中n为原数组的长度,我们需要遍历原数组和哈希表各一次,他们的大小均为O(n)
空间复杂度:O(n),其中n为原数组的长度,最坏情况下,哈希表和原数组等大。
模板:
题目:
我的思路及解法:看到这道题,我第一秒想到的就是暴力解法哈哈哈,我的能力只允许我想到暴力解法了。已知题目:nums[i]在[1,n]之中,我设置了双重循环,外层是[1,n]里的数字,每次取一个数字i,遍历所有数组中的元素,若没有匹配上则说明缺失了i,将他添加入list集合中,最后将集合返回,思路很简单,代码也很简单,一次执行就能通过,但是数据量一大起来消耗时间太长了,leetcode的控制台直接显示超出时间限制了,我自己在idea上面是可行的,可是消耗时间成本太大了,我先将他传上来然后再想一下怎么优化吧。
代码:
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> missingList=new ArrayList();
int count=0;
int flag=0;
for(int i=1;i<=nums.length;i++){
flag=0;
for(int j=0;j<nums.length;j++){
if(nums[j]==i){
flag=1;
break;
}else{
continue;
}
}
if(flag==0){
missingList.add(i);
}
}
return missingList;
}
}
复杂度分析:
时间复杂度:O(
N
2
N^2
N2),双重循环,n为数组长度。
空间复杂度:O(1)
更优解法:原地修改
思路及算法:参考leetcode题库448官方解答
代码:
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> missingList=new ArrayList();
int n=nums.length;
int x=0;
int i=0;
for(i=0;i<n;i++){
x=(nums[i]-1)%n;
nums[x]+=n;
}
for(i=0;i<n;i++){
if(nums[i]<=n){
missingList.add(i+1);
}
}
return missingList;
}
}
复杂度分析:
时间复杂度:O(n),其中n是数组nums的长度
空间复杂度:O(1),返回值不计入空间复杂度
题目:
我的思路及解法:第一次自己做出了中等题,好有成就感,虽然这道中等难度的题应该是中等里面的下等,不过还是很开心哈哈哈哈,美中不足的就是没有符合题目中对空间复杂度的限制了。下面说一下我的思路。
题目中说每个整数出现一次或两次,那么也有一些整数是没有出现的。
设定:整数x在数组上的位置为nums[x-1],nums[x-1]的值正负表示该整数的出现频率。
定义一个跟原数组等长的新数组copyNums,遍历数组nums,取nums[i]的值的绝对值,寻找该整数在数组上的位置,令该位置上的值乘-1,并将其添加到数组中copyNums,若该位置再次被访问,则说明该元素出现了两次(再乘-1为正),更新其在copyNums中的值。
copyNums中,copyNums[j]大于0的表示整数(j+1)出现两次,小于0的表示出现1次,等于0的表示未出现。
遍历copyNums,取大于0的元素下标j+1,添加到res中,返回res
代码:
class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res=new ArrayList();
int n=nums.length;
int z=0;
int[] copyNums=new int[n];
for(int i=0;i<n;i++){
z=nums[i]>0?nums[i]-1:(-nums[i]-1);
nums[z]=-nums[z];
copyNums[z]=nums[z];
}
for(int j=0;j<n;j++){
if(copyNums[j]>0){
res.add(j+1);
}
}
return res;
}
}
复杂度分析:
时间复杂度:O(n),n为原数组长度
空间复杂度:O(n),n为原数组长度
更优解法:使用正负号作为标记
思路及算法:在官方解答下找到了同样用正负号作为标记的解法,而且完美解决了空间复杂度O(n)的问题
代码:
class Solution {
public List<Integer> findDuplicates(int[] nums) {
int n = nums.length;
List<Integer> ans = new ArrayList<Integer>();
for (int i = 0; i < n; ++i) {
int x = Math.abs(nums[i]);
if (nums[x - 1] > 0) {
nums[x - 1] = -nums[x - 1];
} else {
ans.add(x);
}
}
return ans;
}
}
复杂度分析:
时间复杂度:O(n),n为数组长度
空间复杂度:O(1)。
目前看到的最优解法:元素和数组长度进行比较
思路及算法:先遍历数组,每遍历一个数nums[i]就给数组中第nums[i]个数加上数组的长度值。例如[2,1,1],遍历完以后是[2+3+3,1+3,1]也就是[8,4,1]。这个8的出现是因为原数组有两个1,使得遍历后该数组第1个位置的值2加了两次3,所以答案就是1,所以只要知道数组中哪个位置的值大于两倍的数组长度,就知道该位置的顺序值是在数组中出现两次的。
而且这个方法对出现三次四次及以上次数的判断都是通用的,评论区第一的方法对出现奇数次的情况就不适用了。
代码:
class Solution {
public List<Integer> findDuplicates(int[] nums) {
int n=nums.length;
for(int i=0;i<n;i++){
nums[(nums[i]-1)%n]+=n;
//System.out.print(nums[i]);
}
List<Integer> ret=new ArrayList<Integer>();
for(int i=0;i<n;i++){
if (nums[i]>2*n){
ret.add(i+1);
}
}
return ret;
}
}
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(1)
题目:
我的思路及解法:
由题可知,将数组进行排序之后,正常情况下数组的情况为[1…n]。
第一次遍历,取出第i个元素,若第i个元素的值与nums[nums[i]-1]不相同,则与原来该位置上的元素进行交换,交换过后继续对第i个位置上的元素进行判断。否则i++,继续遍历下一个位置上的元素。如果出现nums[i]>nums.length,nums[i]<=0,nums[i]=i+1(该情况说明该元素在正确的位置上)则i++,继续遍历下一个元素。
第一次遍历结束后,此时在[1…n]范围之内的每个元素都在他们的正确位置上了。如示例2经过第一次while遍历之后,nums[1,-1,3,4];
再次遍历,若出现nums[j]!=j+1,则return j+1。该值为没有出现的最小的正整数。
如果数组每个元素都存在[1,n]。则返回n+1
代码:
class Solution {
public int firstMissingPositive(int[] nums) {
int n=nums.length;
int i=0;
int x=0;
int j=0;
while(i<n){
if(nums[i]==i+1||nums[i]<=0||nums[i]>=n){
i++;
continue;
}else{
x=nums[i];
if(x!=nums[x-1]){
nums[i]=nums[x-1];
nums[x-1]=x;
}
else{
i++;
continue;
}
}
}
for(j=0;j<n;j++){
if(nums[j]!=j+1){
return j+1;
}
}
return j+1;
}
}
复杂度分析:
时间复杂度:O(n),n为数组的长度
空间复杂度:O(1)
题目:
我的思路及解法:
首先将初始的H指数的值h设为0,然后将引用次数排序,并且对排序后的数组从大到小遍历。
根据H指数的定义,如果当前H指数为h并且在遍历过程中找到当前值citations[i]>h,则说明我们找到了一篇被引用了至少h+1次的论文,所有将h值加1,继续遍历直到h无法继续增大,最后返回h作为最终答案。
代码:
class Solution {
public int hIndex(int[] citations) {
Arrays.sort(citations);
int h = 0, i = citations.length - 1;
while (i >= 0 && citations[i] > h) {
h++;
i--;
}
return h;
}
}
复杂度分析:
时间复杂度:O(n logn),其中n为数组长度,即为排序的时间复杂度。
空间复杂度:O(logn),其中n为数组长度,即为排序的空间复杂度。
题目:
我的思路及解法:
想了半天没想出来,一看官解我是shaX。。。
果然数学还是太差了,又学到了新知识,当求一件事件很难时,就想着求他的对立事件。
代码:
class Solution {
public int minMoves(int[] nums) {
int minNum = Arrays.stream(nums).min().getAsInt();
int res = 0;
for (int num : nums) {
res += num - minNum;
}
return res;
}
}
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/minimum-moves-to-equal-array-elements/solution/zui-xiao-cao-zuo-ci-shu-shi-shu-zu-yuan-3meg3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析:
时间复杂度:O(n),其中n为数组中的元素数量,我们需要一次遍历求出最小值,一次遍历计算操作次数。
空间复杂度:O(1)
更优解法:
思路及算法:
代码:
复杂度分析:
时间复杂度:
空间复杂度: