剑指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--;
        }
    }
}  

猜你喜欢

转载自blog.csdn.net/gentlezuo/article/details/91039987