答案摘抄自:
https://leetcode-cn.com/tag/array/
给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。
你可以按任意顺序返回答案。
示例 1:
输入:[“bella”,“label”,“roller”]
输出:[“e”,“l”,“l”]
示例 2:
输入:[“cool”,“lock”,“cook”]
输出:[“c”,“o”]
方法:hash 数组
定义一个整形数组 countercounter 记录交集字母出现的次数,下标为 keykey,值为交集字母的次数。
以第零个单词 A[0]A[0] 为基准,确定该单词的字母频次,记录于 countercounter 数组中。
然后从第一个单词 A[1]A[1] 开始遍历,并总结出这些单词的频次,记录于本次遍历的局部变量数组 tempArrtempArr 中。
因为题目要求统计的是所有单词中的出现频次多的字母,所以这些字母必须都在全部单词中出现过才能作为结果的一部分。
以字母 aa 在 A[0] = ccc 和 A[1]=cca 为出现次数为例:
在 A[0]A[0] 中出现 00 次,
在 A[1]A[1] 中出现 11 次。
那么字母 aa 在结果列表中将出现 00 次。
最后把 countercounter 的下标(即 keykey)与字符 aa 的 ASCIIASCII 码相加,转化为具体的字符存入 ListList 中。
综上所述,这本质上是一个在所有单词中求出现频次大于 11 的字母的交集的问题。
public List<String> commonChars(String[] A) {
int[] counter = new int[26];
for(char c : A[0].toCharArray()) // 统计第1个单词各个字母的词频
counter[c - 'a']++;
for (int i = 1; i < A.length; i++) {
int[] tempArr = new int[26];
for (char c : A[i].toCharArray()) {
tempArr[c - 'a']++; // 后一个单词的字母词频
}
for (int j = 0; j < 26; j++) {
if(tempArr[j] < counter[j])
counter[j] = tempArr[j]; // 字母频次的交集
}
}
LinkedList<String> resList = new LinkedList<>();
for (int i = 0; i < 26; i++) {
while (counter[i]-- > 0) { // counter[0] 记录字母a的出现次数
resList.addLast("" + (char)('a' + i));
}
}
return resList;
}
在歌曲列表中,第 i 首歌曲的持续时间为 time[i] 秒。
返回其总持续时间(以秒为单位)可被 60 整除的歌曲对的数量。形式上,我们希望索引的数字 i < j 且有 (time[i] + time[j]) % 60 == 0。
示例 1:
输入:[30,20,150,100,40]
输出:3
解释:这三对的总持续时间可被 60 整数:
(time[0] = 30, time[2] = 150): 总持续时间 180
(time[1] = 20, time[3] = 100): 总持续时间 120
(time[1] = 20, time[4] = 40): 总持续时间 60
示例 2:
输入:[60,60,60]
输出:3
解释:所有三对的总持续时间都是 120,可以被 60 整数。
方法一:暴力枚举(超时)
对于每一个数字,我的都检查其后面的所有数字,看是否有数字能与本数字组成符合要求的一对数。
public int numPairsDivisibleBy60(int[] time) {
int count = 0;
for (int i = 0; i < time.length; i++) {
for (int j = i + 1; j < time.length; j++)
if ((time[i] + time[j]) % 60 == 0)
count++;
}
return count;
}
复杂度分析:
时间复杂度:O(N^2),对于每一个数,都要检查其末尾的所有数字。
空间复杂度:O(1),使用了常数级别的空间。
解法二:取余求差法
其实我们不必没经过一个数字就去检查后面的所有数字,而是采用数组存储每一个数字的补数。这样问题相当于特殊条件的两数之和问题:计算相加后等于 6060 的倍数的数字对的个数。
定义变量 remainder 记录当前余数,大小为60的counter数组计数,从头遍历这个数组[30,20,150,100,40],我们只需要统计某个数字 time[i] 的之前的数字 60-i 的数量。以下幻灯片体现了整个过程:
public int numPairsDivisibleBy60(int[] time) {
int count = 0;
int[] counter = new int[60];
for (int t : time) {
int remainder = t % 60; //余数
if (remainder == 0)
count += counter[remainder]; //如60,120,180这些只能相互成对。
else if (counter[60-remainder] > 0){
count += counter[60-remainder]; //如30,150
}
counter[remainder]++;
}
return count;
}
复杂度分析:
时间复杂度:O(N),N 为数组 time 的大小。
空间复杂度:O(1),使用了常数级别的空间。
给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。
形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + … + A[i] == A[i+1] + A[i+2] + … + A[j-1] == A[j] + A[j-1] + … + A[A.length - 1]) 就可以将数组三等分。
示例 1:
输出:[0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1
示例 2:
输入:[0,2,1,-6,6,7,9,-1,2,0,1]
输出:false
示例 3:
输入:[3,3,6,5,-2,2,5,1,-9,4]
输出:true
解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4
方法:双指针
数组元素的总和 sum 不是3的倍数,直接返回false
使用双指针left,right, 从数组两头开始一起找,节约时间
当 left + 1 < right 的约束下,可以找到数组两头的和都是 sum/3,那么中间剩下的元素和就一定也是sum/3
(left + 1 < right的约束就是要中间有剩下的元素,使用left < right的约束,数组可能可以恰好被划分成两部分,中间没有元素)
class Solution {
public boolean canThreePartsEqualSum(int[] A) {
int sum = 0;
for(int i : A){
sum += i;
}
if(sum%3 != 0){
// 总和不是3的倍数,直接返回false
return false;
}
// 使用双指针,从数组两头开始一起找,节约时间
int left = 0;
int leftSum = A[left];
int right = A.length - 1;
int rightSum = A[right];
// 使用left + 1 < right 的原因,防止只能将数组分成两个部分
// 例如:[1,-1,1,-1],使用left < right作为判断条件就会出错
while(left + 1 < right){
if(leftSum == sum/3 && rightSum == sum/3){
// 左右两边都等于 sum/3 ,中间也一定等于
return true;
}
if(leftSum != sum/3){
// left = 0赋予了初值,应该先left++,在leftSum += A[left];
leftSum += A[++left];
}
if(rightSum != sum/3){
// right = A.length - 1 赋予了初值,应该先right--,在rightSum += A[right];
rightSum += A[--right];
}
}
return false;
}
}
给定由若干 0 和 1 组成的数组 A。我们定义 N_i:从 A[0] 到 A[i] 的第 i 个子数组被解释为一个二进制数(从最高有效位到最低有效位)。
返回布尔值列表 answer,只有当 N_i 可以被 5 整除时,答案 answer[i] 为 true,否则为 false。
示例 1:
输入:[0,1,1]
输出:[true,false,false]
解释:
输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。
示例 2:
输入:[1,1,1]
输出:[false,false,false]
示例 3:
输入:[0,1,1,1,1,1]
输出:[true,false,false,false,true,false]
示例 4:
输入:[1,1,1,0,1]
输出:[false,false,false,false,false]
class Solution {
public List<Boolean> prefixesDivBy5(int[] A) {
List<Boolean> res = new ArrayList<Boolean>();
int tail = 0;
for(int i: A) {
tail = tail * 2 + i;
tail = (tail > 9) ? tail - 10 : tail;
if(tail == 0 || tail == 5) {
res.add(true);
} else {
res.add(false);
}
}
return res;
}
}
给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
注意:每次拼写时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。
示例 1:
输入:words = [“cat”,“bt”,“hat”,“tree”], chars = “atach”
输出:6
解释:
可以形成字符串 “cat” 和 “hat”,所以答案是 3 + 3 = 6。
示例 2:
输入:words = [“hello”,“world”,“leetcode”], chars = “welldonehoneyr”
输出:10
解释:
可以形成字符串 “hello” 和 “world”,所以答案是 5 + 5 = 10。
方法一:
//统计字符表中各自字符所出现的次数;
//对词汇表中的每个单词也做上述统计;
//比较1,2;如果1中各个字符出现的次数总是大于等于2,则符合要求;
class Solution {
public int countCharacters(String[] words, String chars) {
int[] charsOfCase = new int[26];//统计字符表中的字符
int count = 0;//单词长度之和
for(char c : chars.toCharArray()){//统计字符表中的字符
charsOfCase[c-'a']++;
}
for(String str : words){
int[] temp = new int[26];
boolean flag = true;
for(char c : str.toCharArray()){//存储统计词汇表中单词的字符
temp[c-'a']++;
}
for(int i = 0; i < charsOfCase.length; i++){//对比两个统计数据
if(temp[i] > charsOfCase[i]){
flag = false;
break;
}
}
if(flag) count += str.length();
}
return count;
}
}
方法二:HashMap
class Solution
{
public int countCharacters(String[] words, String chars)
{
if(words.length==0||chars.length()==0)return 0;
char[] ch=chars.toCharArray();
HashMap<Character,Integer> map=new HashMap<>();
int ans=0;
for(char c:ch)
{
map.put(c,map.getOrDefault(c,0)+1);
}
int len=words.length;
for(int i=0;i<=len-1;i++)
{
boolean flag=false;
char[] cc=words[i].toCharArray();
HashMap<Character,Integer> temp=(HashMap)map.clone();
for(int j=0;j<=cc.length-1;j++)
{
if(!map.containsKey(cc[j])){flag=true;break;}//no such,break
else
{
if(temp.get(cc[j])>=1)temp.put(cc[j],temp.get(cc[j])-1);//enough,then -1
else {flag=true;break;}//not enough,break
}
}
if(flag)continue;
ans+=words[i].length();
}
return ans;
}
}
学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。
请你返回能让所有学生以 非递减 高度排列的最小必要移动人数。
注意,当一组学生被选中时,他们之间可以以任何可能的方式重新排序,而未被选中的学生应该保持不动。
示例1:
输入:heights = [1,1,4,2,1,3]
输出:3
解释:
当前数组:[1,1,4,2,1,3]
目标数组:[1,1,1,2,3,4]
在下标 2 处(从 0 开始计数)出现 4 vs 1 ,所以我们必须移动这名学生。
在下标 4 处(从 0 开始计数)出现 1 vs 3 ,所以我们必须移动这名学生。
在下标 5 处(从 0 开始计数)出现 3 vs 4 ,所以我们必须移动这名学生。
示例 2:
输入:heights = [5,1,2,3,4]
输出:5
示例 3:
输入:heights = [1,2,3,4,5]
输出:0
class Solution {
public int heightChecker(int[] heights) {
int[] arr = new int[101];
for (int height : heights) {
arr[height]++;
}
int count = 0;
for (int i = 1, j = 0; i < arr.length; i++) {
while (arr[i]-- > 0) {
if (heights[j++] != i) count++;
}
}
return count;
}
}
给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。
要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。
示例 1:
输入:[1,0,2,3,0,4,5,0]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]
示例 2:
输入:[1,2,3]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,2,3]
class Solution {
public void duplicateZeros(int[] arr) {
int i = 0 , zero = 0 , l = arr.length-1;
while(i <= l){
if(arr[i] == 0)
zero++;
if(i + zero >= l)
break;
i++;
}
while(i >=0){
arr[l--] = arr[i];
if(arr[i] == 0 && (i + zero <= arr.length-1) )//<=用来判断最后一个如果是0是否超出边界
arr[l--] = arr[i];
i--;
}
}
}
给你两个数组,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。
示例:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]
方法:桶排序
class Solution {
public int[] relativeSortArray(int[] arr1, int[] arr2) {
//由于arr1的可能取值为0-1000,共1001个取值,不算一个很大的取值范围,所以可以利用桶式排序
int[] bucket = new int[1001];
//计数每个桶有多少个数,也就是每个值在数组中有几个
for(int num:arr1){
bucket[num]++;
}
//由于重新排序不会改变数组的长度,所以可以利用原数组,可以节省空间复杂度
int i = 0;
//由于排序是按照相对顺序进行排序,所以,首先根据arr2中的桶的顺序,依次从对应的桶中取数到arr1中
//并注意,每拿出一个数,需要将对桶中的数的个数进行-1操作
for(int num:arr2){
while(bucket[num]-- > 0){
arr1[i++] = num;
}
}
//未在arr2中的桶中的数,按照桶号升序进行输出,所以进行二次遍历
for(int j = 0; j < 1001; ++j){
while(bucket[j]-- > 0){
arr1[i++] = j;
}
}
return arr1;
}
}
给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。
请你找到并返回这个整数
示例:
输入:arr = [1,2,2,6,6,6,6,7,10]
输出:6
方法:双指针
class Solution {
public int findSpecialInteger(int[] arr) {
int before = arr.length / 4 + 1;
for(int i = 0; before < arr.length; i++, before++){
if(arr[i] == arr[before]) return arr[i];
}
return arr[arr.length-1];
}
}
给你一个由一些多米诺骨牌组成的列表 dominoes。
如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。
形式上,dominoes[i] = [a, b] 和 dominoes[j] = [c, d] 等价的前提是 ac 且 bd,或是 ad 且 bc。
在 0 <= i < j < dominoes.length 的前提下,找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。
示例:
输入:dominoes = [[1,2],[2,1],[3,4],[5,6]]
输出:1
class Solution {
public int numEquivDominoPairs(int[][] dominoes) {
int ans = 0;
int[] cp = new int[100];
for(int[] arr:dominoes){
Arrays.sort(arr);
ans+=cp[arr[0]*10+arr[1]]++;
}
return ans;
}
}
给你一个数组 arr ,请你将每个元素用它右边最大的元素替换,如果是最后一个元素,用 -1 替换。
完成所有替换操作后,请你返回这个数组。
示例:
输入:arr = [17,18,5,4,6,1]
输出:[18,6,6,6,1,-1]
方法:逆序遍历
从右往左遍历,先记录右边最大值 rightMax 为最后一个值,向左每次更新 rightMax,使用变量 t 先记住当前 arr[i] 就可以了。
class Solution {
public int[] replaceElements(int[] arr) {
int rightMax = arr[arr.length - 1];
arr[arr.length - 1] = -1;
for (int i = arr.length - 2; i >= 0; i--) {
int t = arr[i];
arr[i] = rightMax;
if (t > rightMax)
rightMax = t;
}
return arr;
}
}
给你一个以行程长度编码压缩的整数列表 nums 。
考虑每对相邻的两个元素 freq, val] = [nums[2i], nums[2i+1]] (其中 i >= 0 ),每一对都表示解压后子列表中有 freq 个值为 val 的元素,你需要从左到右连接所有子列表以生成解压后的列表。
请你返回解压后的列表。
示例:
输入:nums = [1,2,3,4]
输出:[2,4,4,4]
解释:第一对 [1,2] 代表着 2 的出现频次为 1,所以生成数组 [2]。
第二对 [3,4] 代表着 4 的出现频次为 3,所以生成数组 [4,4,4]。
最后将它们串联到一起 [2] + [4,4,4] = [2,4,4,4]。
示例 2:
输入:nums = [1,1,2,3]
输出:[1,3,3]
解题思路:
//难点在于声明返回数组的大小;
//为了得到返回数组的大小,首先遍历一遍数组nums,计算索引为偶数的数字之和得到返回数组的大小len;
//声明返回数组result的大小为len;
//再遍历一遍数组,进行解码,解码原则为—索引为奇数i的数字nums[i]重复nums[i-1]次,并依次放入返回数组result中;
//最后返回out;
class Solution {
public int[] decompressRLElist(int[] nums) {
int i=0;
int len=0;
while(i<nums.length){
len+= nums[i];
i+=2;
}
int[] result = new int[len];
int index=0;
i=0;
while(i<nums.length){
for(int k=0;k<nums[i];k++){
result[index]=nums[i+1];
index++;
}
i+=2;
}
return result;
}
}
给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。
序号代表了一个元素有多大。序号编号的规则如下:
序号从 1 开始编号。
一个元素越大,那么序号越大。如果两个元素相等,那么它们的序号相同。
每个数字的序号都应该尽可能地小。
示例 1:
输入:arr = [40,10,20,30]
输出:[4,1,2,3]
解释:40 是最大的元素。 10 是最小的元素。 20 是第二小的数字。 30 是第三小的数字。
示例 2:
输入:arr = [100,100,100]
输出:[1,1,1]
解释:所有元素有相同的序号。
示例 3:
输入:arr = [37,12,28,9,100,56,80,5,12]
输出:[5,3,4,2,8,6,7,1,3]
class Solution {
public int[] arrayRankTransform(int[] arr) {
// 1. 边界条件判断
if (arr == null || arr.length == 0) {
return arr;
}
// 2. 去重
Set<Integer> set = new HashSet<>();
for (int i : Arrays.copyOf(arr, arr.length)) {
set.add(i);
}
int[] copy = new int[set.size()];
int index = 0;
for (int i : set) {
copy[index++] = i;
}
// 3. 排序
Arrays.sort(copy);
// 4. 装入Map
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < copy.length; i++) {
map.put(copy[i], i + 1);
}
//5. 得到结果
for (int i = 0; i < arr.length; i++) {
int value = arr[i];
arr[i] = map.get(value);
}
return arr;
}
}
我们来定义一个函数 f(s),其中传入参数 s 是一个非空字符串;该函数的功能是统计 s 中(按字典序比较)最小字母的出现频次。
例如,若 s = “dcce”,那么 f(s) = 2,因为最小的字母是 “c”,它出现了 2 次。
现在,给你两个字符串数组待查表 queries 和词汇表 words,请你返回一个整数数组 answer 作为答案,其中每个 answer[i] 是满足 f(queries[i]) < f(W) 的词的数目,W 是词汇表 words 中的词。
示例 1:
输入:queries = [“cbd”], words = [“zaaaz”]
输出:[1]
解释:查询 f(“cbd”) = 1,而 f(“zaaaz”) = 3 所以 f(“cbd”) < f(“zaaaz”)。
示例 2:
输入:queries = [“bbb”,“cc”], words = [“a”,“aa”,“aaa”,“aaaa”]
输出:[1,2]
解释:第一个查询 f(“bbb”) < f(“aaaa”),第二个查询 f(“aaa”) 和 f(“aaaa”) 都 > f(“cc”)。
class Solution {
public int[] numSmallerByFrequency(String[] queries, String[] words) {
int[] result = new int[queries.length];
for (int i = 0; i < queries.length; i++) {
int countMinQuery = countMinCode(queries[i]);
for (String word : words) {
if (countMinQuery < countMinCode(word)) {
result[i] ++;
}
}
}
return result;
}
/**
* 统计最小出现频次
* @param s
* @return
*/
public static int countMinCode(String s) {
char min = s.charAt(0);
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) < min) {
min = s.charAt(i);
count = 0;
}
if (s.charAt(i) == min) {
count++;
}
}
return count;
}
}
环线上的公交车都可以按顺时针和逆时针的方向行驶。
返回乘客从出发点 start 到目的地 destination 之间的最短距离。
示例 1:
输入:distance = [1,2,3,4], start = 0, destination = 1
输出:1
解释:公交站 0 和 1 之间的距离是 1 或 9,最小值是 1。
示例 2:
输入:distance = [1,2,3,4], start = 0, destination = 2
输出:3
解释:公交站 0 和 2 之间的距离是 3 或 7,最小值是 3。
示例 3:
输入:distance = [1,2,3,4], start = 0, destination = 3
输出:4
解释:公交站 0 和 3 之间的距离是 6 或 4,最小值是 4。
解题思路:
求两点之间的距离就只有两种方式,一种是从 start 到 destination,另一种是从 destination 到 start。
求两者的较小者即可。
class Solution {
public int distanceBetweenBusStops(int[] distance, int start, int destination) {
// 记录从 start 到 destination 的距离
int len1 = 0;
// 记录从 destination 到 start 的距离
int len2 = 0;
int cur1 = start;
int cur2 = destination;
while(cur1 != destination) {
// 进行记录
len1 += distance[cur1];
// 更新起点
cur1 = (cur1 + 1) % distance.length;
}
while(cur2 != start) {
// 进行记录
len2 += distance[cur2];
// 更新起点
cur2 = (cur2 + 1) % distance.length;
}
return Math.min(len1, len2);
}
}
给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。
输入为三个整数:day、month 和 year,分别表示日、月、年。
您返回的结果必须是这几个值中的一个 {“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”}。
示例 1:
输入:day = 31, month = 8, year = 2019
输出:“Saturday”
示例 2:
输入:day = 18, month = 7, year = 1999
输出:“Sunday”
示例 3:
输入:day = 15, month = 8, year = 1993
输出:“Sunday”
解题思路:
1、设置一个num变量计算总天数
2、计算71年到现在经过的所有年份的天数,闰年366.平年365,加到num中
3、计算1月到现在每个月的总天数,加到num中
4、num中加上day
5、将num转化为星期即可
class Solution {
public String dayOfTheWeek(int day, int month, int year) {
int num=0;
int days[]=new int[]{31,28,31,30,31,30,31,31,30,31,30,31};
//计算年份
for(int i=1971;i<year;i++) {
if(i%4==0&&i%100!=0||i%400==0)
num+=366;
else
num+=365;
}
if(year%4==0&&year%100!=0||year%400==0)
days[1]=29;
for(int i=1;i<month;i++)
{
num+=days[i-1];
}
num+=day;
String[] ans_day=new String[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
return ans_day[(num+4)%7];
}
}
给你个整数数组 arr,其中每个元素都 不相同。
请你找到所有具有最小绝对差的元素对,并且按升序的顺序返回。
示例 1:
输入:arr = [4,2,1,3]
输出:[[1,2],[2,3],[3,4]]
示例 2:
输入:arr = [1,3,6,10,15]
输出:[[1,3]]
示例 3:
输入:arr = [3,8,-10,23,19,-4,-14,27]
输出:[[-14,-10],[19,23],[23,27]]
class Solution {
public List<List<Integer>> minimumAbsDifference(int[] arr) {
ArrayList<List<Integer>> list=new ArrayList<>();
int min=Integer.MAX_VALUE;
Arrays.sort(arr);
for(int i=0;i<arr.length-1;i++){
if(Math.abs(arr[i]-arr[i+1])<min)
min=Math.abs(arr[i]-arr[i+1]);
}
for (int i = 1; i < arr.length; i++) {
if (Math.abs(arr[i] - arr[i - 1]) == min) {
ArrayList<Integer>list1=new ArrayList<>();
list1.add(arr[i - 1]);
list1.add(arr[i]);
list.add(list1);
}
}
System.out.println(min);
return list;
}
}
数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。
你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):
将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。
最开始的时候,同一位置上也可能放着两个或者更多的筹码。
返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。
示例 1:
输入:chips = [1,2,3]
输出:1
解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。
示例 2:
输入:chips = [2,2,2,3,3]
输出:2
解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。
这道题其实就是统计奇数和偶数的个数,并且返回个数较少的。
因为移动2次代价为0,那么可以看成是
1)把全部的奇数移动一个位为偶数,此处不考虑怎么移动,
因为移动为偶数后全部移动到一个地方的步数肯定是偶数,那么不计算代价。
2)把全部的偶数移动一个位为奇数,此处不考虑怎么移动,
因为移动为奇数后全部移动到一个地方的步数肯定是偶数,那么不计算代价。
综上所述,代价就是把奇数/偶数移动为偶数/奇数的代价,
也就是奇数/偶数的个数,那么要想代价最小的话,就看奇数少还是偶数少。
class Solution {
public int minCostToMoveChips(int[] chips) {
int odd = 0,even = 0;
for(int data : chips)
if(data % 2 == 1)
odd++;
else
even++;
return Math.min(odd,even);
}
}
在一个 XY 坐标系中有一些点,我们用数组 coordinates 来分别记录它们的坐标,其中 coordinates[i] = [x, y] 表示横坐标为 x、纵坐标为 y 的点。
请你来判断,这些点是否在该坐标系中属于同一条直线上,是则返回 true,否则请返回 false。
示例 1:
输入:coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
输出:true
示例 2:
输入:coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]]
输出:false
public boolean checkStraightLine(int[][] coordinates) {
int k1 = coordinates[1][0] - coordinates[0][0];
int k2 = coordinates[1][1] - coordinates[0][1];
for(int i = 0; i < coordinates.length - 1; i++) {
if((coordinates[i + 1][0] - coordinates[i][0]) * k2 != (coordinates[i + 1][1] - coordinates[i][1]) * k1) {
return false;
}
}
return true;
}
给你一个 n 行 m 列的矩阵,最开始的时候,每个单元格中的值都是 0。
另有一个索引数组 indices,indices[i] = [ri, ci] 中的 ri 和 ci 分别表示指定的行和列(从 0 开始编号)。
你需要将每对 [ri, ci] 指定的行和列上的所有单元格的值加 1。
请你在执行完所有 indices 指定的增量操作后,返回矩阵中 「奇数值单元格」 的数目。
示例 1:
输入:n = 2, m = 3, indices = [[0,1],[1,1]]
输出:6
解释:最开始的矩阵是 [[0,0,0],[0,0,0]]。
第一次增量操作后得到 [[1,2,1],[0,1,0]]。
最后的矩阵是 [[1,3,1],[1,3,1]],里面有 6 个奇数。
示例 2:
输入:n = 2, m = 2, indices = [[1,1],[0,0]]
输出:0
解释:最后的矩阵是 [[2,2],[2,2]],里面没有奇数。
直接记一下行数和列数出现的次数即可。
class Solution {
public int oddCells(int n, int m, int[][] indices) {
int[] row=new int[n];
int[] col=new int[m];
for(int i=0;i<indices.length;i++) {
row[indices[i][0]]++;
col[indices[i][1]]++;
}
int ans=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) {
if((row[i]+col[j])%2>0)
ans++;
}
return ans;
}
}
平面上有 n 个点,点的位置用整数坐标表示 points[i] = [xi, yi]。请你计算访问所有这些点需要的最小时间(以秒为单位)。
你可以按照下面的规则在平面上移动:
每一秒沿水平或者竖直方向移动一个单位长度,或者跨过对角线(可以看作在一秒内向水平和竖直方向各移动一个单位长度)。
必须按照数组中出现的顺序来访问这些点。
示例 1:
输入:points = [[1,1],[3,4],[-1,0]]
输出:7
解释:一条最佳的访问路径是: [1,1] -> [2,2] -> [3,3] -> [3,4] -> [2,3] -> [1,2] -> [0,1] -> [-1,0]
从 [1,1] 到 [3,4] 需要 3 秒
从 [3,4] 到 [-1,0] 需要 4 秒
一共需要 7 秒
示例 2:
输入:points = [[3,2],[-2,2]]
输出:5
class Solution {
public int minTimeToVisitAllPoints(int[][] points) {
int min = 0;
for (int i = 1; i < points.length; ++i)
min += findMinStep(points[i - 1][0], points[i - 1][1], points[i][0], points[i][1]);
return min;
}
private static int findMinStep(int x1, int y1, int x2, int y2) {
int minSlope = Math.min(Math.abs(x1 - x2), Math.abs(y1 - y2));
return Math.abs(x1 - x2) + Math.abs(y1 - y2) - minSlope;
}
}
1.先计算移动到同一行或同一列所需最少步数: Math.min(Math.abs(x1 - x2), Math.abs(y1 - y2))
2.然后计算整体步数: 整体步数 = (x方向移动 - 最小步数) + (y方向移动 - 最小步数) + 最小步数, 化简完即为: Math.abs(x1 - x2) + Math.abs(y1 - y2) - minSlope
A 和 B 在一个 3 x 3 的网格上玩井字棋。
井字棋游戏的规则如下:
玩家轮流将棋子放在空方格 (" ") 上。
第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子。
“X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上。
只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
如果所有方块都放满棋子(不为空),游戏也会结束。
游戏结束后,棋子无法再进行任何移动。
给你一个数组 moves,其中每个元素是大小为 2 的另一个数组(元素分别对应网格的行和列),它按照 A 和 B 的行动顺序(先 A 后 B)记录了两人各自的棋子位置。
如果游戏存在获胜者(A 或 B),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。
你可以假设 moves 都 有效(遵循井字棋规则),网格最初是空的,A 将先行动。
示例 1:
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:“A”
解释:“A” 获胜,他总是先走。
"X " "X " "X " "X " "X "
" " -> " " -> " X " -> " X " -> " X "
" " "O " "O " "OO " “OOX”
示例 2:
输入:moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
输出:“B”
解释:“B” 获胜。
"X " "X " "XX " “XXO” “XXO” “XXO”
" " -> " O " -> " O " -> " O " -> "XO " -> "XO "
" " " " " " " " " " "O "
示例 3:
输入:moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
输出:“Draw”
输出:由于没有办法再行动,游戏以平局结束。
“XXO”
“OOX”
“XOX”
示例 4:
输入:moves = [[0,0],[1,1]]
输出:“Pending”
解释:游戏还没有结束。
"X "
" O "
" "
public String tictactoe(int[][] moves) {
int m = moves.length;
// 用数组记录0-2行、0-2列、正对角线、副对角线是否已满3个棋子
// count[0-2]对应0-2行、count[3-5]对应0-2列、count[6]对应正对角线、count[7]对应副对角线
int[] count = new int[8];
// 思路第2步已解释为何只需考虑最后一个落棋的人
// 倒序统计此人走棋情况
for(int i = m - 1; i >= 0; i -= 2) {
// 此棋对行的影响
count[moves[i][0]]++;
// 此棋对列的影响
count[moves[i][1] + 3]++;
// 此棋对正对角线的影响
if(moves[i][0] == moves[i][1])
count[6]++;
// 此棋对副对角线的影响 (
// 此处为3x3的情况,其余大小的棋盘可以类推
if(moves[i][0] + moves[i][1] == 2)
count[7]++;
// 满3个棋子则胜利
if(count[moves[i][0]] == 3 || count[moves[i][1] + 3] == 3 ||
count[6] == 3 || count[7] == 3)
// A先B后 则总长度为偶时 最后为B 反之为A
return m % 2 == 0 ? "B" : "A";
}
// 未胜时,棋盘未下满则继续
if(moves.length < 9)
return "Pending";
// 未胜时,棋盘下满则平局结束
return "Draw";
}
给你一个整数数组 nums,请你返回其中位数为 偶数 的数字的个数。
示例 1:
输入:nums = [12,345,2,6,7896]
输出:2
解释:
12 是 2 位数字(位数为偶数)
345 是 3 位数字(位数为奇数)
2 是 1 位数字(位数为奇数)
6 是 1 位数字 位数为奇数)
7896 是 4 位数字(位数为偶数)
因此只有 12 和 7896 是位数为偶数的数字
示例 2:
输入:nums = [555,901,482,1771]
输出:1
解释:
只有 1771 是位数为偶数的数字。
解题思路:
将int转为String,调用.length,然后%2==0即为偶数
class Solution {
public int findNumbers(int[] nums) {
int res=0;
for(int i:nums){
if(String.valueOf(i).length()%2==0){
res++;
}
}
return res;
}
}
给你一个整数 n,请你返回 任意 一个由 n 个 各不相同 的整数组成的数组,并且这 n 个数相加和为 0 。
示例 1:
输入:n = 5
输出:[-7,-1,1,3,4]
解释:这些数组也是正确的 [-5,-1,1,2,3],[-3,-1,2,-2,4]。
示例 2:
输入:n = 3
输出:[-1,0,1]
示例 3:
输入:n = 1
输出:[0]
方法一:枚举
public int[] sumZero(int n) {
int sum = 0;
int[] arr = new int[n];
int len = 0;
for (int i = 0; i <= n - 2; i++) {
arr[len++] = i;
sum += i;
}
arr[len] = -sum;
return arr;
}
方法二:双指针迭代
public int[] sumZero(int n) {
int[] arr = new int[n];
int l = 0, r = n-1; //左边界与右边界
int i = 1;
while (l < r) {
arr[l++] = i;
arr[r--] = -i;
i++;
}
return arr;
}
给你一个大小为 m * n 的方阵 mat,方阵由若干军人和平民组成,分别用 0 和 1 表示。
请你返回方阵中战斗力最弱的 k 行的索引,按从最弱到最强排序。
如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。
军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。
示例 1:
输入:mat =
[[1,1,0,0,0],
[1,1,1,1,0],
[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,1,1]],
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2
行 1 -> 4
行 2 -> 1
行 3 -> 2
行 4 -> 5
从最弱到最强对这些行排序后得到 [2,0,3,1,4]
示例 2:
输入:mat =
[[1,0,0,0],
[1,1,1,1],
[1,0,0,0],
[1,0,0,0]],
k = 2
输出:[0,2]
解释:
每行中的军人数目:
行 0 -> 1
行 1 -> 4
行 2 -> 1
行 3 -> 1
从最弱到最强对这些行排序后得到 [0,2,3,1]
public int[] kWeakestRows(int[][] mat, int k) {
int[][] tmp = new int[mat.length][2];
for(int i = 0; i < mat.length; i++){
for(int j = 0; j < mat[0].length; j++){
tmp[i][0] = i;
if(mat[i][j] == 1) tmp[i][1] += 1;
}
}
Arrays.sort(tmp, (o1, o2) -> o1[1] - o2[1]);
int[] res = new int[k];
for(int i =0; i < k; i++){
res[i] = tmp[i][0];
}
return res;
}
给你一个整数数组 arr,请你检查是否存在两个整数 N 和 M,满足 N 是 M 的两倍(即,N = 2 * M)。
更正式地,检查是否存在两个下标 i 和 j 满足:
i != j
0 <= i, j < arr.length
arr[i] == 2 * arr[j]
示例 1:
输入:arr = [10,2,5,3]
输出:true
解释:N = 10 是 M = 5 的两倍,即 10 = 2 * 5 。
示例 2:
输入:arr = [7,1,14,11]
输出:true
解释:N = 14 是 M = 7 的两倍,即 14 = 2 * 7 。
示例 3:
输入:arr = [3,1,7,11]
输出:false
解释:在该情况下不存在 N 和 M 满足 N = 2 * M 。
方法:HashMap
前提设定:
用HashMap
遍历开始:
class Solution {
public boolean checkIfExist(int[] arr) {
Map<Integer,Integer> map = new HashMap<>();
for(int tempInt : arr){
//Integer tempRst = map.get(tempInt);
if(map.keySet().contains(tempInt)){
return true;
}else{
map.put(tempInt*2,tempInt);
if(tempInt%2 == 0){
map.put(tempInt/2,tempInt);
}
}
}
return false;
}
}
给你一个 m * n 的矩阵 grid,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。
请你统计并返回 grid 中 负数 的数目。
示例 1:
输入:grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]
输出:8
解释:矩阵中共有 8 个负数。
示例 2:
输入:grid = [[3,2],[1,0]]
输出:0
示例 3:
输入:grid = [[1,-1],[-1,-1]]
输出:3
示例 4:
输入:grid = [[-1]]
输出:1
方法:非递增序列,遇到负数,该 行/列 后面就全是负数
class Solution {
public int countNegatives(int[][] grid) {
int cnt = 0;
for(int i=0;i<grid.length;i++){
//按列非递增
if(grid[i][0]<0){
cnt += (grid.length-i)*grid[0].length;
break;
}
for(int j=0;j<grid[i].length;j++){
//按行非递增
if(grid[i][j]<0){
cnt += grid[i].length-j;
break;
}
}
}
return cnt;
}
}
给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。
换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 nums[j] < nums[i] 。
以数组形式返回答案。
示例 1:
输入:nums = [8,1,2,2,3]
输出:[4,0,1,1,3]
解释:
对于 nums[0]=8 存在四个比它小的数字:(1,2,2 和 3)。
对于 nums[1]=1 不存在比它小的数字。
对于 nums[2]=2 存在一个比它小的数字:(1)。
对于 nums[3]=2 存在一个比它小的数字:(1)。
对于 nums[4]=3 存在三个比它小的数字:(1,2 和 2)。
示例 2:
输入:nums = [6,5,4,8]
输出:[2,1,0,3]
示例 3:
输入:nums = [7,7,7,7]
输出:[0,0,0,0]
解题思路:
temp[]数组的作用是先将nums[]排序,最后temp[]返回结果
排序后每个元素的索引为小于等于这个元素的元素数量,我们需要单独处理相邻元素相等的情况
只需要将相邻相等元素的value设为相等即可
class Solution {
public int[] smallerNumbersThanCurrent(int[] nums) {
int n = nums.length;
int[] temp = new int[n];
temp = Arrays.copyOf(nums, n);
Arrays.sort(temp);
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < n; i++){
if(i == 0){
map.put(temp[i],0);
}else{
if(temp[i] > temp[i-1]){
map.put(temp[i],i);
}else{
map.put(temp[i],map.get(temp[i-1]));
}
}
}
for(int i = 0; i < n; i++){
temp[i] = map.get(nums[i]);
}
return temp;
}
}