相关介绍
集合&列表&数组
集合:
- 集合中装载的元素类型不一定相同
- 元素没有顺序
列表:
列表是在集合的概念上形成的,是一种线性的数据集合,通常具有以下特征:
- 元素按一定的顺序排列
- 长度是可变的,可以向列表中添加/删除元素
表现形式:
- 常见的表现形式是:链表和数组
- 特殊的表现形式:栈和队列
数组:
数组是列表的一种具体的表现形式,它的特点有:
- 它与列表的最明显的不同就是引入了索引的概念
- 数组中的元素在内存中是连续存储的,且每个元素占用相同大小的内存
数组的操作
1、读取元素
数组在内存中的存储方式是连续内存的方式,通常会记录下第一个元素的内存地址,然后通过第一个元素的内存地址+索引值的方式读取元素,时间复杂度为 O(1)。
2、查找元素
查找元素时要从头到尾挨个匹配,因此最坏时间复杂度为 O(n)。
3、插入元素
数组插入元素时,如果插入的位置正好是末尾,那么时间复杂度为O(1),但是首尾之间的情况就是插入后,后面的元素要整体后移一个单位,所以是O(n)。此时用链表是最合适的,时间复杂度为O(1)。
4、删除元素
删除元素,整体上要前移,所以为O(n)。
题目一
主站-1991. 找到数组的中间位置
给你一个下标从 0 开始的整数数组 nums ,请你找到 最左边 的中间位置 middleIndex (也就是所有可能中间位置下标最小的一个)。
中间位置 middleIndex 是满足 nums[0] + nums[1] + … + nums[middleIndex-1] == nums[middleIndex+1] + nums[middleIndex+2] + … + nums[nums.length-1] 的数组下标。
如果 middleIndex == 0 ,左边部分的和定义为 0 。类似的,如果 middleIndex == nums.length - 1 ,右边部分的和定义为 0 。
请你返回满足上述条件 最左边 的 middleIndex ,如果不存在这样的中间位置,请你返回 -1 。
解题思路
- 先遍历数组得到数组之和 sum;
- 再遍历数组得到数组的左侧元素之和 left_sum,当 left_sum 的二倍加上当前元素的和等于 sum 时,满足条件直接返回;
- 否则表示没有合适的元素,返回 -1;
代码及注释
class Solution {
public int findMiddleIndex(int[] nums) {
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
int left_sum = 0;
for (int j = 0; j < nums.length; j++) {
if (left_sum * 2 + nums[j] == sum) {
return j;
}
left_sum += nums[j];
}
return -1;
}
}
题目二
主站-56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
解题思路
常规的思路就是我们拿到两个数组比较它的首尾元素,两边都取那个范围更大的值,但是这样就需要每拿到一个数组就跟前面的所有数组做对比,有些情况本来没有重叠的两个数组,加上另一个数组后反而有重叠了,所以还要从头至尾比较,时间复杂度比较高。
先将区间按照子数组的首元素排序,这样就可以只比较当前区间的末尾元素与比较区间的首尾元素即可,具体做法是:
- 如果当前区间的末尾元素 < 比较区间的首元素时,说明没有交集,新开一个合并区间;
- 比较当前区间的末尾元素 & 比较区间的尾元素,取一个大的作为合并区间末尾数值;
复制数组,返回结果
代码及注释
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] interval0, int[] interval1) {
return interval0[0] - interval1[0];
}
});
int[][] res = new int[intervals.length][2];
int index = -1;
for (int i = 0; i < intervals.length; i++) {
if (index == -1 || res[index][1] < intervals[i][0]) {
res[++index] = intervals[i];
} else {
res[index][1] = Math.max(res[index][1], intervals[i][1]);
}
}
return Arrays.copyOf(res, index + 1);
}
题目三
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
解题思路
假设矩阵中一个元素的位置为:(i,j), 对于矩阵中第 i 行的第 j 个元素,在旋转后,它出现在倒数第 i 列的第 j 个位置。 通过归纳,经过旋转后的位置为:(j,n-i-1)。那我们进行如下操作:
- 先对矩阵进行水平翻转:(x,y)=(n-x-1,j)
- 再对矩阵按主对角线翻转:(a,b)=(b,a)
因此经过两次翻转后的坐标应该是:(x,y)=(y,n-x-1),正好就是旋转90°的坐标
代码及注释
class Solution {
public void rotate(int[][] matrix) {//matrixnew[col][n−row−1]=matrix[row][col]
int n=matrix.length;
for(int i=0;i<n/2;i++){
for(int j=0;j<n;j++){
int temp=matrix[i][j];
matrix[i][j]=matrix[n-i-1][j];
matrix[n-i-1][j]=temp;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
int temp=matrix[i][j];
matrix[i][j]=matrix[j][i];
matrix[j][i]=temp;
}
}
}
}
题目四
73. 矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
解题思路
- 可以遍历矩阵,遇到 0 时就进入一个方法将所在的行、列置零,但是有个问题就是最后都为 0 了,就没法区分是置零产生的还是矩阵中原有的;
- 还有一种方法就是通过标志位矩阵来记录出现零的具体位置,这种方法的空间复杂度还可以优化,因为这里不论矩阵中一行(一列)有几个零,所在的行(列)都要置零,那么我们不需要记录准确的位置,只需要记录行(列)出现过零就可以;
- 那么就可以通过一个行、列标志向量来记录哪一行或者哪一列出现过零就可以,然后再遍历矩阵将对应的行或者列置零就可以。
代码及注释
public class Solution {
public void SetZeroes(int[][] matrix) {
bool[] row = new bool[matrix.Length];
bool[] column = new bool[matrix[0].Length];
for (int i = 0; i < matrix.Length; i++) {
for (int j = 0; j < matrix[0].Length; j++) {
if (matrix[i][j] == 0) {
row[i] = true;
column[j] = true;
}
}
}
for (int i = 0; i < matrix.Length; i++) {
for (int j = 0; j < matrix[0].Length; j++) {
if (row[i] || column[j]) {
matrix[i][j] = 0;
}
}
}
}
}