一、数组
1、026——删除有序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。将最终结果插入 nums 的前 k 个位置后返回 k 。不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
/**
* 思路:用temp记录当前位置的值,然后依次比较,如果相同数组下标移动,
* 如果不同,将当前数组下标的值,复制到ans位置
*
* @param nums 目标数组
* @return 新数组长度
*/
public int removeDuplicates(int[] nums) {
if (null == nums || nums.length == 0) {
return 0;
}
int temp = nums[0];
int ans = 1;
int index = 1;
//
while (index < nums.length) {
if (nums[index] != temp) {
nums[ans++] = nums[index];
temp = nums[index];
}
index++;
}
return ans;
}
2、027——移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
/**
* 思路:用ans下标记录不同于目标值的元素,返回ans值
*
* @param nums 目标数组
* @param val 目标值
* @return 返回新数组长度
*/
public static int removeElement(int[] nums, int val) {
if (null == nums || nums.length == 0) {
return 0;
}
int index = 0;
int ans = 0;
while (index < nums.length) {
if (nums[index] != val) {
nums[ans++] = nums[index];
}
index++;
}
return ans;
}
3、031——下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
/**
* 思路:从后往前找到第一个arr[k] < arr[k + 1],这个时候从arr[k+1] 往后依次递减
* 然后再从后往前找到第一个大于arr[k]的值,两者交换,然后将从arr[k+1]开始往后这段反转
* 如果没找到arr[k] < arr[k + 1],此时k为-1,即数组为降序数组,直接倒序就行
*
* @param nums 目标数组
*/
public static void nextPermutation(int[] nums) {
if (null == nums || nums.length < 2) {
return;
}
int k = nums.length - 2;
while (k >= 0 && nums[k] >= nums[k + 1]) {
k--;
}
if (k >= 0) {
int i = nums.length - 1;
while (i >= 0 && nums[k] >= nums[i]) {
i--;
}
swap(nums, k, i);
}
reverse(nums, k + 1);
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
private static void reverse(int[] arr, int left) {
int l = left;
int r = arr.length - 1;
while (l < r) {
swap(arr, l, r);
l++;
r--;
}
}
4、041——缺失的第一个正数
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
/**
* 思路:原地哈希,由题可知返回值为[1,N + 1],
* 把1放到下标为0,2放到下标为1的位置,以此类推,哪个位置的值不等于下标+1,就返回那个位置的下标+1值
*
* @param nums 目标数组
* @return 返回未出现的最小正整数
*/
public static int firstMissingPositive(int[] nums) {
if (null == nums || nums.length == 0) {
return 0;
}
int len = nums.length;
for (int i = 0; i < len; i++) {
while (nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
// 满足在指定范围内、并且没有放在正确的位置上,才交换
swap(nums, nums[i] - 1, i);
}
}
for (int i = 0; i < len; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
// 都正确则返回数组长度 + 1
return len + 1;
}
private static void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
5、054——螺旋矩阵
给你一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
/**
* 定义边界值,不断缩小边界值
*
* @param matrix 目标数组
* @return 顺时针遍历结果
*/
public static List<Integer> spiralOrder(int[][] matrix) {
List<Integer> list = new ArrayList<>();
if (null == matrix || matrix.length == 0) {
return list;
}
int u = 0;
int d = matrix.length - 1;
int l = 0;
int r = matrix[0].length - 1;
while (true) {
// 向右移动到最右
for (int i = l; i <= r; i++) {
list.add(matrix[u][i]);
}
// 若上边界大于下边界,则遍历遍历完成
if (++u > d) {
break;
}
// 向下遍历
for (int i = u; i <= d; i++) {
list.add(matrix[i][r]);
}
// 若右边界小于左边界,则遍历遍历完成
if (--r < l) {
break;
}
// 向左遍历
for (int i = r; i >= l; i--) {
list.add(matrix[d][i]);
}
// 若下边界小于上边界,则遍历遍历完成
if (--d < u) {
break;
}
for (int i = d; i >= u; i--) {
list.add(matrix[i][l]);
}
if (++l > r) {
break;
}
}
return list;
}
6、056——合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
/**
* 思路:排序 + 双指针
* 左指针指区间开始,一个临时变量存最大的区间结尾值,右指针也指向区间开始,
* 比较该区间开始值和临时最大值是否重合,重合取较大的值,更新右指针,
* 如果不重合,放入结果集,更新左指针
*
* @param intervals 目标数组
* @return 返回合并后的区间
*/
public int[][] merge(int[][] intervals) {
if (intervals == null || intervals.length == 0) {
return new int[0][2];
}
// 排序
Arrays.sort(intervals, (a, b) -> {
return a[0] - b[0];
});
List<int[]> res = new ArrayList<>();
//下一个数组的起始值必须要小于上一个数组的最大值才能合并
int leftIndex = 0;
int rightIndex = 0;
while (rightIndex < intervals.length) {
int start = intervals[leftIndex][0];
int end = intervals[rightIndex][1];
while (rightIndex < intervals.length && intervals[rightIndex][0] <= end) {
end = Math.max(end, intervals[rightIndex][1]);
rightIndex++;
}
res.add(new int[]{start, end});
leftIndex = rightIndex;
}
int[][] result = new int[res.size()][2];
for (int i = 0; i < res.size(); i++) {
result[i][0] = res.get(i)[0];
result[i][1] = res.get(i)[1];
}
return result;
}
7、057——插入区间
给你一个 无重叠的 *,*按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
/**
* 思路:因为是有序的,所以从左到右,先把左边不重合的区间添加进去,
* 在找到重合的区间,添加进去,再把右边不重合部分添加进去
*
* @param intervals 原数组
* @param newInterval 插入数组
* @return 合并后的区间
*/
public static int[][] insert2(int[][] intervals, int[] newInterval) {
if (intervals == null || intervals.length == 0) {
return new int[][]{{newInterval[0], newInterval[1]}};
}
if (newInterval == null || newInterval.length == 0) {
return intervals;
}
List<int[]> res = new ArrayList<>();
int len = intervals.length;
int index = 0;
// 判断左边不重合
while (index < len && intervals[index][1] < newInterval[0]) {
res.add(intervals[index]);
index++;
}
// 判断重合
while (index < len && intervals[index][0] <= newInterval[1]) {
newInterval[0] = Math.min(intervals[index][0], newInterval[0]);
newInterval[1] = Math.max(intervals[index][1], newInterval[1]);
index++;
}
res.add(newInterval);
// 判断右边不重合
while (index < len && intervals[index][0] > newInterval[1]) {
res.add(intervals[index]);
index++;
}
return res.toArray(new int[0][]);
}
8、059—— 螺旋矩阵 II
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
/**
* 思路:同54题旋转矩阵,定义边界值,不断更新边界值,遍历
*
* @param n 输入的正整数值
* @return 顺时针螺旋排列的数组
*/
public int[][] generateMatrix(int n) {
if (n == 1) {
return new int[][]{{1}};
}
int[][] result = new int[n][n];
int u = 0;
int d = n - 1;
int l = 0;
int r = n - 1;
int start = 1;
while (start <= n * n) {
// 向左遍历
for (int i = l; i <= r; i++) {
result[u][i] = start++;
}
if (++u > d) {
break;
}
// 向下遍历
for (int i = u; i <= d; i++) {
result[i][r] = start++;
}
if (--r < l) {
break;
}
// 向左遍历
for (int i = r; i >= l; i--) {
result[d][i] = start++;
}
if (--d < u) {
break;
}
for (int i = d; i >= u; i--) {
result[i][l] = start++;
}
if (++l > r) {
break;
}
}
return result;
}
9、073——矩阵置零
给定一个 *m* x *n*
的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法
输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
/**
* 思路: 先判断第一行和第一列是否需要置0, 然后用第一行和第一列来标记该行或该列是否是要置0
* 然后处理第一行和第一列
*
* @param matrix 目标数组
*/
public void setZeroe1(int[][] matrix) {
if (null == matrix || matrix.length == 0) {
return;
}
int row = matrix.length;
int col = matrix[0].length;
boolean rowFlag = false;
boolean colFlag = false;
// 第一行是否有0
for (int i = 0; i < col; i++) {
if (matrix[0][i] == 0) {
rowFlag = true;
break;
}
}
// 第一列是否有0
for (int i = 0; i < row; i++) {
if (matrix[i][0] == 0) {
colFlag = true;
break;
}
}
// 把第一行和第一列作为标记位
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
if (matrix[i][j] == 0) {
matrix[i][0] = matrix[0][j] = 0;
}
}
}
// 置0
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}
if (rowFlag) {
for (int j = 0; j < col; j++) {
matrix[0][j] = 0;
}
}
if (colFlag) {
for (int i = 0; i < row; i++) {
matrix[i][0] = 0;
}
}
}
10、118——杨辉三角
给定一个非负整数 *numRows
,*生成「杨辉三角」的前 numRows
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
/**
* 思路:根据杨辉三角的特性可知,每一层第一个和最后一个都为1,其他位置等于上一层,左边和右边之和
*
* @param numRows 非负整数
* @return 杨辉三角集合
*/
public static List<List<Integer>> generate(int numRows) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
for (int i = 1; i <= numRows; i++) {
List<Integer> level = new ArrayList<>();
for (int j = 1; j <= i; j++) {
if (j == 1 || j == i) {
level.add(1);
} else {
level.add(ans.get(i - 2).get(j - 2) + ans.get(i - 2).get(j - 1));
}
}
ans.add(level);
}
return ans;
}
11、119——杨辉三角 II
给定一个非负索引 rowIndex
,返回「杨辉三角」的第 rowIndex
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
/**
* 思路:本题仅需最后一行的数据,当处理到最后一行时,row正好是上一行的数据,直接使用
*
* @param rowIndex 要返回的行数
* @return 返回输入行的集合
*/
public List<Integer> getRow(int rowIndex) {
List<Integer> row = new ArrayList<>();
row.add(1);
for (int i = 1; i < rowIndex; i++) {
row.add(0);
for (int j = i; j > 0; j--) {
row.set(j, row.get(j) + row.get(j - 1));
}
}
return row;
}