数组&数学(array&Math)
1 常见题目
数学类包括数字变换、计数以及数学公式;
数组类可能用到hash、循环遍历等等。
1.1 (lee-JZ-62) 圆圈中最后剩下的数字
经典的约瑟夫环的问题
原始问题:一个长度为n的环,起点为1,删除第m个元素,并使该元素之后的元素为起点,删除第m个元素,直至剩一个元素。
从原始问题出发,当第一次操作时,删除了第m个元素,起点为m+1,此时问题可转化为:长度为n-1的环,起点为m+1,每次删除第m个元素,求最终剩下的元素。
思路:利用数学公式反推 (当前index + m) % 上一轮剩余数字的个数。
输入: n = 5, m = 3
输出: 3
(1)数学+迭代
/*
* 1.数学+迭代
* 时间复杂度:O(n)
* 空间复杂度:O(1)
*/
public int ysf (int n, int m) {
int res = 0;
for(int i = 2;i <= n;i++) {
res = (res + m) % i;
}
return res+1;
}
(2)数学+递归
/*
* 2.数学+递归
* 时间复杂度:O(n)
* 空间复杂度:O(n)
*/
public int lastRemaining(int n, int m) {
return f(n, m);
}
public int f(int n, int m) {
if (n == 1) {
return 0;
}
int x = f(n - 1, m);
return (m + x) % n;
}
(3)使用ArrayList
public int ysf(int n,int m) {
List<Integer> list = new ArrayList<Integer>();
for(int i = 1;i<=n;i++) { //n个元素添加到循环链表中,用集合可以代替
list.add(i);
}
int index = 0;
while(list.size() > 1) { //直到剩下一个元素的时候循环停止
index = (index+m-1)%list.size();
list.remove(index); //每次删除第m个元素
}
return list.get(0); //返回最后剩下的元素
}
(4)输出依次出局的人
private String ysf(int[] input) {
int N = input[0];
int M = input[1];
// if(N<1 || M<1) return null;
ArrayList<Integer> list = new ArrayList<Integer>();
StringBuilder sb = new StringBuilder();
for(int i=0;i<N;i++){
list.add(i+1);
}
int index=0;
while(list.size()>0){
index = (index+M-1) % list.size();
sb.append(list.get(index)).append(" ");
list.remove(index);
}
return sb.delete(sb.length()-1,sb.length()).toString();
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("输入n:");
int n = input.nextInt();
System.out.print("输入m:");
int m = input.nextInt();
int[] in = new int[] {n,m};
String res = ysf(in);
System.out.print(res);
}
1.2 (lee-48) 旋转图像
给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。
说明:必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
/**
* 思路: 先置换再反转矩阵每一行
* 时间复杂度:O(n^2)
* 空间复杂度:O(1) 要求原地旋转
* @param matrix
*/
public void rotate(int[][] matrix) {
int n = matrix.length;
//置换矩阵
for(int i = 0;i <n;i++) {
for(int j = i;j < n;j++) { //注意
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
//反转矩阵每一行
for(int i = 0;i < n;i++) {
for(int j = 0;j < n/2;j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[i][n-j-1];
matrix[i][n-j-1] = temp;
}
}
}
1.3 (lee-448) 找到所有数组中消失的数字
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。
输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]
输入:nums = [1,1]
输出:[2]
(1) 数组下标计数
/*
* 思路:利用数组下标计数实现对目标数组的每个元素的计数,随后在循环一次的循环中查找并添加计数为零的元素
*/
public List<Integer> findDisappearedNumbers(int[] nums) {
int[] count = new int[nums.length+1];
for(int i = 0;i < nums.length;i++) {
count[nums[i]]++;
}
List<Integer> res = new ArrayList<>();
for(int i = 1;i <= nums.length;i++) {
if(count[i] == 0) {
res.add(i);
}
}
return res;
}
(2) 遍历标记visited
/*
* 思路:先遍历一遍数组,标记哪些出现过,如果nums[visited] < 0 , 表示i+1出现过
* 再遍历一遍数组,如果nums[i-1] > 0 ,说明i没出现过,加入结果集
* 例如:输入:nums = [4,3,2,7,8,2,3,1]
* 标记后为[-4,-3,-2,-7,8,2,-3,-1]
* 由于8,2都大于0,返回其下标5,6
* 输出:[5,6]
*/
public List<Integer> findDisappearedNumbers1(int[] nums) {
for(int i = 0;i < nums.length;i++) {
int visited = Math.abs(nums[i])-1;
if(nums[visited] > 0) {
nums[visited] *= -1;
}
}
List<Integer> res = new ArrayList<>();
for(int i = 1;i <= nums.length;i++) {
if(nums[i-1] > 0) {
res.add(i);
}
}
return res;
}