一、448. Find All Numbers Disappeared in an Array

给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
输入:[4,3,2,7,8,2,3,1]
输出:[5,6]

  1.算法思路(本题关键点:注意数组下标的范围和要求的数之间有什么联系。

  既然要遍历[1,n]中的数,由于数组中的长度已经保证了为n,因此数组的下标加上1就正好满足从1到n,因此,要对下标做文章。

  2.代码实现

  将[4,3,2,7,8,2,3,1]变成[-4,-3,-2,-7,8,2,-3,-1]然后遍历到8和2这两个正数时,就将数组下标加上1就是要求的结果了。

    public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
int val = Math.abs(nums[i]) - 1;
if (nums[val] > 0) nums[val] = -nums[val];// 如果不进行判断,那么2和3就会反转两次,还是变成2和3,最后结果为[2,3,5,6]
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) list.add(i+1);
}
return list;
}

  

  二、283. Move Zeroes

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

  1.算法思路:双指针,前一个指针指的是数组的索引,后一个指针指的是非零元素的索引

  2.代码实现

  (1)最普通的思路最清晰的双指针

    public void moveZeroes(int[] nums) {
int prev = 0;
int curr = 0;
while (curr < nums.length) {
if (nums[prev] == 0 && nums[curr] != 0) {
nums[prev] = nums[curr];
nums[curr] = 0;
prev++;
curr++;
} else if (nums[prev] != 0 && nums[curr] == 0) {
prev++;
curr++;
} else if (nums[prev] != 0 && nums[curr] != 0) {
prev++;
curr++;
} else {
curr++;
}
}
}

Low Code

  (2)高级的双指针写法1,(关键点:nums[lastNonZeroFoundAt++]的写法)同样使用指针lastNonZeroFoundAt和指针i。

void moveZeroes(vector<int>& nums) {
int lastNonZeroFoundAt = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0) nums[lastNonZeroFoundAt++] = nums[i];
}
for (int i = lastNonZeroFoundAt; i < nums.size(); i++) nums[i] = 0;
}

  (3)高级的双指针写法2

void moveZeroes(vector<int>& nums) {
for (int lastNonZeroFoundAt = 0, cur = 0; cur < nums.size(); cur++) {
if (nums[cur] != 0) swap(nums[lastNonZeroFoundAt++], nums[cur]);
}
}

  三、(二维数组--九宫格问题)661. 图片平滑器

输入:
[[1,1,1],
[1,0,1],
[1,1,1]]
输出:
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
解释:
对于点 (0,0), (0,2), (2,0), (2,2): 平均(3/4) = 平均(0.75) = 0
对于点 (0,1), (1,0), (1,2), (2,1): 平均(5/6) = 平均(0.83333333) = 0
对于点 (1,1): 平均(8/9) = 平均(0.88888889) = 0

  1.算法思路

  之前的思路是为了防止计算的时候数周围的数没有九个,因此建立一个比原始矩阵大一圈的矩阵,然后将原来的矩阵复制到大矩阵的中间。也就是在原矩阵四周圈上一圈0;

  但是有可以不用新建矩阵的方法。(关键点:在做加法之前先判断索引是否有效,如果有效才进行加法计算)

  2.代码实现

    public int[][] imageSmoother(int[][] M) {
int[][] res = new int[M.length][M[0].length];
for (int i = 0; i < M.length; i++) {
for (int j = 0; j < M[0].length; j++) {
int count = 0;
for (int x = i - 1; x <= i + 1; x++) {
for (int y = j - 1; y <= j + 1; y++) {
if (0 <= x && x < M.length && 0 <= y && y < M[0].length) {
res[i][j] += M[x][y];
count++;
}
}
}
res[i][j] /= count;
}
}
return res;
}

  四、(双指针问题,单个指针for循环)830. Positions of Large Groups

Input: "abcdddeeeeaabbbcd"
Output: [[3,5],[6,9],[12,14]]

  1.解法思路

  使用prev指针指向子字符串的起始位置,使用curr指向子字符串的终止位置,如果curr和prev的距离相差3就添加到列表中。

  2.代码实现(关键点:prev待定,curr指针for循环,如果当前字符和下一字符相等就什么也不做,for循环里面curr会自动加1向后移动

    public List<List<Integer>> largeGroupPositions(String S) {
int prev = 0;
List<List<Integer>> result = new ArrayList<>();
for (int curr = 0; curr < S.length(); curr++) {
if (curr == S.length() - 1 || S.charAt(curr) != S.charAt(curr+1)) {
if (curr-prev+1 >= 3) {
result.add(Arrays.asList(new Integer[] {prev, curr}));
}
prev = curr + 1;
}
}
return result;
}

  五、628. Maximum Product of Three Numbers

Input: [1,2,3,4]
Output: 24

  1.解法思路

  (1)常规算法,排序,由于可能存在负数的原因,而两个负数乘起来就是负数,例如{-4,-3,-2,-1,60},因此需要比较排序后的Math.max(nums[nums.length-1] * nums[nums.length-2] * nums[nums.length-3], nums[0] * nums[1] * nums[nums.length-1]);

  时间复杂度O(nlog(n)),空间复杂度O(log(n)

  (2)更快的算法,按照自己的规则手动进行排序,时间复杂度O(n),空间复杂度O(1)

    public int maximumProduct(int[] nums) {
int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE;
int max1 = Integer.MIN_VALUE, max2 = Integer.MIN_VALUE, max3 = Integer.MIN_VALUE;
for (int n: nums) {
if (n <= min1) {
min2 = min1;
min1 = n;
} else if (n <= min2) { // n lies between min1 and min2
min2 = n;
}
if (n >= max1) { // n is greater than max1, max2 and max3
max3 = max2;
max2 = max1;
max1 = n;
} else if (n >= max2) { // n lies betweeen max1 and max2
max3 = max2;
max2 = n;
} else if (n >= max3) { // n lies betwen max2 and max3
max3 = n;
}
}
return Math.max(min1 * min2 * max1, max1 * max2 * max3);
}

  六、(数组插入问题)(三指针问题)88. Merge Sorted Array

Input:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3 Output: [1,2,2,3,5,6]

  1.解题思路

  由于数组插入数据的困难性,再要求将nums中的数插入到nums1的合适位置中时,可以从后往前比较,将最大的数放在nums1的最末位,一次比较。

  2.解题代码(注意点:如果nums2中的数全都先于nums1插入完成,那么nums1中无序改变了;如果nums1中的数全部都移向了nums1的后面,那么nums1前面的数将由nums2中的数依次填充。

    public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1;
int j = n - 1;
int k = m + n - 1;
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
else nums1[k--] = nums2[j--];
}
while (j >= 0) nums1[k--] = nums2[j--];
}

  七、(放花问题)605. Can Place Flowers

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True
输入: flowerbed = [1,0,0,0,1], n = 2
输出: False

  1.解题思路(关键点:找出位置之后,直接种上花,就不用再烦恼连续的0能种多少花的问题。)

  和前面的二维数组的边界问题一样,可以在if判断语句上面添加对索引的限制。

  遍历一次,如果存在值为0且左右都为0,那么count加1并且将该位置设为1,即种上花。

  2.代码实现(关键点:解决第一个位置和最后一个位置的问题,这种关系表达式的表达技巧

    public boolean canPlaceFlowers(int[] flowerbed, int n) {
int count = 0, i = 0;
while (i < flowerbed.length) {
if (flowerbed[i] == 0 &&
(i == 0 || flowerbed[i-1] == 0) &&
(i == flowerbed.length-1 || flowerbed[i+1] == 0)) {
count++
;
flowerbed[i] = 1;
}
i++;
}
return count <= n;
}

  七点五、(坐座位问题)849. Maximize Distance to Closest Person

输入:[1,0,0,0,1,0,1]
输出:2
解释:如果亚历克斯坐在第二个空位(seats[2])上,他到离他最近的人的距离为 2 。果亚历克斯坐在其它任何一个空位上,他到离他最近的人的距离为 1 。
因此,他到离他最近的人的最大距离是 2 。

  1.思路:和上面的养花问题一样,无非就是寻找距离1最远距离的位置,同样存在边界问题。

  2.解法

    public int maxDistToClosest(int[] seats) {
int N = seats.length;
int prev = -1, future = 0;
int ans = 0; for (int i = 0; i < N; ++i) {
if (seats[i] == 1) {
prev = i;
} else {
while (future < N && seats[future] == 0 || future < i)
future++; int left = prev == -1 ? N : i - prev;
int right = future == N ? N : future - i;
ans = Math.max(ans, Math.min(left, right));
}
} return ans;
}

双指针解法

    public int maxDistToClosest(int[] seats) {
int N = seats.length;
int K = 0; //current longest group of empty seats
int ans = 0; for (int i = 0; i < N; ++i) {
if (seats[i] == 1) {
K = 0;
} else {
K++;
ans = Math.max(ans, (K + 1) / 2);
}
} for (int i = 0; i < N; ++i) if (seats[i] == 1) {
ans = Math.max(ans, i);
break;
} for (int i = N-1; i >= 0; --i) if (seats[i] == 1) {
ans = Math.max(ans, N - 1 - i);
break;
} return ans;
}

按0分组解法

  

  八、(最短无序连续子数组)581. Shortest Unsorted Continuous Subarray(TODO)

输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。

  1.思路:

  九、(int和Integer的问题)414. Third Maximum Number

Input: [3, 2, 1]
Output: 1
Explanation: The third maximum is 1. Input: [1, 2]
Output: 2
Explanation: The third maximum does not exist, so the maximum (2) is returned instead. Input: [2, 2, 3, 1]
Output: 1
Explanation: Note that the third maximum here means the third maximum distinct number.
Both numbers with value 2 are both considered as second maximum.

  1.解题思路:上面的有一题也是按照自己的顺序对数组进行一次遍历,然后排序找出了5个数。这里也可以用同样的原理:

  2.有问题的代码,在定义了int类型的Integer.MIN_VALUE后,如果输入是{2,1,Integer.MIN_VALUE}那么根据return语句可以知道返回结果并不是Integer.MIN_VALUE,这里就有了一个问题。自己想的解决思路是再设置三个标志位,如果输入Integer.MIN_VALUE确实修改了就将标志位设置为true,但是这样会造成额外的开销。

    public int thirdMax(int[] nums) {
int first = Integer.MIN_VALUE, second = Integer.MIN_VALUE, third = Integer.MIN_VALUE;
for (int i : nums) {
if (i < second && i > third) {
third = i;
} else if (first > i && i > second) {
third = second;
second = i;
} else if (i > first) {
third = second;
second = first;
first = i;
}
}
return third == Integer.MIN_VALUE ? first : third;
}

  3.解决问题的方法:将int类型改成Integer类型即可,其他完全一样,这样在return中就可以使用third==null?来进行判断。

  不过由于考虑到初始时first,second,third为null时直接替换,在if条件中加上了first == null ||与条件,这就会发生另外一种情况,就是{2,2,3,1}时,第二个2会直接进入到second,也就是无法去除重复元素,因此需要在前面加上一条判断语句, if (i.equals(first) || i.equals(second) || i.equals(third)) continue;,即如果和三个中的其中一个相同就直接退出循环。

    public int thirdMax(int[] nums) {
Integer first = null, second = null, third = null;
for (Integer i : nums) {
if (i.equals(first) || i.equals(second) || i.equals(third)) continue;
if (first == null || i > first) {
third = second;
second = first;
first = i;
} else if (second == null || (first > i && i > second)) {
third = second;
second = i;
} else if (third == null || second > i && i > third) {
third = i;
}
}
return third == null ? first : third;
}

  十、(考虑完各种情况)665. Non-decreasing Array

输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。 输入: [4,2,1]
输出: False
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

  1.思路:主要是要分清各种情况

  首先,定位到A[p-1]>A[p],如果p不是唯一的或者不存在,那么就是false。

  定位到之后,分析下面的情况:

  • p == -1:整个数组本身就是非递减数列。
  • p ==  1:也就是nums的nums[0] > nums[1],这个时候,只要让nums[0] = nums[1]
  • p == nums.length - 1:也就是nums的nums[nums.length - 2] > nums[nums.length - 1],这个时候,只要让最后一个点的值大于或等于倒数第二个点的值即可。
  • 如果nums[p-2]、nums[p-1]、nums[p]、nums[p+1]都存在的情况下:
  • nums[p-2] <= nums[p]:类似于{1,3,2,4}这种情况,可以把3变成1和2之间的数。
  • nums[p-1] <= nums[p+1]:类似于{1,3,2,4}这种情况,也可以把2变成3和4之间的数。

  2.代码:需要注意的是,由于最多只能改变一个数的值,因此,

  for循环中的if-else语句就是判断是否有两个或两个以上的值满足要求,如果是,就返回false。也就是说,p只能被赋值一次!

    public boolean checkPossibility(int[] nums) {
int p = -1;
for (int i = 1; i < nums.length; i++) {
if (nums[i-1] > nums[i]) {
if (p != - 1) return false;
else p =
i;
}
}
return (p == -1 || p == 1 || p == nums.length-1 || nums[p-2] <= nums[p] || nums[p-1] <= nums[p+1]);
}

  十一、739. Daily Temperatures

  十二、565.Array Nesting 854 / 856 test cases passed

输入: A = [5,4,0,3,1,6,2]
输出: 4
解释:
A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2. 其中一种最长的 S[K]:
S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}

  1.常规做法(O(n2) + O(1)),854 / 856 test cases passed,Time Limit Exceeded

    public int arrayNesting(int[] nums) {
int maxlen = 0;
int len = 0;
int tmp = 0;
for (int i = 0; i < nums.length; i++) {
len = 1;
tmp = nums[i];
while (nums[tmp] != nums[i]) {
len++;
tmp = nums[tmp];
}
maxlen = Math.max(maxlen, len);
}
return maxlen;
}

  2.更快的做法1(O(n) + O(n)):Using Visited Array  

  思路就是,例如A = [5,4,0,3,1,6,2],如果我们在A[0]=5的过程中已经确定了5, 6, 2, 0,那么我们将0,2,5,6这些位置进行标记,之后在遍历到2/5/6时直接跳过即可,

    public int arrayNesting(int[] nums) {
boolean[] visited = new boolean[nums.length];
int res = 0;
for (int i = 0; i < nums.length; i++) {
if (!visited[i]) {
int start = nums[i], count = 0;
do {
start = nums[start];
count++;
visited[start] = true;
}
while (start != nums[i]);
res = Math.max(res, count);
}
}
return res;
}

  3.更快的做法2(O(n) + O(1)):Without Using Extra Space

  思路就是,例如A = [5,4,0,3,1,6,2],如果我们在A[0]=5的过程中已经确定了5, 6, 2, 0,那么2/5/6在之后的遍历过程中可以不用了,也就是在原数组中修改这些位置上的值即可,反正也再也用不到了。

    public int arrayNesting(int[] nums) {
int res = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != Integer.MAX_VALUE) {
int start = nums[i], count = 0;
while (nums[start] != Integer.MAX_VALUE) {
int temp = start;
start = nums[start];
count++;
nums[temp] = Integer.MAX_VALUE;
}
res = Math.max(res, count);
}
}
return res;
}

  十三、medium 529(DFS+BFS)

  1.

    public char[][] updateBoard(char[][] board, int[] click) {
int m = board.length, n = board[0].length;
int row = click[0], col = click[1]; if (board[row][col] == 'M') {
board[row][col] = 'X';
} else {
int count = 0;
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++) {
if (i == 0 && j == 0) continue;
int r = row + i, c = col + j;
if (r < 0 || r >= m || c < 0 || c >= n) continue;
if (board[r][c] == 'M' || board[r][c] == 'X') count++;
}
}
if (count > 0) {
board[row][col] = (char)(count + '0');
} else {
board[row][col] = 'B';
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++) {
if (i == 0 && j == 0) continue;
int r = row + i, c = col + j;
if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue;
if (board[r][c] == 'E') updateBoard(board, new int[] {r, c});
}
}
}
}
return board;
}

  十四、

Leetcode Tags(2)Array的更多相关文章

  1. Leetcode Tags(13)Bit Manipulation

    一.477.汉明距离总和 输入: , , 输出: 解释: 在二进制表示中,4表示为0100,14表示为1110,2表示为0010.(这样表示是为了体现后四位之间关系) HammingDistance( ...

  2. Leetcode Tags(13)Tree

    1.前序.中序.后序递归方式遍历二叉树 public void preOrderRecur(Node T) { if (T != null) { System.out.print(T.val + &q ...

  3. Leetcode Tags(8)Binary Search

    一.475. Heaters 输入: [1,2,3],[2] 输出: 1 解释: 仅在位置2上有一个供暖器.如果我们将加热半径设为1,那么所有房屋就都能得到供暖. 输入: [1,2,3,4],[1,4 ...

  4. Leetcode Tags(6)Math

    一.204. Count Primes Count the number of prime numbers less than a non-negative number, n. Input: 10 ...

  5. Leetcode Tags(5)Hash Table

    一.500. Keyboard Row 给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词. 输入: ["Hello", "Alaska", &q ...

  6. Leetcode Tags(3)String(TODO)

    一.Easy 696 Count Binary Substrings Input: "00110011" Output: 6 Explanation: There are 6 su ...

  7. Leetcode Tags(1)Linked List

    1.知识点回顾 https://www.cnblogs.com/BigJunOba/p/9174206.html https://www.cnblogs.com/BigJunOba/p/9174217 ...

  8. LeetCode记录(1)——Array

    1.Two Sum naive 4.Median of Two Sorted Arrays 找两个已排序数组的中位数 直接数可以过,但很蠢,O(m+n)时间 class Solution { publ ...

  9. Leetcode Tags(4)Stack & Queue

    一.232. Implement Queue using Stacks private Stack<Integer> stack; /** Initialize your data str ...

随机推荐

  1. opencv目标检测之canny算法

    canny canny的目标有3个 低错误率 检测出的边缘都是真正的边缘 定位良好 边缘上的像素点与真正的边缘上的像素点距离应该最小 最小响应 边缘只能标识一次,噪声不应该标注为边缘 canny分几步 ...

  2. redis-计数信号量

    1.基本概念 2.信号量类 3.测试类 4.测试日志 基本概念 计数信号量是一种锁,它可以让用户限制一项资源最多能够同时被多少个进程访问, 技术信号量和其他锁的区别:当客户端获取锁失败时,客户端会选择 ...

  3. nslookup的基本使用

    nslookup的基本使用 nslookup:name server lookup 用来查询DNS的. 1:安装nslookup命令 [root@localhost ~]# yum install b ...

  4. 前端深入之css篇|link和@import到底有什么区别?

    写在前面 在真正的前端开发中,我们很少去写行内样式和内嵌样式,通常都是去引用外部样式. 而在我们学习之初的外部样式表都是用link引入的,但是当后来我们学习的逐渐深入,发现@import也可以引入样式 ...

  5. Java 学习笔记之 实例变量与线程安全

    实例变量与线程安全: 不共享数据: public class NoSharedThread extends Thread { private int count = 5; public NoShare ...

  6. Spring Boot 2.X(六):Spring Boot 集成Redis

    Redis 简介 什么是 Redis Redis 是目前使用的非常广泛的免费开源内存数据库,是一个高性能的 key-value 数据库. Redis 与其他 key-value 缓存(如 Memcac ...

  7. Dubbo学习系列之十五(Seata分布式事务方案TCC模式)

    上篇的续集. 工具: Idea201902/JDK11/Gradle5.6.2/Mysql8.0.11/Lombok0.27/Postman7.5.0/SpringBoot2.1.9/Nacos1.1 ...

  8. .netCore+Vue 搭建的简捷开发框架 (4)--NetCore 基础 -2

    上节中,我们初步的介绍了一下NetCore的一些基础知识,为了控制篇幅(其实也是因为偷懒),我将NetCore 基础分为两部分来写. 0.WebAPI 项目的建立 1..NetCore 项目执行(加载 ...

  9. 04-04 AdaBoost算法代码(鸢尾花分类)

    目录 AdaBoost算法代码(鸢尾花分类) 一.导入模块 二.导入数据 三.构造决策边界 四.训练模型 4.1 训练模型(n_e=10, l_r=0.8) 4.2 可视化 4.3 训练模型(n_es ...

  10. 线程池和lambda表达式

    线程池1.什么是线程池.一个用来创建和管理线程的容器;2.线程池的作用.提高线程的复用性,降低资源消耗提高线程的响应速度,提高线程的可管理性3.线程的核心思想;线程的复用 4.线程池的创建Execut ...