数组(一)
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年 的智能车之旅,感兴趣的小伙伴也可以一起来讨论讨论!!)