LeetCode 001 数组(一)系列

数组(一)

LeetCode是个很好的刷题网站,但是我们应该怎么刷呢?盲目的刷题不仅效率不高,覆盖的知识面也非常有限,所以从今天开始我会陆续分享自己做题的路线,希望此时在看我博客的你能有所方向,我们一起加油!多说无益,撸起袖子开始干!
内容用Java语言编写

数组(一)系列题型如下
在这里插入图片描述

数组的遍历(485)

485题:最大连续 1 的个数

给定一个二进制数组, 计算其中最大连续 1 的个数
示例:
输入:[1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
提示
输入的数组只包含 0 和 1 。 输入数组的长度是正整数,且不超过 10,000。

暴力解法(MyCode)

看到这类数组的遍历问题,我想到的第一个解法就是暴力解决
比较大小可以用此Math函数 max = Math.max(mid, max);
或者使用三目运算 max=mid>max?mid:max;

/**
* max 返回连续1的最大数量
* mid 中间比较大小的介质
*/
class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int max=0,mid=0;     
        for(int i=0;i<nums.length;i++){
            if(nums[i]==1)
                mid++;
            else {
                if(mid>max){
                    max=mid;
                }
                mid=0;
            }
        }
        if(mid>max)
            max=mid;
        return max;
    }
}

 执行用时:2 ms, 在所有 Java 提交中击败了89.40%的用户 
 内存消耗:40.4 MB, 在所有 Java提交中击败了5.03%的用户'
双指针(Other’s Code)

先献上我对这个代码的思路和想法,我觉得双指针算法思路挺不错!
在这里插入图片描述

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int n = nums.length;
        int ans = 0;
        for (int i = 0, j = 0; i < n; j = i) {
            if (nums[i] == 1) {
                while (j + 1 < n && nums[j + 1] == 1) j++;
                ans = Math.max(ans, j - i + 1);
                i = j + 1;
            } else {
                i++;
            }
        }
        return ans;
    }
}

统计数组中的元素(697)(442)

697题:数组的度

给定一个非空且只包含非负数的整数数组 nums,数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
示例 1
输入:[1, 2, 2, 3, 1]
输出:2
解释:输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.
示例 2:
输入:[1,2,2,3,1,4,2] 输出:6
提示:
nums.length 在1到 50,000 区间范围内。
nums[i] 是一个在 0 到 49,999 范围内的整数。

哈希表(MyCode)

先介绍一下哈希表的基础知识
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 HashMap 实现了 Map
接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。 HashMap
是无序的,即不会记录插入的顺序。 HashMap 继承于AbstractMap,实现了
Map、Cloneable、java.io.Serializable 接口。

通俗的讲就是通过哈希表生成key-value一一对应的关系
HashMap<Integer, int[]> map = newHashMap<Integer, int[]>();
生成 key为integer类型的值对应int 类型的数组(value)
使用数组的目的就是想达到一对多的作用类似函数的映射关系

/**
* 我谈谈看到这道题的感受,首先这道题特别不好理解,实现的难度也很大,我一开始是想用链表去实现更加方便,但是
* 我的能力很有限,最后我决定由HashMap解决
* 首先我想到的是就是要将度,首索引和尾索引存到HashMap中的value,便想到运用数组。
* 我遇到了一个问题现在还是不能明白如下
* map.get(nums[i])[0]=1;
* map.get(nums[i])[1]=i; 
* map.get(nums[i])[2]=i;
* 以上三条语句和底下这一条有什么区别吗?
* map.put(nums[i], new int[]{1, i, i});
* 错误提示:java.lang.NullPointerException
* 经过我的查找:
* NullPointerException即空指针异常,俗称NPE。如果一个对象为null,调用其方法或访问其字段就会产生这个异常
* 问题出在给数组初始化的问题,出现数组引用对象为null
* 遍历解法有点暴力解决的做法,然后我开始搜索别人的代码,多去学习学习!!
*/

class Solution {
    public int findShortestSubArray(int[] nums) {
        HashMap<Integer, int[]> map = new HashMap<Integer, int[]>();
        //将度,首索引和尾索引填入到HashMap 
        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});
            }   
        }
        //遍历HashMap
        int maxdegree=0,minlen=0;
        int[] len = new int[50000];
        for(int i=0;i<nums.length;i++){
            len[nums[i]]=map.get(nums[i])[2]-map.get(nums[i])[1]+1;
            if(map.containsKey(nums[i])){
                if(maxdegree<map.get(nums[i])[0]){
                    maxdegree=map.get(nums[i])[0];
                    minlen=len[nums[i]];
                }else if(maxdegree==map.get(nums[i])[0]){
                    if(len[nums[i]]<minlen){
                        minlen=len[nums[i]];
                    }
                }
            }
        }
        return minlen;
    }
}

执行用时:28 ms, 在所有 Java 提交中击败了28.91%的用户
内存消耗:43 MB, 在所有 Java 提交中击败了41.46%的用户

数组法(Other’s Code)

不知名大佬的2ms模板,超越了百分百,多看一些大佬的代码,确实很棒!感觉很受用!必须赞!!!

/**
*  看到代码的第一眼就发现了这两条代码比较好奇,我承认我还是第一次看到
*  int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
*  解释如下:
*  在JDK中,整形类型是有范围的,最大值为Integer.MAX_VALUE,即2147483647,
*  最小值为Integer.MIN_VALUE -2147483648。对整形最大值加1,2147483648(越界了)
*  那么此时值为多少呢?结果是-2147483648,即是Integer.MIN_VALUE。
*  
*/
class Solution {
    public int findShortestSubArray(int[] nums) {
        int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
        //数组遍历foreach
        for (int i : nums) {
        	//得到数组的最大值和最小值的边界
            max = Math.max(i, max);
            min = Math.min(i, min);
        }
        //用于记录数组对应的度
        int[] counts = new int[max - min + 1];

        int countMax = 0;
        for (int i : nums) {
        	//++counts[i - min] 先自增1,然后将counts[i - min]代入运算
            countMax = Math.max(countMax, ++counts[i - min]);
        }
        if (countMax == 1) return 1;
        
        // 遍历统计数组
        int minLength = nums.length;
        for (int i = 0; i < counts.length; i++) {
            if (counts[i] == countMax) {
                int tmp = min + i;
                //通过start 和 end 分别从前后找到对应数的数组长度
                int start = 0, end = nums.length - 1;
                while (start < end && nums[start] != tmp) {
                    start++;
                }
                while (start < end && nums[end] != tmp) {
                    end--;
                }
                minLength = Math.min(minLength, end - start + 1);
            }
        }
        return minLength;
    }
   

442题:数组中重复的数据

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入: [4,3,2,7,8,2,3,1]
输出: [2,3]

重复标记法(Other’s Code)

看到这道题,表面上看起来很简单,但是当真正去满去“你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?”这个条件的时候,发现有点迟钝和迷惑,想了很久还是无果,可能思维定势了!

/*
*  首先讲一下这个语句,我对这个去决定值表示异议,因为1 ≤ a[i] ≤ n ,那这个取绝对值不是
*  多此一举,但是当去掉绝对值的时候,报错原因就是有负数输入;然后为啥是减去1呢?这个就要
*  考虑数组下标越界的问题0~num[n]-1
*  int numFlag = Math.abs(nums[i]) - 1;
*  接下来就是区分是否重复出现,通过取反就可以标志出现过一次,但是这个时候我又有疑惑了,   *  题中提到的不是出现2次吗?这只能判断出现多次呀!所以纠结的地方还挺多!!
*  (如果有大佬帮我一下,我不甚感激!!)
*  nums[numFlag] = -nums[numFlag];
*  最后就是多提一句就是add() 方法将元素插入到指定位置的动态数组中。
*  countTwo.add(numFlag + 1);
*/
class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        List<Integer> countTwo = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            int numFlag = Math.abs(nums[i]) - 1;
            if (nums[numFlag] > 0) {
                nums[numFlag] = -nums[numFlag];
            } else {
                countTwo.add(numFlag + 1);
            }
        }
        return countTwo;
    }
}

执行用时:7 ms, 在所有 Java 提交中击败了76.21%的用户
内存消耗:47.1 MB, 在所有 Java 提交中击败了82.68%的用户
计数排序(MyCode)

唯一不足的是违背了题意,但是创造接近100%执行效率以及没有我上面对问题

/*
*  这里我就不多说了,应该特别好理解吧!
*/
class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        List<Integer> list = new ArrayList<>();
        int[] countTwo = new int[nums.length + 1];
        for(int i = 0; i<nums.length;i++){
            countTwo[nums[i]]++;
        }
        for (int i = 1; i <= nums.length; i++) {
            if (countTwo[i] == 2) 
                list.add(i);
        }
        return list;
    }
}

执行用时:4 ms, 在所有 Java 提交中击败了99.95%的用户
内存消耗:47.4 MB, 在所有 Java 提交中击败了44.70%的用户

数组的改变和移动(665)

665题:非递减数列

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]
示例 1:
输入: nums = [4,2,3] 输出: true 解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
示例 2:
输入: nums = [4,2,1] 输出: false 解释: 你不能在只改变一个元素的情况下将其变为非递减数列。
提示:

  • 1 <= n <= 10 ^ 4
  • 10 ^ 5 <= nums[i] <= 10 ^ 5
比较法(MyCode)

看到这类题型,得理解非递增数列,就是相邻的数组元素之间都是递增或者不增的数列,看起来简单但是思考起来还是磕磕碰碰!!

//重点理解这句
 if (i > 0 && nums[i + 1] < nums[i - 1]) {
           nums[i + 1] = nums[i];
 }
class Solution {
    public boolean checkPossibility(int[] nums) {
        int turn = 0;  
        for (int i = 0; i < nums.length - 1; ++i) {
            if (nums[i] > nums[i + 1]) {
                turn++;
                if (turn > 1) {
                    return false;
                }
                if (i > 0 && nums[i + 1] < nums[i - 1]) {
                    nums[i + 1] = nums[i];
                }
            }
        }
        return true;
    }
}

执行用时:1 ms, 在所有 Java 提交中击败了99.98%的用户
内存消耗:39.5 MB, 在所有 Java 提交中击败了97.22%的用户
贪心法(Other’s Code)

大佬的想法很清晰,就是抓住中间过程的"下降"类似四边形的顶点 则返回False
如果存在i,使的nums[i+1]<nums[i],我们就说出现了一次“下降”

分3种情况:
1、不存在下降的情况,即下降次数为0,返回true。

2、只出现了1次下降,将下降后的元素记为nums[x]

此时,可以尝试将nums[x-1]变小,或者将nums[x]变大,以达到“非递减”的目的。
如果x=1或者x=n-1,只需要将nums中第一个元素减小(最差到Integer.MIN_VALUE),
                 或者最后一个元素增大总能满足要求;
如果1<x<n-1,若将nums[x]变大必须要求nums[x-1]<=nums[x+1]
                 亦即,x前后的两个元素不能再“捣乱”。
             若将nums[x-1]变小,须要求nums[x-2]<=nums[x]
                 亦即,x-1前后的两个元素不能再“捣乱”。

3、出现超过1次的下降,肯定不同只通过调整一个元素完成要求,返回false。

class Solution {
	public boolean checkPossibility(int[] nums) {
	    int n = nums.length;
	    if (n <= 1) return true;
	    int down = 0;
	    for (int i = 1; i < n; i++) {
	        if (nums[i] < nums[i - 1]) {
	            down++;
	            if (down > 1) {
	                return false;
	            }
	            if (i > 1 && i < n - 1 && nums[i - 1] > nums[i + 1] && nums[i - 2] > nums[i]) {
	                return false;
	            }
	        }
	    }
	    return true;
	}
}

二维数组及滚动数组(119)

119题:杨辉三角 II

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
输入: 3
输出: [1,3,3,1]
进阶:你可以优化你的算法到 O(k) 空间复杂度吗?
在这里插入图片描述

递归算法(MyCode)

在这里插入图片描述由于超出运算时间的限制,我也不知道我的代码有没有问题,但是我还是想要分享出来,大家一起看一看,可能有不同的思想碰撞,每个人都不应该吝啬自己的思想,才能创造出更好的算法!!

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> listHead = new ArrayList<>();
        List<Integer> listTail = new ArrayList<>();
        if(rowIndex == 0){
            listTail.add(1);
        }if(rowIndex == 1){
            listTail.add(1);
            listTail.add(1);
        }else {
            listHead = getRow(rowIndex-1);
            listTail.add(1);
            for(int i = 1;i<rowIndex;i++){
                int sum = listHead.get(i-1)+listHead.get(i);
                listTail.add(sum);
            }
            listTail.add(1);
        }
        return listTail;
    }
}
线性递推(Other’s Code)

这个是官方的解题思路,我觉得我的境界还差的太远他用了一条核心代码诠释了什么是大神!! – row.add((int) ((long) row.get(i - 1) * (rowIndex - i + 1) / i));

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((int) ((long) row.get(i - 1) * (rowIndex - i + 1) / i));
        }
        return row;
    }
}

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.9 MB, 在所有 Java 提交中击败了96.97%的用户

个人总结

回想当初,刚进大学,懵懂无知,想着怎么多把课程成绩学到最好以及加入学生会的想法,我觉得真正学到的东西太少了,现在作为大二的我开始刷题,我觉得也为时不晚,所以我开始我的LeetCode的刷题之路,我想的是每周做5道同时记录在博客,再次大家做个见证,感兴趣的也可以加入我 的队伍中,我们一起前进!!加油!!

(闲谈:我也参加十六届智能车竞赛,并且是本校智能车队长,所以后续我还会发我的2年 的智能车之旅,感兴趣的小伙伴也可以一起来讨论讨论!!)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Al_tair

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值