目录
杨辉三角
在屏幕上打印杨辉三角。
1
1 1
1 2 1
1 3 3 1
……........
public class Main {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[][] arr = new int[10][10];
int i = 0;
int j = 0;
for (i = 0; i < 10; i++) {
for (j = 0; j <= i; j++) {
if (i == j) {
arr[i][j] = 1;
}
if (j == 0) {
arr[i][j] = 1;
}
if (i >= 2 && j >= 1) {
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
}
}
}
for (i = 0; i < 10; i++) {
for (j = 0; j <= i; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
消失的数字
https://leetcode.cn/problems/missing-number-lcci/description/?envType=list&envId=OoMH8xH5
数组
nums
包含从0
到n
的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?注意:本题相对书上原题稍作改动
示例 1:
输入:[3,0,1] 输出:2示例 2:
输入:[9,6,4,2,3,5,7,0,1] 输出:8
class Solution {
/*
解题思路:
直接将数组中所有元素之和sum,然后求出1~N的前N项和-sum即可
时间复杂度O(N),空间复杂度:O(1)
缺陷:如果数组中元素比较多,相加完成之后容易溢出
*/
public int missingNumber1(int[] nums) {
int sum = 0;
for(int i = 0; i < nums.length; ++i){
sum += nums[i];
}
return ((1+nums.length)*nums.length)/2 - sum;
}
/*
解题思路:
采用异或的方式解决,因为两个相同的数字异或的结果是0
因此:将0~N之间的数字,与数组中的每个数字异或,
最终的结果就是丢失的数字
*/
public int missingNumber2(int[] nums) {
int data = 0;
for(int i = 0; i < nums.length; ++i){
data ^= nums[i];
data ^= i;
}
data ^= nums.length;
return data;
}
/*
解题思路:
将1~N之间的数组相加,然后减去nums数组中的每个数字
*/
public int missingNumber(int[] nums) {
int data = nums.length;
for(int i = 0; i < nums.length; ++i){
data = data - nums[i] + i;
}
return data;
}
}
删除有序数组中的重复项(I)
给你一个 升序排列 的数组
nums
,请你** 原地 (opens new window)** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有
k
个元素,那么nums
的前k
个元素应该保存最终结果。将最终结果插入
nums
的前k
个位置后返回k
。不要使用额外的空间,你必须在 **原地 (opens new window)**修改输入数组并在使用 O(1) 额外空间的条件下完成。
判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组 int[] expectedNums = [...]; // 长度正确的期望答案 int k = removeDuplicates(nums); // 调用 assert k == expectedNums.length; for (int i = 0; i < k; i++) { assert nums[i] == expectedNums[i]; }
如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4] 解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
提示:
1 <= nums.length <= 3 * 10^4
-10^4 <= nums[i] <= 10^4
nums
已按 升序 排列
/**
解题思路:双指针思想 一个指针对数组遍历,一个指针指向即将被赋值的位置
1.遍历数组中的每个元素
2.当前被遍历的元素跟上一个元素进行比较,如果当前元素跟上个元素不同,那么就保存,相同就跳过。
*/
class Solution {
public int removeDuplicates(int[] nums) {
//指针 j 指向即将被赋值的位置
int j = 0;
//指针 i 进行数组遍历
for (int i = 0; i < nums.length; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
//i == 0说明是第一个元素,第一个元素要先赋值,因为它前面没有元素,每次遍历的时候要比较当前元素和它上一个元素的值,如果不相等,说明不重复,就需要赋值
nums[j] = nums[i];
//一旦赋值了说明此时的nums[j]上有元素了,有元素就要让j向下一个位置移动,因为j是指向即将被赋值的位置,相当于占个位置
j++;
}
}
//遍历完成之后,直接返回j即可,数组索引的特性,索引代表几就说明前面有几个元素
return j;
}
}
删除有序数组中的重复项(II)
https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/description/
给你一个有序数组
nums
,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }示例 1:
输入:nums = [1,1,1,2,2,3] 输出:5, nums = [1,1,2,2,3] 解释:函数应返回新长度 length = 5 , 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 不需要考虑数组中超出新长度后面的元素。示例 2:
输入:nums = [0,0,1,1,1,1,2,3,3] 输出:7, nums = [0,0,1,1,2,3,3] 解释:函数应返回新长度 length = 7 , 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。不需要考虑数组中超出新长度后面的元素。提示:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums
已按升序排列
/**
解题思路:双指针思想 一个指针对数组遍历,一个指针指向即将被赋值的位置
1.遍历数组中的每个元素
2.当前被遍历的元素跟即将被赋值位置的元素的上一个的上一个元素进行比较,如果不同,那么就保存记录,相同就跳过。
*/
class Solution {
public int removeDuplicates(int[] nums) {
//指针 j 指向即将被赋值的位置
int j = 0;
//指针 i 进行数组遍历
for (int i = 0; i < nums.length; i++) {
if (j < 2 || nums[i] != nums[j - 2]) {//j < 2,说明最多可以连续出现2次,其次是需要注意这里是和即将被赋值元素的上一个的上一个进行比较
nums[j] = nums[i];
//一旦赋值了说明此时的nums[j]上有元素了,有元素就要让j向下一个位置移动,因为j是指向即将被赋值的位置,相当于占个位置
j++;
}
}
//遍历完成之后,直接返回j即可,数组索引的特性,索引代表几就说明前面有几个元素
return j;
}
}
移除元素
https://leetcode.cn/problems/remove-element/description/?envType=list&envId=OoMH8xH5
给你一个数组
nums
和一个值val
,你需要 原地 (opens new window)移除所有数值等于val
的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用
O(1)
额外空间并 **原地 (opens new window)**修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 1:
**输入:**nums = [3,2,2,3], val = 3 **输出:**2, nums = [2,2] **解释:**函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3] 解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
class Solution {
// public int removeElement(int[] nums, int val) {
// /*
// 1. 从前往后遍历nums,找到val第一次出现的位置
// 2. 将val之后的所有元素整体往前搬移,即删除该val
// 3. nums中有效元素个数减少一个
// 循环进行上述操作,直到nums中所有值为val的元素全部删除完
// 时间复杂度:O(N^2) 空间复杂度:O(1)
// */
// int size = nums.length;
// int i = 0;
// while(true){
// // 1. 在nums中找val的位置
// for(i = 0; i < size; ++i){
// if(nums[i] == val){
// break;
// }
// }
// if(i == size){
// // nums中没有发现值为val的元素,直接跳出while
// break;
// }
// // 2. 将i位置之后所有元素整体往前搬移一个位置
// for(int pos = i+1; pos < size; ++pos){
// nums[pos-1] = nums[pos];
// }
// // 注意:一趟之后size一定要减去1,因为删除了一个val
// size--;
// }
// return size;
// }
/*
解题思路:
1. 创建一个长度与nums相同的数组ret
2. 遍历nums,将nums中所有与val不同的元素搬移到ret中
3. 将ret中所有元素拷贝回nums中
时间复杂度: O(N) 空间复杂度: O(N)
*/
// public int removeElement(int[] nums, int val) {
// int[] ret = new int[nums.length];
// int size = 0;
// for(int i = 0; i < nums.length; ++i){
// if(nums[i] != val){
// ret[size++] = nums[i];
// }
// }
// System.arraycopy(ret, 0, nums, 0, size);
// return size;
// }
/*
解题思路:
1. 设置一个变量count,用来记录nums中值等于val的元素的个数
2. 遍历nums数组,对于每个元素进行如下操作:
a. 如果num[i]等于val,说明值为val的元素出现了一次,count++
b. 如果nums[i]不等于元素,将nums[i]往前搬移count个位置
因为nums[i]元素之前出现过count个值等于val的元素,已经被删除了
因此此处需要将nums[i]往前搬移
3. 返回删除之后新数组中有效元素个数
时间复杂度:O(N) 空间复杂度:O(1)
*/
// public int removeElement(int[] nums, int val) {
// int count = 0;
// for(int i = 0; i < nums.length; ++i){
// if(nums[i] == val){
// count++;
// }else{
// nums[i - count] = nums[i];
// }
// }
// return nums.length - count;
// }
/**
解题思路:双指针思想
1.遍历数组中的每个元素
2.遍历的元素跟val作比较,如果不等于val就保留,等于val就跳过
*/
public int removeElement(int[] nums, int val) {
//指针 j 指向即将被赋值的位置
int j = 0;
//指针 i 遍历数组
for (int i = 0; i < nums.length; i++) {
//进行筛选,如果每次遍历到的元素的值跟val不一样,就有资格留下来进行赋值
if (nums[i] != val) {
nums[j] = nums[i];
//一旦赋值了说明此时的nums[j]上有元素了,有元素就要让j向下一个位置移动,因为j是指向即将被赋值的位置,相当于占个位置
j++;
}
}
//遍历完成之后,直接返回j即可,数组索引的特性,索引代表几就说明前面有几个元素
return j;
}
}
移动零
https://leetcode.cn/problems/move-zeroes/description/
给定一个数组
nums
,编写一个函数将所有0
移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0]
示例 2:
输入: nums = [0] 输出: [0]
提示:
1 <= nums.length <= 10^4
-2^31 <= nums[i] <= 2^31 - 1
class Solution {
/**
解题思路:双指针
1.指针 i 遍历数组的每个元素
2.遍历的元素如果非 0,就赋值且让指针 j 向后移动,为 0 就跳过
3.因为有几个非 0 的元素,j就会向后移动几个位置,即 j 前面的都是非 0 元素,j指向所有为 0 元素的第一个位置,后面都是为 0 的元素
时间复杂度:O(n),其中 n 为序列长度。每个位置至多被遍历两次。
空间复杂度:O(1)。只需要常数的空间存放若干变量。
*/
public void moveZeroes(int[] nums) {
// 指针 j 指向经过一系列操作后数组中所有为 0 元素的第一个位置上
// 一开始默认在索引为 0 的位置
int j = 0;
// 指针 i 从头到尾遍历数组
// 遍历完毕之后,j 指向了一个为 0 的元素,或者如果数组中不存在 0 ,就和 i 一样,超过了数组的范围
for (int i = 0; i < nums.length; i++) {
// 在遍历过程中,如果发现访问的元素是非 0 元素
// 说明 j 不在正确的位置上,需要向后移动,寻找合适的位置
if (nums[i] != 0) {
// 这个时候,原先 j 的值需要被 i 的值覆盖
nums[j] = nums[i];
// j 需要向后移动,寻找合适的位置
j++;
}
}
// 接下来,只需要把 j 及其后面所有的元素都设置为 0 就行,因为 j 指向是所有为 0 元素的第一个位置上
for (int i = j; i < nums.length; i++) {
// 都设置为 0
nums[i] = 0;
}
}
}
最大连续 1 的个数
给定一个二进制数组
nums
, 计算其中最大连续1
的个数。示例 1:
输入:nums = [1,1,0,1,1,1] 输出:3 解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
示例 2:
输入:nums = [1,0,1,1,0,1] 输出:2
提示:
1 <= nums.length <= 10^5
nums[i]
不是0
就是1
.
public class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
// 最后一个 0 所在的索引位置
int lastZero = -1;
// 结果
int ans = 0;
// 从左到右访问数组 nums
for (int i = 0; i < nums.length; i++) {
// 1、当前元素为 0 ,更新 lastZero
if (nums[i] == 0) {
lastZero = i;
}
// 2、否则说明当前元素为 1
else {
// 通过 lastZero 可以获取当前元素距离最前面的 1 的个数
// 对比之前的 ans ,更新获取最大值
ans = Math.max(ans, i - lastZero);
}
}
// 返回结果
return ans;
}
}
改变原有数组元素的值
题目内容:
实现一个方法 transform, 以数组为参数, 循环将数组中的每个元素 乘以 2 , 并设置到对应的数组元素上. 例如 原数组为 {1, 2, 3}, 修改之后为 {2, 4, 6}将数组里面的值 扩大2倍,有两种方式,一种是在自己本身上进行扩大,一种是新的数组中扩大。
/*import java.util.Arrays;
public class OnlineTest {
public static int[] transFrom(int[] a) {
for (int i = 0; i < a.length; i++) {
a[i] *= 2;
}
return a;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {1, 2, 3};
int[] ret = transFrom(array); //创建了一个变量和array同时指向array指向的那个数组,没创建新数组,但是多了个变量。
System.out.println(Arrays.toString(ret));
}
}*/
/*
import java.util.Arrays;
public class OnlineTest {
public static void transFrom(int[] a) {
for (int i = 0; i < a.length; i++) {
a[i] *= 2;
}
}//无需返回值,直接在原数组上扩大二倍
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {1, 2, 3};
transFrom(array);
System.out.println(Arrays.toString(array));
}
}*/
import java.util.Arrays;
public class OnlineTest {
public static int[] transFrom(int[] a) {
int[] tmpArray = new int[a.length];
for (int i = 0; i < a.length; i++) {
tmpArray[i] = a[i] * 2;
}
return tmpArray;
}//返回新的数组的地址,两个不同的数组空间
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {1, 2, 3};
int[] ret = transFrom(array);
System.out.println(Arrays.toString(ret));
}
}
奇数位于偶数之前
题目内容:
调整数组顺序使得奇数位于偶数之前。调整之后,不关心大小顺序。如数组:[1,2,3,4,5,6]
调整后可能是:[1, 5, 3, 4, 2, 6]
import java.util.Arrays;
public class OnlineTest {
public static void fun(int[] array) {
//在数组的两端的下标分别同时向中间逼近,前提:left < right 左边:如果是奇数就让指针++,右边:如果是偶数就让指针--,
int left = 0;
int right = array.length - 1;
while (left < right) {
while (left < right && array[left] % 2 != 0) {//奇数 如果全是奇数,left一直++,left>right,下标不准确,交换会出现错误
left++;
}
while (left < right && array[right] % 2 == 0) {//偶数 如果全是偶数,right一直--,right<left,下标不准确,交换会出现错误
right--;
}
//让左半部分的偶数和右半部分的奇数互换
int tmpArray = array[left];
array[left] = array[right];
array[right] = tmpArray;
}
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6};
fun(array);
System.out.println(Arrays.toString(array));
}
}
二分查找
题目内容:
给定一个有序整型数组, 实现二分查找
注意:二分查找仅限于有序的数据之中
import java.util.Arrays;
public class OnlineTest {
public static int myBinarySearch(int[] arr, int key) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;// 每次中间下标是要发生变化的,要放在循环
if (arr[mid] > key) {//查找的值小于中间值, 说明值在左边, 右下标变化
right = mid - 1;
} else if (arr[mid] < key) {//查找的值大于中间值, 说明值在右边, 左下标变化
left = mid + 1;
} else {
return mid; //找到了,返回下标
}
}
return -1; // 找不到返回-1
}
/*
* 局面
* 外接给我的任务、参数,称为局面
* 经过一定的处理,变成一个新局面,
* */
//递归写法:
public static int binarySearch(int[] arr, int left, int right, int target) {
int midIndex = left + ((right - left) >> 1);
int midValue = arr[midIndex];
//递归调用 出口:找到值,规则:往右走或者往左走
if (target > midValue) {//往右走
return binarySearch(arr, midIndex + 1, right, target);
} else if (target < midValue) {//往左走
return binarySearch(arr, left, midIndex - 1, target);
} else {
return midIndex;
}
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int ret1 = myBinarySearch(arr, 5);
int ret2 = myBinarySearch(arr, 6);
//递归调用:
int ret3 = binarySearch(arr, 0, 3, 2);//在指定的下标范围查找target
System.out.println(ret1);//4
System.out.println(ret2);//-1
System.out.println(ret3);//1
System.out.println("-----");
Arrays.sort(arr);// 注意:使用二分查找的前提是有序数组!!!
System.out.println(Arrays.binarySearch(arr, 5));//4
System.out.println(Arrays.binarySearch(arr, 6)); //-6 如果查找的数 > 最后一个元素,返回的值是:-(arr.length+1)
}
}
冒泡排序
题目内容:
给定一个整型数组, 实现冒泡排序(升序排序)
进一步优化,当数据在排序过程当中有序了,会在某一趟排序后,发现数据没有交换。
所以,每一趟排序完,都去检查是否发生了交换,没有交换证明数据已近有序,不需要再进行剩余趟数的排序了。
import java.util.Arrays;
public class OnlineTest {
//如果数很多,意味着要比较很多趟,假如在前几趟就已经有序了,后面很多趟都不需要继续比较了,再比较就是浪费
//假如第一趟有序了,第二趟没有发生交换才能说明当前数组有序
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) { //有n个数,要比较n-1趟
boolean flag = false; //每一趟都要有flag,因为不知道具体在哪一趟会有序
for (int j = 0; j < array.length - 1 - i; j++) { //每趟只需比较n - 1 - i个数,不需要n-1个数,因为每趟都会找出一个最大值往右边放,比较的个数就会少一个
if (array[j] > array[j + 1]) {
int tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
flag = true; //此趟发生了交换
}
if (flag == false) {//说明此趟没有进行交换,可判断当前数组有序,停止后面的比较
return;
}
}
}
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
}
两数之和
题目内容:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
本题最重要的一句话:假设每种输入只会对应一个答案
也就意味着不会有多个答案,暴力求解就是挨个匹配查找即可
如果想提升效率,需要用到哈希表来解答
import java.util.Arrays;
public class OnlineTest {
public static int[] findTarget(int[] nums, int target) {
int[] ret = {-1, -1};
//int[] ret = new int[2];
for (int i = 0; i < nums.length; i++) {//遍历这个数组
for (int j = i + 1; j < nums.length; j++) {//从第二个数开始遍历
if (nums[i] + nums[j] == target) {
/*ret = new int[]{i, j};*/
ret[0] = i;
ret[1] = j;
return ret;
}
}
}
return ret;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
int[] ret = findTarget(nums, 18);
System.out.println(Arrays.toString(ret));
}
}
只出现一次的数字
题目内容:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。示例 1:
输入: [2,2,1]
输出: 1
示例 2:输入: [4,1,2,1,2]
输出: 4本题主要考察运算符:异或。
异或的特点是:
1、n ^ n = 0;即两个相同的数字异或是0
2、0 ^ n = n;即0和任何数字进行异或,结果就是那个任何数字。
public int singleNumber(int[] nums) {
// 用异或运算的性质可以巧妙的解决这个问题,因为数组中只有一个数字出现一次
// 则其他出现两次的数字用异或运算后都是0,最终整个数组异或运算的结果即为所求。
int ret = 0;
for (int i : nums) {
ret ^= i;
}
return ret;
}
public class OnlineTest {
//0 ^ x = x x ^ x = 0 异或有交换律,所以最终会剩下只出现一次的数字
public static int findNum(int[] array) {
int ret = array[0];
for (int i = 1; i < array.length; i++) {
ret ^= array[i];
}
return ret;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {2, 2, 1};
int ret = findNum(array);
System.out.println(ret);
}
}
多数元素
题目内容:
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:输入:[2,2,1,1,1,2,2]
输出:2常规办法:数组中出现次数超过一半的数字,一定是排好序之后,中间位置的数字。
import java.util.Arrays;
public class OnlineTest {
public static int findMoreNum1(int[] array) {
//先进行排序,因为是找多数元素:在数组中出现次数>n/2的元素,所以数组中间的那个数一定是多数元素
Arrays.sort(array);
return array[array.length / 2];
}
public static int findMoreNum2(int[] array) {
int count = 0;
int tmp = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] == tmp) {
count++;
} else if (array[i] != tmp) {
count--;
} /*else if (count == 0) { //这儿必须是if,如果一直array[i] != tmp, count--之后等于-1,没有执行此语句了
count++;
tmp = array[i];
}*/
if (count == 0) {
//2 2 3 3 2,当count == 0时,说明此时已经遍历到第2个3了,此时count++之后=1,tmp = 3 i = 3
//最后一次遍历,i = 4,array[4] = 2 != tmp = 3,count--之后=0,走if(count == 0)这个分支,count又++之后=1
//此时赋值给tmp = array[4] = 2,即出现次数最多的那个数
//总结:哪个数让count == 0了,下次计数就从它开始。
count++;
tmp = array[i];
}
}
return tmp;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {2, 2, 3, 3, 2};
int ret1 = findMoreNum1(array);
int ret2 = findMoreNum2(array);
System.out.println(ret1);
System.out.println("---");
System.out.println(ret2);
}
}
存在连续三个奇数的数组
题目内容:
给你一个整数数组 arr,请你判断数组中是否存在连续三个元素都是奇数的情况:如果存在,请返回 true ;否则,返回 false 。示例 1:
输入:arr = [2,6,4,1]
输出:false
解释:不存在连续三个元素都是奇数的情况。
示例 2:
输入:arr = [1,2,34,3,4,5,7,23,12]
输出:true
解释:存在连续三个元素都是奇数的情况,即 [5,7,23] 。
数字是连续出现的,所以我们只需要定义一个计数器,如果连续出现的次数超过3,则返回true。
import java.util.Arrays;
public class OnlineTest {
public static boolean fun(int[] array) {
int count = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 != 0) {
count++;
if (count == 3) {
return true;
}
} else {
count = 0;
}
}
return false;
}
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
int[] array = {1,3,5,2,4,6};
boolean ret = fun(array);
System.out.println(ret);
}
}
合并两个有序数组
给你两个按 非递减顺序 排列的整数数组
nums1
和nums2
,另有两个整数m
和n
,分别表示nums1
和nums2
中的元素数目。请你 合并
nums2
到nums1
中,使合并后的数组同样按 非递减顺序 排列。注意:最终,合并后数组不应由函数返回,而是存储在数组
nums1
中。为了应对这种情况,nums1
的初始长度为m + n
,其中前m
个元素表示应合并的元素,后n
个元素为0
,应忽略。nums2
的长度为n
。示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。 合并结果是 [1] 。示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1 输出:[1] 解释:需要合并的数组是 [] 和 [1] 。 合并结果是 [1] 。 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109
class Solution {
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] = nums2[j];
j--;
k--;
} else {
nums1[k] = nums1[i];
i--;
k--;
}
}
//nums1走完了,nums2里面还有元素没拷贝到nums1
while (j >= 0) {
nums1[k] = nums2[j];
j--;
k--;
}
}
}
旋转数组
https://leetcode.cn/problems/rotate-array/description/?envType=list&envId=OoMH8xH5
给定一个整数数组
nums
,将数组中的元素向右轮转k
个位置,其中k
是非负数。示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右轮转 1 步:[7,1,2,3,4,5,6]
向右轮转 2 步:[6,7,1,2,3,4,5]
向右轮转 3 步:[5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
/*
解题思路:
按照题目所说,进行k次旋转,每次旋转移动一个字符
注意:k如果超过nums的长度时,对k取模
时间复杂度: O(KN)
思路没有问题,但是会时间复杂度会超
*/
/*
class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;
for(int i = 0; i < k; ++i){
// 将数组中前n-1个元素整体往后搬移一个位置
int last = nums[nums.length-1];
for(int j = nums.length-1; j >= 1; --j){
nums[j] = nums[j-1];
}
// 将搬移前最后一个元素放到数组0号位置
nums[0] = last;
}
}
}
*/
/*
解题思路:使用三次逆转法,让数组旋转k次
1. 整体逆置
2. 逆转子数组[0, k - 1]
3. 逆转子数组[k, size - 1]
*/
class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length-1);
reverse(nums, 0, k-1);
reverse(nums, k, nums.length-1);
}
public void reverse(int[] nums, int left, int right){
while(left < right){
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
right--;
}
}
}
数组元素循环右移(旋转数组ACM模式版)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
int M = scanner.nextInt();
int[] A = new int[N];
for (int i = 0; i < N; i++) {
A[i] = scanner.nextInt();
}
rotate(A, N, M);
for (int i = 0; i < N; i++) {
System.out.print(A[i] + " ");
}
}
public static void reverse(int[] arr, int start, int end) {
while (start < end) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
public static void rotate(int[] arr, int N, int M) {
reverse(arr, 0, N - 1);
reverse(arr, 0, M - 1);
reverse(arr, M, N - 1);
}
}
找出距离最近的点对
这个过程有点类似选择排序,每次选择距离最小的点对。
public class MinDistance {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
double[][] arr = {
{1, 1},
{1, 0.5},
{2, 1},
{3, 4},
{2.8, 1},
{1, 7},
{4, 1.8},
{1.1, 9},
{3, 5},
{5, 6},
};
double minDis = Double.MAX_VALUE;
int minI = 0;
int minJ = 0;
//依次把每个点作为起点
for (int i = 0; i < arr.length; i++) {
//计算和其余的点的距离,保留更小的距离
for (int j = i+1; j < arr.length; j++) {
//(x1,y1) (x2,y2),求这两个点的距离公式:((x1-x2)^2+(y1,y2)^2)^(1/2)
double x1 = arr[i][0];
double y1 = arr[i][1];
double x2 = arr[j][0];
double y2 = arr[j][1];
double dis = Math.sqrt(Math.pow((x1-x2),2)+Math.pow((y1-y2),2));//Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
if(dis < minDis){//如果这两个点的距离小于double类型数据的最大值(在这儿必然成立)
minDis = dis;//将当前计算出来的距离赋值给minDis
//通过上面的循环+判断,获取当前二维数组中两个点距离最小的索引并赋值
minI = i;//第一个点更新为i,确定了i就相当于找到了x1,y1,起定位作用
minJ = j;//第二个点更新为j,确定了j就相当于找到了x2,y2,起定位作用
}
}
}
System.out.println("min distance:" + minDis + ",i:" + minI + ",j:" + minJ);//min distance:0.5,i:0,j:1
//0是指{1, 1}
//1是指{1, 0.5}
}
}
【模拟】小红书2023秋招提前批-小红的数组构造
题目描述
小红希望你构造一个数组,满足以下条件:
1. 数组共有
n
个元素,且所有元素两两不相等。2. 所有元素的最大公约数等于
k
。3. 所有元素之和尽可能小。
请你输出数组元素之和的最小值。
输入描述
两个正整数
n
和k
1 ≤ n, k ≤ 10^5
输出描述
一个正整数,代表数组元素之和的最小值。
示例一
输入
3 1
输出
6
说明
构造数组
[1, 2, 3]
为满足题意的数组示例二
输入
2 6
输出
18
说明
构造数列
[6, 12]
为满足题意的数列。本题较为简单,构造
[k, 2k, 3k, ..., nk]
这样的数组即为符合要求的数组(这个结论很容易用反证法证明),使用等差数列求和公式可知该数组的和为
k + 2k + 3k + ... + nk = (1 + 2 + 3 + ... + n) * k = (1 + n) * n // 2 * k
故对于输入的两个参数
n
和k
,只需要输出(1 + n) * n * k / 2
即为答案。注意,要用
long
而不是int
,防止计算结果过大而爆内存。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long n = scanner.nextLong();
long k = scanner.nextLong();
System.out.println((1 + n) * n * k / 2);
scanner.close();
}
}
【模拟】科大讯飞2023非凡计划-将企鹅击落水中最小的力
题目描述
牛牛发明了一款破冰行动的游戏,假设在河面上,有
n
块紧密相连的冰块。标号1-n
。将一只企鹅放到任意一个标号为k
的冰块上,现在通过打破一些冰块来使得企鹅掉落河中,然而他无法打破企鹅所踩的冰块k
。每个冰块都有不同的强度Ai
,所以需要一个等于Ai
的力来打破冰。当两侧的冰块都没有与河两岸连接时,则冰块会掉落到河中。输入描述
第一行给出
n
,表示冰块的数量。第二行中,按顺序给出代表打破第
i
块冰块所需的力的Ai
。题目保证企鹅所在的地方用-1
表示,没有企鹅位于冰块1
或冰块n
的情况。
3<=n<=2*10^5
输出描述
输出可以击落企鹅到水中的最小力。
示例
输入
5 7 -1 6 2 5
输出
9
说明
假设冰块的索引从
1
开始。冰块1
和冰块4
可以通过分别施加7
和2
的力来打破,此时企鹅所在的冰块2
和冰块3
一起掉落河中。所以企鹅将落下。所以,可以使企鹅掉落的最小力是2+7=9
。解题思路
本题是非常简单的模拟题,按照以下步骤完成即可:
找到企鹅所在的冰块的索引
k
分别计算位于冰块
k
前后的两个子数组的最小值将两个最小值相加,即为答案
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = sc.nextInt();
}
sc.close();
int k = 0;
for (int i = 0; i < n; i++) {
if (nums[i] == -1) {//确定当企鹅的位置
k = i;
break;
}
}
//找[0,k-1]的min
int leftMin = Integer.MAX_VALUE;
for (int i = 0; i < k; i++) {
leftMin = Math.min(leftMin, nums[i]);
}
//找[k+1,n-1]的min
int rightMin = Integer.MAX_VALUE;
for (int i = k + 1; i < n; i++) {
rightMin = Math.min(rightMin, nums[i]);
}
int answer = leftMin + rightMin;
System.out.println(answer);
}
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入整数的个数 n: ");
int n = scanner.nextInt();
int[] numbers = new int[n];
// 依次输入 n 个整数
for (int i = 0; i < n; i++) {
System.out.print("请输入第 " + (i + 1) + " 个整数: ");
numbers[i] = scanner.nextInt();
}
int max = Integer.MIN_VALUE;
// 寻找最大值
for (int i = 0; i < n; i++) {
max = Math.max(max, numbers[i]);
}
System.out.println("输入的 " + n + " 个整数中最大的数是: " + max);
}
}
【模拟】美团2023秋招-小美走公路
【模拟】美团2023秋招-小美走公路
题目描述与示例
题目描述
有一个环形的公路,上面共有
n
站,现在给定了顺时针第i
站到第i
+
1
站之间的距离(特殊的,也给出了第n
站到第1
站的距离)。小美想沿着公路第x
站走到第y
站,她想知道最短的距离是多少?输入描述
第一行输入一个正整数
n
,代表站的数量。第二行输入
n
个正整数ai
,前n
-
1
个数代表顺时针沿着公路走,i
站到第i
+
1
站之间的距离;最后一个正整数代表顺时针沿着公路走,第n
站到第1
站的距离。第三行输入两个正整数
x
和y
,代表小美的出发地和目的地。
1 <= n <= 10^5
1 <= a <= 10^9
1 <= x, y <= n
输出描述
一个正整数,代表小美走的最短距离。
示例一
输入
3
1 2 2
2 3
输出
2
说明
示例二
输入
3
1 2 2
1 3
输出
2
说明
解题思路
注意,本题和LeetCode1184.公交站间的距离完全一致。
题目要求计算最短距离,实际上一共只有两条可能的路径:顺时针从
x
走到y
和逆时针从x
走到y
。因此只需要计算顺时针和逆时针的两个结果进行比较即可。
唯一需要注意的地方就是边界,即第
n
站到第1
站的距离的计算,这里可以通过逻辑判断是否到达边界,也可以通过取余的方式避免边界问题。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 读取站的数量
int[] distances = new int[n]; // 存储各站之间的距离
for (int i = 0; i < n; i++) {
distances[i] = scanner.nextInt(); // 读取各站之间的距离
}
int x = scanner.nextInt(); // 读取出发站
int y = scanner.nextInt(); // 读取目的站
int clockwiseDistance = 0; // 顺时针方向的距离
int counterClockwiseDistance = 0; // 逆时针方向的距离
scanner.close();
// 注意x和y都要减1,因为在数组下标我们是从0开始的
// 计算顺时针距离
// 注意i = (i + 1) % n,表示任何一个数取余n,都是<n的,也就是说i不会越界
for (int i = x - 1; i != y - 1; i = (i + 1) % n) {
clockwiseDistance += distances[i];
}
// 计算逆时针距离
for (int i = y - 1; i != x - 1; i = (i + 1) % n) {
counterClockwiseDistance += distances[i];
}
// 输出最短距离
System.out.println(Math.min(clockwiseDistance, counterClockwiseDistance));
}
}
【模拟】OPPO2023秋招提前批-小欧数组求和
题目描述
小欧拿到了一个数组,她有
q
次操作,每次操作修改一个元素。小欧希望每次修改后得到当前数组所有元素之和。你能帮帮她吗?输入描述
第一行输入两个正整数
n
和q
,代表数组的大小和操作次数。第二行输入
n
个正整数ai
,代表小欧拿到的初始数组。接下来的
q
行,每行输入两个正整数i
和x
,代表将第i
个元素修改为x
。输出描述
输出
q
行,每行输出一个正整数,代表当前数组元素之和。示例
输入
5 3
1 2 3 4 5
2 3
3 3
5 1
输出
16
16
12
说明
第一次修改后,数组变成
[1,3,3,4,5]
,元素之和为16
。第二次修改后,数组变成
[1,3,3,4,5]
,元素之和为16
。第三次修改后,数组变成
[1,3,3,4,1]
,元素之和为12
。解题思路
本题属于非常简单的模拟题。
对于某一次特定的修改,假设我们已知上一次修改后的数组和为
nums_sum
,而本次修改将nums[i]
修改为x
,那么修改后的和应该为nums_sum-nums[i]+x
。而此处修改后的和又可以作为下一次修改的上一次修改后的数组和来使用,即存在
nums_sum = nums_sum - nums[i] + x
需要被在本次修改后输出。同时
nums[i]
需要修改为x
。本题要特别注意,题目最后
q
行输入的索引值是从1
开始的,故映射到数组的索引值,必须进行i -= 1
的修改。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int q = scanner.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt();
}
int numsSum = 0;
// 计算初始数组numsSum的和
for (int num : nums) {
numsSum += num;
}
// 存储结果的值
int[] ans = new int[q];
// 循环q次,修改q次
for (int j = 0; j < q; j++) {
int i = scanner.nextInt() - 1;// 要修改的元素的下标
int x = scanner.nextInt();// 要修改的值
numsSum -= nums[i]; //先让初始和减去要修改的元素原本的值
nums[i] = x;// 修改的值
numsSum += x;// 更新 新的数组和
ans[j] = numsSum;// 更新结果变量
}
for (int num : ans) {
System.out.println(num);
}
}
}
【模拟】大疆2023秋招-农田中作物的最大产量
题目描述
给定一个二维数组
cropField
,其中每个元素代表农田中对应位置的作物产量,每个位置上的作物经过灌溉可使产量加1
倍(即对应位置作物产量数*2
),重复灌溉同一位置不能多次加倍。现在需要修建
2
条灌溉渠道,1
条从上到下灌溉,1
条从左到右灌溉。请确定哪行和哪列应该被灌溉,使得农田中作物的总产量最大化,并输出最大化的总产量。
输入描述
第一行输入两个数字
n
和m
,表示二维数组的行数和列数接下来的
n
行输入二维数组。输出描述
一个整数,表示最大产量
示例
输入
4 4
2 3 1 4
1 2 0 3
4 2 1 7
3 1 4 2
输出
64
说明
选择倒数第二行和最后一列进行灌溉。
选择最后一行和最后一列进行灌溉。
解题思路
模拟题,首先搞懂题意,题干要求我们求最大值,这个最大值其实是在整个二维数组加起来之上然后其中一行翻倍,其中一列翻倍,但是公共交点只能翻倍一次
然后可以使用行列维度的
sum
数组提前存储每一行每一列的值提供使用。具体的逻辑如下:
首先,读取输入,获取农田的行数和列数,然后创建一个二维数组来存储农田的作物产量。
接着,遍历二维数组,计算每行和每列的总产量,并累加到一个计数器中。
初始化一个变量,用于记录最大产量。
使用两层循环遍历每个位置,计算当前位置所在行和列的总产量,减去当前位置的作物产量,得到当前位置的最大产量。然后更新最大产量变量。
最后,将最大产量与总产量相加,得到最终结果,并输出。
思考:如果题目修改为,公共交点可以多次翻倍,那么应该用什么方法来解决本问题?
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int m = scanner.nextInt();
int n = scanner.nextInt();
//存储数值
int[][] cropField = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cropField[i][j] = scanner.nextInt();
}
}
long count = 0;
//行的总产量的数组,每个元素代表一行的总产量
int[] rowTotalYield = new int[m];
//列的总产量的数组,每个元素代表一列的总产量
int[] colTotalYield = new int[n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
rowTotalYield[i] += cropField[i][j];
colTotalYield[j] += cropField[i][j];
}
// 注意这个count不能放入内层循环
count += rowTotalYield[i];// 没灌溉之前的总产量
}
int maxTotalYield = 0;// 记录最大产量
//枚举计算
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
//cropField[i][j]代表交点
int totalYield = rowTotalYield[i] + colTotalYield[j] - cropField[i][j];
maxTotalYield = Math.max(maxTotalYield, totalYield);
}
}
maxTotalYield += count;
System.out.println(maxTotalYield);
}
}