目前刷了一遍代码随想录,跟着剑指再总结一下之前做过的题,参考代码随想录、剑指Offer、力扣等,如有侵权,联系删除
实际题目中,往往使用二维数组处理矩阵类相关问题,包括矩阵旋转、对角线遍历,以及对子矩阵的操作等。
反向指针:从两端向中间迭代数组,一个指针从头部开始,另一个指针从尾部开始
同向指针:使用两个同向的指针——快慢指针
用途:求正数数组中子数组的和或乘积
需要考虑以下三点
使用双指针解决子数组之和的题目时数组中的所有数字都必须是正数;当数组中存在负数和零时,可以采用累加数组数字求子数组之和的方法
使用双指针解决子数组之和的题目时数组中的所有数字都必须是正数;当数组中存在负数和零时,可以采用累加数组数字求子数组之和的方法
在有序数组或者有序数组旋转后的数组中查询某一个元素
通过按照某种规律反转原数组,最后实现最终结果,达到空间复杂度O(1)
统计数组中的次数
还没整理完,之后单独贴链接:(
还没整理完,之后单独贴链接:(
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c5tv3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题解
解题思路
代码
public int[][] merge(int[][] intervals){
//1.先对输入数组按照区间左边的值进行升序排列
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0]-o2[0];
}
});
//2.初始化一个变量outputs,用于存储合并直接的区间结果
List<int []> outputs = new ArrayList<>();
//将第一个区间直接加入outputs
outputs.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
//比较当前处理的区间左边的值和outputs中最后一个区间右边的值
if(intervals[i][0] > outputs.get(outputs.size()-1)[1]){//没有重叠
outputs.add(intervals[i]);
}else{
outputs.get(outputs.size()-1)[1] = Math.max(outputs.get(outputs.size()-1)[1],intervals[i][1]);
}
}
//3.转换为数组
return outputs.toArray(new int[outputs.size()][]);
}
编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。
2021/10/24
我的题解
一个rowsSet存行中有为0的数的行号,一个colsSet存列中有为0的数的列号。第一次遍历得到所有包含0值的行和列,第二次遍历、第三次遍历将所有包含0值的行和列置零
public static void setZeroes(int[][] matrix) {
int row = matrix.length;
int col = matrix[0].length;
Set rows = new TreeSet();
Set cols = new TreeSet();
//找到为0的元素所在行所在列
for (int i=0;i<row;i++){
for (int j=0;j<col;j++){
if(matrix[i][j]==0){
rows.add(i);
cols.add(j);
}
}
}
Iterator it = rows.iterator();
//将指定行的元素置零
while(it.hasNext()){
int i = (int)it.next();
for (int j=0;j<col;j++){
matrix[i][j]=0;
}
}
it = cols.iterator();
//将指定列的元素置零
while (it.hasNext()){
int j = (int)it.next();
for (int i=0;i<row;i++){
matrix[i][j] = 0;
}
}
}
题解
方法一
使用两个标记数组分别记录每一行和每一列是否有零出现。
具体地,我们首先遍历该数组一次,如果某个元素为 0,那么就将该元素所在的行和列所对应标记数组的位置置为true。最后我们再次遍历该数组,用标记数组更新原数组即可。
方法二
我们可以用矩阵的第一行和第一列代替方法一中的两个标记数组,以达到 O(1)的额外空间。但这样会导致原数组的第一行和第一列被修改,无法记录它们是否原本包含 0。因此我们需要额外使用两个标记变量分别记录第一行和第一列是否原本包含 0。
方法三
我们可以对方法二进一步优化,只使用一个标记变量记录第一列是否原本存在 0。这样,第一列的第一个元素即可以标记第一行是否出现 0。但为了防止每一列的第一个元素被提前更新,我们需要从最后一行开始,倒序地处理矩阵元素。
class Solution {
public void setZeroes(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
boolean flagCol0 = false;
for (int i = 0; i < m; i++) {
if (matrix[i][0] == 0) {
flagCol0 = true;
}
for (int j = 1; j < n; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = matrix[0][j] = 0;
}
}
}
for (int i = m - 1; i >= 0; i--) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
if (flagCol0) {
matrix[i][0] = 0;
}
}
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/zero-matrix-lcci/solutions/742402/ling-ju-zhen-by-leetcode-solution-7ogg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给你一个大小为
m x n
的矩阵mat
,请以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素。
我的题解
//2021/10/24
public int[] findDiagonalOrder(int[][] mat) {
int sum = mat.length * mat[0].length; //数组的长度
int[] res = new int[sum];// 返回的结果
int num = 0;// 存放进数组的元素的下标
int i = 0; // 矩阵的行
int j = 0; // 矩阵的列
boolean isUp = true;// 记录遍历的方向,当isUp为true时,遍历方向为向右上方遍历;当isUp为false时,遍历方向为向左下方遍历
boolean isOut = false;// 记录遍历是否已经出界,【在遍历过程中,存在连续两次出界的情况,如果上一次已经出界,那么即使此次遍历再次出界,仍不改变遍历方向】
while(num < sum){
boolean lastDir = isUp;//记录上一次遍历的方向
boolean isLastOut = isOut;//记录上一次遍历是否出界
if(i>=0 && i<mat.length && j>=0 && j<mat[0].length){//如果索引未超出边界,则将其放入数组中
isOut = false;
res[num++] = mat[i][j];
}else{// 如果索引超出边界,则改变遍历方向
isOut = true;
if (!isLastOut){//【如果上一次已经出界,那么即使此次遍历再次出界,仍不改变遍历方向】
isUp = !isUp;
}
}
// 根据方向,改变索引
if (isUp){//遍历方向为右上时
if(lastDir == isUp){// 当与前一次遍历方向相同时,行号才需减1,否则只进行列加1操作【右】
i--;
}
j++;
}else{//遍历方向为左下时
i++;
if(lastDir == isUp){// 当与前一次遍历方向相同时,列号才需减1,否则只进行行加1操作【下】
j--;
}
}
}
return res;
}
题解一
链接:https://leetcode-cn.com/problems/diagonal-traverse/solution/dui-jiao-xian-bian-li-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题解二
作者:moreality
链接:https://leetcode-cn.com/problems/diagonal-traverse/solution/498-dui-jiao-xian-bian-li-zui-jian-dan-y-ibu3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解题思路
找规律
注意左下(+1, -1)和右上移动(-1, +1)不会改变当前(行 + 列)的和, 即判断方式为行 + 列的和;if (行 + 列)是偶数, 就是右上, 是奇数就是左下
边界情况
给你一个正整数
n
,生成一个包含1
到n2
所有元素,且元素按顺时针顺序螺旋排列的n x n
正方形矩阵matrix
。
题解1
题解2
class Solution {
public int[][] generateMatrix(int n) {
int l = 0, r = n - 1, t = 0, b = n - 1;
int[][] mat = new int[n][n];
int num = 1, tar = n * n;
while(num <= tar){
for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.
t++;
for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.
r--;
for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.
b--;
for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.
l++;
}
return mat;
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/spiral-matrix-ii/solution/spiral-matrix-ii-mo-ni-fa-she-ding-bian-jie-qing-x/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题解3:按层模拟
给你一个
m
行n
列的矩阵matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
题解2 对应LC59题解2
在循环的过程中多判定一次count < size
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
int rowLen = matrix.length;
int colLen = matrix[0].length;
int size = rowLen * colLen;
List<Integer> res = new ArrayList<>(size);
int r = colLen -1,l = 0,t = 0 ,b = rowLen - 1;
int count = 0;
while (count < size){
for (int i = l; count < size && i <= r; i++){
res.add(count++,matrix[t][i]);
}
t++;
for (int i = t; count < size && i <= b; i++){
res.add(count++,matrix[i][r]);
}
r--;
for (int i = r; count < size && i >= l; i--){
res.add(count++,matrix[b][i]);
}
b--;;
for (int i = b; count < size && i >= t; i--){
res.add(count++,matrix[i][l]);
}
l++;
}
return res;
}
}
给定一个数组
nums
,将其划分为两个连续子数组left
和right
, 使得:
left
中的每个元素都小于或等于right
中的每个元素。left
和right
都是非空的。left
的长度要尽可能小。在完成这样的分组后返回
left
的 长度 。用例可以保证存在这样的划分方法。
2022/10/24
思路:当左边数组的最大值小于右边数组的最小值时,left
中的每个元素都小于或等于 right
中的每个元素,返回下标+1
实现:使用rightMin数组记录该元素右边的数组的最小值(不包括该元素),使用leftMax记录该元素左边的数组的最大值(包括该元素),当leftMax[i]<=rightMin[i]时,返回i+1
代码
class Solution {
public int partitionDisjoint(int[] nums) {
int len = nums.length;
int[] leftMax = new int[len];// 记录该元素左边的数组的最大值(包括该元素)
int[] rightMin = new int[len];// 记录该元素右边的数组的最小值(不包括该元素)
leftMax[0] = nums[0];
rightMin[len-1] = nums[len-1];
for (int i = 1; i < len; i++){
leftMax[i] = Math.max(leftMax[i-1],nums[i]);
}
for (int i = len-2; i >= 0; i--){
rightMin[i] = Math.min(rightMin[i+1],nums[i+1]);
}
for (int i = 0; i <len; i++){
if (leftMax[i] <= rightMin[i]){
return i+1;
}
}
return -1;
}
}
优化
使用leftmax变量代替数组
class Solution {
public int partitionDisjoint(int[] nums) {
int len = nums.length;
int leftMax = 0;// 记录该元素左边的数组的最大值(包括该元素)
int[] rightMin = new int[len];// 记录该元素右边的数组的最小值(不包括该元素)
rightMin[len-1] = nums[len-1];
for (int i = len-2; i >= 0; i--){
rightMin[i] = Math.min(rightMin[i+1],nums[i+1]);
}
for (int i = 0; i <len; i++){
leftMax = Math.max(leftMax,nums[i]);
if (leftMax <= rightMin[i]){
return i+1;
}
}
return -1;
}
}
思路:假设一个left的划分,其最大值为maxLeft,划分位置为leftPos,此时nums[0,leftPos]均属于left。
实现:使用变量curMax维护下标i左边的元素的最大值
class Solution {
public int partitionDisjoint(int[] nums) {
int n = nums.length;
int leftMax = nums[0], leftPos = 0, curMax = nums[0];
for (int i = 1; i < n - 1; i++) {
curMax = Math.max(curMax, nums[i]);
if (nums[i] < leftMax) {
leftMax = curMax;
leftPos = i;
}
}
return leftPos + 1;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/partition-array-into-disjoint-intervals/solutions/1913934/fen-ge-shu-zu-by-leetcode-solution-t4pm/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度
给你一个数组,将数组中的元素向右轮转
k
个位置,其中k
是非负数。
借助辅助数组
class Solution {
public void rotate(int[] nums, int k) {
int len = nums.length;
k = k % len;
int[] res = new int[len];
for (int i = 0; i < len; i++){
res[i] = nums[(i+len-k)%len];
}
for (int i = 0; i < len; i++){
nums[i] = res[i];
}
}
}
借助辅助变量,轮转k次
超时
class Solution {
public void rotate(int[] nums, int k) {
int len = nums.length;
k = k % len;
int tmp = 0;
for (int i = 1; i <= k; i++){
tmp = nums[len-1];
for (int j = len - 1; j > 0; j--){
nums[j] = nums[j-1];
}
nums[0] = tmp;
}
}
}
类似字符串反转的题目,
class Solution {
private void reverse(int[] nums, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
public void rotate(int[] nums, int k) {
int n = nums.length;
k %= n;
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
}
给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?
思路:第i行的第 j个元素在旋转后恰好是倒数第i列的第 j个元素。因此对于矩阵中的元素 matrix[i][j]
,在旋转后,它的新位置为 matrix[j][matrix.length]
复杂度分析
时间复杂度: O ( N 2 ) O(N^2) O(N2),其中 N 是matrix 的边长。
空间复杂度: O ( N 2 ) O(N^2) O(N2),其中 N 是matrix 的边长。
如果是顺时针旋转90°
一定是先对角线翻转,再水平翻转
如果是逆时针旋转90°
一定是先水平翻转,再对角线翻转
代码
class Solution {
public void rotate(int[][] matrix) {
//先对角翻转
for (int i=0;i<matrix.length;i++){
for (int j=i;j<matrix[i].length;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] =temp;
}
}
//再水平翻转
for (int i=0;i<matrix.length;i++){
for (int j=0;j<matrix[i].length/2;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[i][matrix[i].length-1-j];
matrix[i][matrix[i].length-1-j] =temp;
}
}
}
}
复杂度分析
时间复杂度: O ( N 2 ) O(N^2) O(N2),其中 N 是matrix 的边长。
空间复杂度: O ( 1 ) O(1) O(1),原地反转
给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。
换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。
以数组形式返回答案。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/how-many-numbers-are-smaller-than-the-current-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public int[] smallerNumbersThanCurrent(int[] nums) {
int lens = nums.length;
int[] res = new int[lens];
for (int i = 0; i < lens; i++){
for (int j = 0; j < lens; j++){
if (nums[j] < nums[i]){
res[i]++;
}
}
}
return res;
}
}
class Solution {
public int[] smallerNumbersThanCurrent(int[] nums) {
int lens = nums.length;
int[] sortNums = Arrays.copyOf(nums, nums.length);
Arrays.sort(sortNums);
Map<Integer,Integer> map = new HashMap<>();
int[] res = new int[lens];
for (int i = 0; i < lens; i++){
if (!map.containsKey(sortNums[i])){
map.put(sortNums[i],i);
}
}
for (int i = 0; i < lens; i++){
res[i] = map.get(nums[i]);
}
return res;
}
}
数组元素的值域为 [0,100][0,100],所以可以考虑建立一个频次数组cnt ,cnt[i] 表示数字 i 出现的次数。那么对于数字ii 而言,小于它的数目就为 cnt[0…i−1] 的总和。
class Solution {
public int[] smallerNumbersThanCurrent(int[] nums) {
int[] cnt = new int[101];
int n = nums.length;
for (int i = 0; i < n; i++) {
cnt[nums[i]]++;
}
for (int i = 1; i <= 100; i++) {
cnt[i] += cnt[i - 1];
}
int[] ret = new int[n];
for (int i = 0; i < n; i++) {
ret[i] = nums[i] == 0 ? 0 : cnt[nums[i] - 1];
}
return ret;
}
}
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/how-many-numbers-are-smaller-than-the-current-number/solution/you-duo-shao-xiao-yu-dang-qian-shu-zi-de-shu-zi--2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
给你一个整数数组
arr
,请你帮忙统计数组中每个数的出现次数。如果每个数的出现次数都是独一无二的,就返回
true
;否则返回false
。
class Solution {
public boolean uniqueOccurrences(int[] arr) {
Map<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < arr.length; i++){
map.put(arr[i],map.getOrDefault(arr[i],0) + 1);
}
Set<Map.Entry<Integer,Integer>> set = map.entrySet();
Set<Integer> num = new HashSet<>();
for (Map.Entry<Integer,Integer> node:set){
if (num.contains(node.getValue())){
return false;
}else{
num.add(node.getValue());
}
}
return true;
}
}
数组长度和以及数组中的值有限,因此可以利用数组做哈希
class Solution {
public boolean uniqueOccurrences(int[] arr) {
int[] count = new int[2001];
boolean[] flag = new boolean[1001];
for (int i = 0; i < arr.length; i++){
count[arr[i]+1000]++;
}
for (int i = 0; i < count.length; i++){
if (count[i] > 0){
if (!flag[count[i]]){
flag[count[i]] = true;
}else{
return false;
}
}
}
return true;
}
}
给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。
返回该最大总和 。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c24he/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的题解
先对数组进行选择排序,再对下标为0,1,3……2n-1的元素求和
class Solution {
public int arrayPairSum(int[] nums) {
selectSort(nums);//选择排序
int sum = 0;
for(int i=0;i<nums.length;i+=2){
sum += nums[i];
}
return sum;
}
public void selectSort(int[] nums){
for (int i=0;i<nums.length;i++){
int minIndex = i;
for(int j=i+1;j<nums.length;j++){
if(nums[j] < nums[minIndex]){
minIndex = j;
}
}
if(i != minIndex){
int temp = nums[i];
nums[i] = nums[minIndex];
nums[minIndex] = temp;
}
}
}
}