剑指Offer系列之题41~题45

41.数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

暴力解:利用HashMap存储数字出现次数;

依次异或:先进行依次异或得到两个出现一次数字的异或结果,根据该结果进行分组,然后每一组进行异或得到这两个数字。


1.暴力解:

//num1,num2分别为长度为1的数组。
//将num1[0],num2[0]设置为返回结果
import java.util.Map;
import java.util.HashMap;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        //法1:先排序,然后遍历
        //法2:辅助map存储
        Map<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<array.length;++i){
            if(!map.containsKey(array[i])){
                map.put(array[i],1);
            }else{
                map.put(array[i],2);
            }
        }
        int count=0;
        for(int i=0;i<array.length;++i){
            if(map.get(array[i])==1){
                if(count==0){
                    num1[0]=array[i];
                    count++;
                }else{
                    num2[0]=array[i];
                }
            }
        }
    }
}

2.依次异或:

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.Map;
import java.util.HashMap;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        //方法1:先排序,然后遍历
        //方法2:辅助map存储
        //方法3:依次异或:异或的结果中,相同数字全部抵消。得到的结果是两个出现一次的数字异或的结果。
        //其中至少有一位是1,即两数字这一位不同,然后根据该位是否为1将全部数分为两组,
        //然后每一组的异或结果就是只出现一次的数
        int res=0;
        for(int i=0;i<array.length;++i){
            res^=array[i];
        }
        int n=0;
        while((res & 1) !=1){//未找到1
            res=res>>1;
            n++;//第n位是1
        }
        for(int i=0;i<array.length;++i){
            if((array[i]>>n & 1)!=1){//若该位不是1
                num1[0]^=array[i];
            }else{
                num2[0]^=array[i];
            }
        }
    }
}

42.和为S的连续正数序列 🔺

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输出描述:输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

双指针:左右指针从头开始(1,2),当大于和时左指针右移,小于和时,右指针右移。都向右移是为了查找所有序列


双指针:

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
       //连续的  从小到大  至少两个数
        ArrayList<ArrayList<Integer>> resList=new ArrayList<>();
        int low=1;//左右指针,两者都只能右移
        int high=2;
        while(high>low){
            int cur=(low+high)*(high-low+1)/2;//当前序列的和
            if(cur==sum){
                //若相等则加入列表
                ArrayList<Integer> res=new ArrayList<>();
                for(int i=low;i<=high;++i){
                    res.add(i);
                }
                resList.add(res);
                //添加后移动左指针继续查找下一个序列
                low++;
            }else if(cur>sum){//若大于和,则将左指针右移,减小序列的和
                low++;
            }else if(cur<sum){//若小于和,则将右指针右移,增大序列的和
                high--;
            }
        }
        return resList;
    }
}

43.和为S的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

输入描述:对应每个测试案例,输出两个数,小的先输出。

双指针,分别从前后开始遍历,大于和时右指针左移,小于和时左指针右移,第一次等于和时,即是乘积最小。


1.双指针:

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        //递增排序 可能存在相等的情况
        int product=Integer.MAX_VALUE;//乘积
        int low=0;
        int high=array.length-1;
        ArrayList<Integer> res=new ArrayList<>();
        if(array.length<=1)
            return res;
        while(low<high){//从头和尾开始遍历,当大于和时,右指针左移,小于和时,左指针右移
            int cur=array[low]+array[high];
            if(cur==sum){
                if((array[low]*array[high])<product){
                    if(!res.isEmpty()){//若列表非空则先清空列表
                        res.remove(1);
                        res.remove(0);
                    }
                    res.add(array[low]);
                    res.add(array[high]);
                    product=array[low]*array[high];
                }
                low++;
            }else if(cur<sum){
                low++;
            }else{
                high--;
            }
        }
        return res;
    }
}

2.简化版:

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        //递增排序 可能存在相等的情况
        int low=0;
        int high=array.length-1;
        ArrayList<Integer> res=new ArrayList<>();
        if(array.length<=1)
            return res;
        while(low<high){//从头和尾开始遍历,当大于和时,右指针左移,小于和时,左指针右移
            int cur=array[low]+array[high];
            if(cur==sum){//第一对相等的就是乘积最小的
                res.add(array[low]);
                res.add(array[high]);
                break;
            }else if(cur<sum){
                low++;
            }else{
                high--;
            }
        }
        return res;
    }
}

44.左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

暴力解:利用String的substring方法;

利用翻转特性:先翻转前n个,再翻转剩余部分,然后整体翻转,可以达到效果。


1.暴力解:

public class Solution {
    public String LeftRotateString(String str,int n) {
        //考虑str、n的异常输入
        if(n>=str.length())//移位超过字符串长度则相当于移减去字符串长度的距离
            n-=str.length();
        if(n<=0 || str.equals(""))
            return str;
        char  c[]=new char[n];
        for(int i=0;i<n;++i){
            c[i]=str.charAt(i);
        }
        String temp="";
        for(int i=0;i<n;++i){
            temp+=c[i];
        }
        str=str.substring(n,str.length());//取要前移的子字符串

        return str+temp;
    }
}

2.简化:

public class Solution {
    public String LeftRotateString(String str,int n) {
        //考虑str、n的异常输入
        if(n<=0 || str.equals(""))
            return str;
        if(n>=str.length())//移位超过字符串长度则相当于移减去字符串长度的距离
            n%=str.length();
        int len=str.length();
        str+=str;

        return str.substring(n,len+n);
    }
}

3.利用翻转特性:

public class Solution {
    public String LeftRotateString(String str,int n) {
        //考虑str、n的异常输入
        if(n<=0 || str.equals(""))
            return str;
        if(n>=str.length())//移位超过字符串长度则相当于移减去字符串长度的距离
            n%=str.length();
        char c[]=str.toCharArray();
        reverse(c,0,n-1);
        reverse(c,n,c.length-1);
        reverse(c,0,c.length-1);

        return new String(c);
    }

    public void reverse(char []c,int start,int end){
        while(start<end){
            char temp=c[start];
            c[start]=c[end];
            c[end]=temp;
            start++;
            end--;
        }
    }
}

45.翻转单词顺序

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

从头和尾依次交换;

或者利用StringBuilder/Buffer从尾到头添加字符串;

先翻转整个字符串,然后以空格为分割翻转单词


1.依次交换:

public class Solution {
    public String ReverseSentence(String str) {
        if(str.trim().equals(""))
            return str;
        String s[]=str.split(" ");
        int start=0;
        int end=s.length-1;
        while(start<end){
            String temp=s[start];
            s[start]=s[end];
            s[end]=temp;
            start++;
            end--;
        }

        String res="";
        for(int i=0;i<s.length;++i){//拼接
            res+=s[i];
            if(i!=s.length-1)
                res+=" ";
        }
        return res;
    }
}

2.整体翻转后以空格为分割翻转每个单词:

public class Solution {
    public String ReverseSentence(String str) {
        if(str.trim().equals(""))
            return str;
        char c[]=str.toCharArray();
        //先翻转整个字符串,然后以空格为界,单独翻转每个单词
        reverse(c,0,c.length-1);
        int firBlank=-1;
        for(int i =0;i<c.length;++i){
            if(c[i]==' '){//找到空格
                int nextBlank=i;
                reverse(c,firBlank+1,nextBlank-1);//翻转空格前的单词
                firBlank=nextBlank;
            }
        }
        reverse(c,firBlank+1,c.length-1);//翻转最后一个单词

        return new String(c);
    }
    public void reverse(char []c,int start,int end){
        while(start<end){
            char temp=c[start];
            c[start]=c[end];
            c[end]=temp;
            start++;
            end--;
        }
    }
}

如有错误,欢迎指正

posted @ 2020-04-15 11:44  雨落成尘  阅读(172)  评论(0编辑  收藏  举报