题解(01-10):link
题解(11-20):link
题解(21-30):link
题解(31-40):link
题解(41-50):link
题解(51-60): link
题解(61-67): link
描述见链接
x
,后面有n
个连续正数,且满足条件,则可以得到式子x + (x + 1) + (x + 2) +...+(x + n) = sum
,化简一下就是(n + 1)(n + 2 * x) = 2 * sum
,且由于至少包括两个数,那么我们可以得到x <= sum / 2
(例子:9 = 4 + 5
) ,即第一个开始的元素应该小于等于目标元素的一半,否则后面就没办法再增加比x
大的数了,由此可以减少运算次数。sum <= 3
包含特殊情况,提前处理public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (sum <= 2) return res;
if (sum == 3) {
ArrayList<Integer> tmp = new ArrayList<>();
tmp.add(1);
tmp.add(2);
res.add(tmp);
return res;
}
for (int x = 1;x <= sum / 2;x ++) {
for (int n = 1;n < sum / 2;n ++) {
// 如果满足条件,则加入结果集中
if (isAns(sum, x, n)) {
ArrayList<Integer> tmp = new ArrayList<>();
for (int i = 0;i <= n;i ++)
tmp.add(x + i);
res.add(tmp);
}
}
}
return res;
}
// 根据判断公式判断x,n 是否能够满足条件
private boolean isAns(int sum, int x, int n) {
int tmp = (n + 1) * (n + 2 * x);
return tmp == sum * 2;
}
}
i
,一个指向j = i + 1
开始j <= (sum + 1) / 2
满足条件。为什么?因为 99 = 44 + 45,保证i = 44,j = (99 + 1) / 2 = 45
i -> j
的临时和tmpSum
可以使用高斯公式计算:(首项 + 尾项) * 项数 / 2
tmpSum < sum
,那么不够大,于是增加 j
tmpSum > sum
,那么不够小,于是增加i
tmpSum = sum
,说明满足条件,那么把i -> j
的数存入结果集,且将j++
进入下一个可能的结果集的判断public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (sum <= 2) return res;
int i = 1, j = 2;
while (j <= (sum + 1) / 2) {
int tmpSum = (i + j) * (j - i + 1) / 2;
if (tmpSum < sum) j ++;
else if (tmpSum > sum) i ++;
else {
ArrayList<Integer> tmp = new ArrayList<>();
for (int k = i;k <= j;k ++) {
tmp.add(k);
}
res.add(tmp);
j ++;
}
}
return res;
}
}
描述见链接
关键在于证明相隔越远的两个数乘积还最小!
证明如下:
i < j < k < m
,且有 i + m = j + k
;i < i + n1 < i + n1 + n2 < i + n1 + n2 + n3
,其中n
为正数n1 + n2 + n1 = n1 + n2 + n3 -> 2*n1 + n2 = n1 + n2 + n3
i * m < j * k
证明:由题意可得
(i * m) - (j * k)
= {i * (i + n1 + n2 + n3)} - {(i + n1) * (i + n1 + n2)} // 中间自己化简一下,注意上面的条件2
= -(n1 + n2) < 0
所以:i * m < j * k
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
// 双指针,距离隔得最远的两个元素的乘积最小,所以一找到一对满足的数就可以返回了
// 需要考虑是否有重复元素,这儿是递增,就没考虑相同元素
ArrayList<Integer> res = new ArrayList<>();
if (array == null || array.length <= 1) return res;
int i = 0, j = array.length - 1;
while (i < j) {
if (array[i] + array[j] == sum) {
res.add(array[i]);
res.add(array[j]);
return res;
}
while (i < j && array[i] + array[j] < sum) i ++;
while (i < j && array[i] + array[j] > sum) j --;
}
return res;
}
}
描述见链接
substring(int start, int end)
取得 [start, end)
的字符串public class Solution {
public String LeftRotateString(String str,int n) {
int N = str.length();
if (n <= 0 || str == null || N <= 1) return str;
n = n % N;
// 取得左边的n位加到右边的末尾
String s1 = str.substring(0, n);
String s2 = str.substring(n, N);
return s2 + s1;
}
}
public class Solution {
public String LeftRotateString(String str,int n) {
int N = str.length();
if (n <= 0 || str == null || N <= 1) return str;
n = n % N;
// 1 2 3 4 5 6 7 循环左移3位应该是 4 5 6 7 1 2 3
// 相当于先把整个反转 7 6 5 4 3 2 1
// 再反转前N - n位 4 5 6 7 3 2 1 反转0 -> (N - n - 1)
// 再反转后n位 4 5 6 7 1 2 3 反转(N - n) -> (N - 1)
StringBuilder sb = new StringBuilder(str);
reverse(sb, 0, N - 1);
reverse(sb, 0, N - n - 1);
reverse(sb, N - n, N - 1);
return sb.toString();
}
private void reverse(StringBuilder sb, int left, int right) {
while (left < right) {
char tmp = sb.charAt(left);
sb.setCharAt(left, sb.charAt(right));
sb.setCharAt(right, tmp);
left ++;
right --;
}
}
}
描述见链接
import java.lang.StringBuilder;
public class Solution {
public String ReverseSentence(String str) {
// 去除字符串前后的空格,要是最后为空,说明整个字符串是一个空字符串
if (str == null || str.trim().equals("")) return str;
String[] arr = str.split(" ");
// 先交换位置,理解起来的话使用StringBuilder也可以从后往前遍历,不用交换
for (int i = 0;i < arr.length / 2;i ++) {
String tmp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = tmp;
}
StringBuilder sb = new StringBuilder();
for (int i = 0;i < arr.length;i ++) {
sb.append(arr[i]);
if (i != arr.length - 1)
sb.append(" ");
}
return sb.toString();
}
}
import java.lang.StringBuilder;
public class Solution {
public String ReverseSentence(String str) {
if (str == null || str.trim().equals("")) return str;
String[] arr = str.split(" ");
StringBuilder sb = new StringBuilder();
// 从后往前遍历,避免交换位置
for (int i = arr.length - 1;i >= 0;i --) {
sb.append(arr[i]);
if (i != 0)
sb.append(" ");
}
return sb.toString();
}
}
描述见链接
public class Solution {
public boolean isContinuous(int [] numbers) {
// 一副牌中最多四个0,即四个癞子牌,默认输入的数组长度应该就是5个
if (numbers == null || numbers.length == 0) return false;
Arrays.sort(numbers);
// countZero 记录0的个数,countGap 记录可以插入0的位置
int countZero = 0, countGap = 0;
for (int i = 0;i < numbers.length - 1;i ++) {
if (countZero == 4) return true;
if (numbers[i] == 0) countZero ++;
// 如果出现重复不为0的数字,说明不可能排成一个5连顺子
else if (numbers[i] == numbers[i + 1]) return false;
else {
// 记录两个数字之间可以插入0的地方
countGap += (numbers[i + 1] - numbers[i] - 1);
}
}
// 只有当 countGap <= countZero ,才能够完全插满中间的空位置,形成顺子
if (countGap <= countZero) return true;
return false;
}
}
描述见链接
removeIndex
m
个小孩子就删掉一个,所以更新removeIndex
的公式,还要考虑越界问题,则removeIndex=(removeIndex + m - 1) % list.size()
。删掉一个小孩子后,整个圈就少了一个人,为了避免越界,就要模上list.size()
。import java.util.LinkedList;
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <= 0 || m <= 0) return -1;
LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < n; i ++)
list.add(i);
int removeIndex = 0;
while (list.size() != 1) {
// 关键在于对这个removeIndex的更新,好好体会一下
removeIndex = (removeIndex + m - 1) % list.size();
list.remove(removeIndex);
}
return list.get(0);
}
}
约瑟夫环公式 : f(n, m) = (f(n - 1, m) + m) % n
n
:一共有多少人
m
:报数到m
就退出
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <= 0 || m <= 0) return -1;
int last = 0; // 一个人时的答案
// 自底向上的递推,先计算出2个人的答案,再往上一直计算n个人的答案
// i = 1即只有一个人时,就是这个人就是赢家,返回下标为0
for (int i = 2;i <= n;i ++) {
last = (last + m) % i;
}
return last;
}
}
f(n, m) = (f(n - 1, m) + m) % n
记住这个转化公式!!!
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <= 0 || m <= 0)
return -1;
else if (n == 1) // 如果 n = 1,说明只有一个人,返回这个人的下标 0
return 0;
else // 公式 f(n, m) = ( f(n - 1, m) + m ) % n
return (LastRemaining_Solution(n - 1, m) + m) % n;
}
}
题目描述: 求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
public class Solution {
public int Sum_Solution(int n) {
int sum = n;
// 前后都应该是返回boolean值
// sum += Sum_Solution(--n) 每次先将--n后再计算,最后n=-1的时候,由前面短路与操作,不会执行叠加 // 操作。所以不会把-1加进去
boolean flag = (sum > 0) && ( (sum += Sum_Solution(--n)) > 0 );
return sum;
}
}
题目描述: 写一个函数,求两个整数之和,要求在函数体内不得使用
+、-、*、/
四则运算符号。
0+0=0,1+1=0,1+0=1,0+1=1
,也就是做异或 运算1 + 1
会产生进位,因此求每一位的进位可以先将两个数做与运算 ,然后再左移一位。public class Solution {
public int Add(int num1,int num2) {
int sum = 0, carry = 1;
while (carry != 0) {
// 无进位相加
sum = num1 ^ num2;
// 对产生的进位进行计算,与运算得到的结果为两个数都有1的位置
carry = (num1 & num2) << 1;
// 迭代运算
num1 = sum;
num2 = carry;
}
return sum;
}
}
public class Solution {
public int Add(int num1,int num2) {
if (num2 == 0)
return num1;
int sum = num1 ^ num2; // 不考虑进位加
int carry = (num1 & num2) << 1; // 考虑进位
return Add(sum, carry);
}
}
// 基于加减法
a = a + b;
b = a - b;
a = a - b;
// 基于位运算
a = a ^ b;
b = a ^ b;
a = a ^ b;
描述见链接
需要注意的问题包括:空指针、空字符串、正负数、整型溢出
注意点:
public class Solution {
public int StrToInt(String str) {
if (str == null || str.length() == 0) return 0;
// boolean isValid = false; // 记录这个数是否合法,好像没啥用
int flag = 0;// 如果是正数为1,如果为负数为-1
char c = str.charAt(0);
if (c == '+') flag = 1; // 正数
else if (c == '-') flag = -1; // 负数
else if (c >= '0' && c <= '9') { // 正数
flag = 1;
str = "+" + str; // 便于统一处理
} else return 0; // 不是数
int len = str.length();
long res = 0; // 记录结果,因为整型可能溢出,所以就使用长整型了
if (len > 11) return 0; // int 数的最大值小于10^10,加上一个符号位最多11位
for (int i = 1;i < len;i ++) {
char digit = str.charAt(i);
if (digit < '0' || digit > '9') return 0;
// 下面这个公式也比较重要
res = res * 10 + (digit - '0');
}
// 在整型范围内是合法的
if (flag == 1 && res <= Integer.MAX_VALUE)
return (int)res;
if (flag == -1 && - res >= Integer.MIN_VALUE)
return (int)(-res);
return 0;
}
}
题目描述: 在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为 7 的数组 {2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字 2。
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if (numbers == null || length < 2) return false;
HashSet<Integer> set = new HashSet<>(); // 使用HashSet存储,遇到重复的就赋值返回了
for (int i = 0;i < length;i ++) {
if (set.contains(numbers[i])) {
duplication[0] = numbers[i];
return true;
} else {
set.add(numbers[i]);
}
}
return false;
}
}
根据题意:
所有的数字都在0
到n-1
的范围内,因此如果没有重复,那么所存储的值也正好是0
到n-1
这n
个数字,我们把原数组重新排列为一个元素和对应下标值相同的数组。即对于一个没有重复元素的数组,有
index : 0 1 2 3 4 5 6 7
eleme : 0 1 2 3 4 5 6 7
具体思路:
i
的数字num[i]
时,首先比较num[i] == i
成立与否,如果成立,则接着比较下一个元素;num[i] == num[num[i]]
成立与否,如果成立,说明是一个重复元素,如果不成立,说明num[i]
应该放到num[num[i]]
这个他的最终归属地去,因此交换这两个元素,重复过程,直到找到结果;O(n)
的示例 :arr = {2,3,1,0,2,5,3}
arr[0] != 0
arr[0] != arr[arr[0]]
,所以交换一下得到{1,3,2,0,2,5,3}
arr[0] != arr[arr[0]]
,所以交换一下得到{3,1,2,0,2,5,3}
arr[0] != arr[arr[0]]
,所以交换一下得到{0,1,2,3,2,5,3}
arr[0] == 0
,退出进行下一次循环,以此类推2
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if (numbers == null || length < 2) return false;
for (int i = 0;i < length;i ++) {
while (numbers[i] != i) {
if (numbers[numbers[i]] == numbers[i]) {
duplication[0] = numbers[i];
return true;
} else {
int tmp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = tmp;
}
}
}
return false;
}
}