剑指offer题解9
38 数组中只出现一次的数字
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
分析:首先,当数组中只有一个数字是单个存在的情况,那么只需要每个数字相互亦或,那么就可以得出答案,因为,两个数亦或,相同的位为0,不同的数位1,也就是两个相同的数亦或等于0,一个数与0亦或所得结果等于它本身。
当出现两个不同的数时,也可以先遍历求亦或,结果就是两个数的亦或结果,由于两个数不同,那么至少有一位(二进制表示中)为1,找到这个位,那么假如把这个数组分成两部分:一部分是这个为1,一部分是这个数为0,也就是这两个数在这两个范围中,在每一部分中其他的数都是偶数个数
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int myxor=0;
for(int a:array){
myxor^=a;
}
int index=0;
while(((1<<index)&myxor)==0){
index++;
}
for(int a:array){
if((a&(1<<index))==0){
num1[0]^=a;
}else{
num2[0]^=a;
}
}
}
}
39 和为S的连续正数序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
分析:
方法一:可以判断出,如果连续的个数是奇数个的话,那么中间值×个数=sum;如果连续的个数是偶数个的话,那么中间两个数的平均数×个数=sum,而且平均数的小数部分是0.5,根据sum=(1+n)*n/2;可以判断出n最大是sqrt(2N)
方法二:滑动窗口
//方法一
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> > res=new ArrayList<>();
for(int count=(int)(Math.sqrt(sum*2));count>=2;count--){
//个数为奇数并且余数为0,偶数并且小数部分为0.5
if(((count&1)==1&&sum%count==0||(sum%count)*2==count)){
ArrayList<Integer> curr=new ArrayList<>();
//j为个数
for(int j=0,k=(sum/count)-(count-1)/2;j<count;j++,k++){
curr.add(k);
}
res.add(curr);
}
}
return res;
}
}
//方法二
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> > res=new ArrayList<>();
int l=1,r=2;
while (l<r){
int currSum=(r+l)*(r-l+1)/2;
if(currSum==sum){
ArrayList<Integer> curr=new ArrayList<>();
for (int i = l; i <= r; i++) {
curr.add(i);
}
res.add(curr);
l++;
//小了右边右移
}else if(currSum<sum){
r++;
//大了左边右移
}else {
l++;
}
}
return res;
}
}
40 和为S的两个数字
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
分析:双指针向中间逼近即可
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
int l=0,r=array.length-1;
ArrayList<Integer> res=new ArrayList<>();
while(l<r){
//所得的第一个数一定满足乘积最小
if(array[l]+array[r]==sum){
res.add(array[l]);
res.add(array[r]);
return res;
}else if(array[l]+array[r]<sum){
l++;
}else{
r--;
}
}
return res;
}
}
41 左旋字符串
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
分析:初始字符串是XY,最后编程YX,YX = (X^T YT)T
2^4
public class Solution {
public String LeftRotateString(String str,int n) {
if(str==null||str.length()==0){
return str;
}
n=n%str.length();
char[] cs=str.toCharArray();
reverse(cs,0,n-1);
reverse(cs,n,str.length()-1);
reverse(cs,0,str.length()-1);
return String.valueOf(cs);
}
private void reverse(char[] cs, int st, int end) {
while (st<end){
char c=cs[st];
cs[st]=cs[end];
cs[end]=c;
st++;
end--;
}
}
}