Day5.牛客网剑指offer 67题之43-54题(java代码)

Day5.牛客网剑指offer 67题之43-54题

仍然是早起的一天准备专业基础知识面试会场安排,不过今天工作量比较小,分配的软件工程课程面试只有两个人,听他们回答问题仿佛看到了两年那个不知所措的自己…后面两天就很忙了,争取明天把最后的题目刷完吧,今天的题有点像之前题目的进化版,所以还是要多想多写,自己之前看到题目就懵,主要还是很多结构用不熟练。

43.左旋转字符串

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

  1. abc→cba 得到cbaXYZdef
  2. XYZdef→fedZYX 得到cbafedZYX
  3. cbafedZYX→XYZdefabc 得到结果
public class Solution {
    public String LeftRotateString(String str,int n) {
        if(str.length()==0) return "";
        char[] cStr=str.toCharArray();
        reverse(cStr,0,n-1);
        reverse(cStr,n,str.length()-1);
        reverse(cStr,0,str.length()-1);
        
        return new String(cStr);
    }
    public void reverse(char []str,int start,int end){
        while(start<end){
            char temp=str[start];
            str[start]=str[end];
            str[end]=temp;
            start++;
            end--;
        }
    }
}

44.翻转单词顺序列

题目描述
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
思路
这一题跟上一题类似的思路,先把每个单词翻转,然后再翻转整个序列,这里就需要找好每一个单词的开头和结尾。

public class Solution {
    public String ReverseSentence(String str) {
        if(str.length()==0) return "";
        char[] cStr=str.toCharArray();
        int startIndex=0,endIndex=0;
        while(endIndex<=str.length()){//这里注意判定走到最后的条件,之前使用了endIndex<用例通不过
            if(endIndex==str.length()||cStr[endIndex]==' '){
                reverse(cStr,startIndex,endIndex-1);
                startIndex=endIndex+1;
            }
            endIndex++;
        }
        reverse(cStr,0,str.length()-1);
        return new String(cStr);
    }
    public void reverse(char[] str,int start,int end){
        while(start<end){
            char temp=str[start];
            str[start]=str[end];
            str[end]=temp;
            start++;
            end--;
        }
    }
}

45.扑克牌顺序

题目描述
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
思路
题目很长找关键信息,其实就是给定的一个数组里五张牌,能不能连成顺子。大小王(0)可以代替任何牌,如果有两张牌一样肯定不是癞子,两张牌之间的差值-1一定要在大王小王数量范围之内。

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers==null||numbers.length<5) return false;
        Arrays.sort(numbers);
        int wNum=0;//王牌
        for(int i=0;i<numbers.length;i++){
            if(numbers[i]==0)
                wNum++;
        }
        for(int i=wNum;i<numbers.length-1;i++){
            if(numbers[i]==numbers[i+1]&&numbers[i]!=0) return false;
            int temp=numbers[i+1]-1-numbers[i];
            if(temp>wNum) return false;
            wNum-=temp;
        }
        return true;
    }
}

46.孩子们的游戏

题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
思路
约瑟夫环的思路,当然可以直接用数学方法解决,自己可以动手走一遍流程,其实我看了别人的思路我也讲不清楚。

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n==0||m==0) return -1;
        int ans=0;
        for(int i=2;i<=n;i++){
            ans=(ans+m)%i;
        }
        return ans;
    }
}

47.求1+2+…+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;
        //n==0||(sum+=Sum_Solution(n-1))>0;
        boolean flag=(n>0)&&(sum+=Sum_Solution(n-1))>0;
        return sum;
    }
}

48.不用加减乘除做加法

题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路
位运算。

public class Solution {
    public int Add(int num1,int num2) {
		while(num2!=0){
            int temp=num1^num2;//求和
            num2=(num1&num2)<<1;//进位
            num1=temp;
        }
        return num1;

}

49.把字符串转化为整数

题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
思路
首先先判断第一个字符+,-号问题,然后后面的数正常转化(这里需要考虑溢出的问题)。
卡了很久啊,溢出值如何AC出来。。。-2147483649就输出不了,leetcode那里好像也有挺多思路的,不过说实话还是挺无语的。。
不考虑溢出(只通过85%)

	public int StrToInt(String str) {
	    if(str.length() == 0) return 0;
	    char[] chars = str.toCharArray();
	    boolean isNegative = chars[0] == '-';//判断是否有负号
	    int result = 0;
	    for(int i = 0; i < chars.length; i ++) {
	        if(i == 0 && (chars[i] == '+' || chars[i] == '-')) continue;//跳过正负号
	        if(chars[i] < '0' || chars[i] > '9') return 0;
	        result = result * 10 + (chars[i] - '0');
	    }
	    return isNegative ? -result : result;//三元
    }

考虑溢出
反正俩个都通过不了,测试用例已经超出long范围了…搜了一下好像是说python可以处理,会写python的就用python去AC吧。

public int StrToInt(String str) {
        if (str == null)
            return 0;
        int result = 0;
        boolean negative = false;//是否负数
        int i = 0, len = str.length();
        /**
         * limit 默认初始化为 负的 最大正整数 ,假如字符串表示的是正数
         * 那么result(在返回之前一直是负数形式)就必须和这个最大正数的负数来比较,
         * 判断是否溢出
         */
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;
 
        if (len > 0) {
            char firstChar = str.charAt(0);//首先看第一位
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;//在负号的情况下,判断溢出的值就变成了 整数的 最小负数了
                } else if (firstChar != '+')//第一位不是数字和-只能是+
                    return 0;
                if (len == 1) // Cannot have lone "+" or "-"
                    return 0;
                i++;
            }
            multmin = limit / 10;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = str.charAt(i++)-'0';//char转int
                if (digit < 0 || digit > 9)//0到9以外的数字
                    return 0;
 
                //判断溢出
                if (result < multmin) {
                    return 0;
                }
                result *= 10;
                if (result < limit + digit) {
                    return 0;
                }
                result -= digit;
            }
        } else {
            return 0;
        }
        //如果是正数就返回-result(result一直是负数)
        return negative ? result : -result;
    }

50.数组中重复的数字

题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路
其实看题目描述的话我觉得用HashSet完全是可以的,不过从代码结构来看的话,这个应该是判断是否有重复值吧,估计就是用计数排序的思想了,让每一个值与数组下标对应,不对应就交换他该到的位置。
也可以直接排序之后找重复,因为在代码里它说是可以返回任意一个重复值。
计数交换排序

public boolean duplicate(int numbers[],int length,int [] duplication) {
        int index = 0;
    	
    	while(index < length) {
    		if(numbers[index] == index) {
    			index ++;
    		} else {
    			int num = numbers[index];
    			if(numbers[num] == num) {//存在重复
    				duplication[0] = num;
    				return true;
    			} else {//不重复则将num交换到numbers[num]上
    				int tmp = numbers[num];
    				numbers[num] = num;
    				numbers[index] = tmp;
    			}
    		}
    	}
    	
    	return false;

    }

排序后比较

public boolean duplicate(int[] numbers, int length, int[] duplication) {//排序,找重复
    	if(numbers == null || numbers.length == 0) {
    		return false;
    	}
    	
    	Arrays.sort(numbers);
    	
    	for(int i = 0; i < length - 1; i ++) {
    		if(numbers[i + 1] == numbers[i]) {
				duplication[0] = numbers[i];
    			return true;
    		}
    	}
    	
    	return false;
    }

51.构建乘积数组

题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
思路
仔细看一下就会发现每一项B[i]都没有对应的A[i]相乘,所以可以把所有对应的A[i]看成是1,此时会把数组B分成上下两部分三角区域,再分别进行连乘即可,图可以自己画一下,其中上部分没有B[n-1]计算,下部分没有B[0]计算。

public int[] multiply(int[] A) {
        int length = A.length;
        int[] B = new int[length];
        if(length != 0 ){
            B[0] = 1;
            //计算下三角连乘
            for(int i = 1; i < length; i++){
                B[i] = B[i-1] * A[i-1];
            }
            int temp = 1;
            //计算上三角
            for(int j = length-2; j >= 0; j--){
                temp *= A[j+1];
                B[j] *= temp;
            }
        }
        return B;
    }

52.正则表达式匹配

题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
思路
正则表达式用到比较少,理解的也不是特别透彻,这里也是学习了别人的思路,主要还是细心,匹配好各种条件。

public boolean match(char[] str, char[] pattern) {
    	if(str == null || pattern == null) return false;
        if(str.length == 0 && pattern.length == 0) return true;//都空,匹配成功
        if(str.length != 0 && pattern.length == 0) return false;//第一个非空,第二个空,匹配失败(相反是有可能成功的,例如"a*a*"可匹配成"")
        if(str.length == 0){
            if((pattern.length == 2 && pattern[1] == '*')){//特殊情况
                return true;
            }else {
                return false;
            }
        }
        
        return matchCore(str, 0, pattern, 0);
    }
    
    private boolean matchCore(char[] str, int sIndex, char[] pattern, int pIndex) {
        if(sIndex == str.length && pIndex == pattern.length) return true;
        
        if(sIndex != str.length && pIndex == pattern.length) return false;
        
        if((pIndex + 1) < pattern.length && pattern[pIndex + 1] == '*') {
            if((sIndex < str.length && str[sIndex] == pattern[pIndex]) 
            		|| (pattern[pIndex] == '.' && sIndex < str.length)) {
            	
                return matchCore(str, sIndex + 1, pattern, pIndex + 2) 
                		|| matchCore(str, sIndex + 1, pattern, pIndex)
                		|| matchCore(str, sIndex, pattern, pIndex + 2);
            }else{
                return matchCore(str, sIndex, pattern, pIndex + 2);
            }
        }
        if((sIndex < str.length && str[sIndex] == pattern[pIndex])
        		|| (pattern[pIndex] == '.' && sIndex < str.length)) {
        	
            return matchCore(str, sIndex + 1, pattern, pIndex + 1);
        }else{
            return false;
        }
    }

53.表示数值的字符串

题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
思路
跟上一题类似,也是需要考虑匹配的所有情况,细心一点就好,尤其要注意符号跟小数点的匹配问题。

public boolean isNumeric(char[] str) {
        //分别需要判断e指数、符号和小数点是否符合要求
        boolean sign=false,decimal=false,e=false;
        for(int i=0;i<str.length;i++){
            if(str[i]=='e'||str[i]=='E'){
                if(e||i==str.length-1) return false;//e后面不能为空
                if(str[i-1]>'9'||str[i-1]<'0') return false;//e的前面必须为数字
                e=true;
            }else if(str[i]=='+'||str[i]=='-'){
                //符号要么在开头,要么前面必须跟e
                if(sign && str[i-1]!='e' && str[i-1]!='E') return false;
                if(!sign && i>0 && str[i-1]!='e' && str[i-1]!='E') return false;
                sign=true;
            }else if(str[i]=='.'){
                //小数点不能大于1个且不能在e后面且不能在末尾
                if(decimal||e||i==str.length-1) return false;
                decimal=true;
            }else if(str[i]>'9'||str[i]<'0')
                return false;
        }
        return true;
    }

也可以直接正则表达式匹配

public boolean isNumeric(char[] str) {
        return new String(str).matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
    }

54.字符流中第一个不重复的字符

题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

输出描述: 如果当前字符流没有存在出现一次的字符,返回#字符。

思路
这一题思路跟34题求第一个只出现一次的字符类似,只不过这里因为是需要实现字符流的输出,所以新加一个ArrayList进行元素存储。

import java.util.HashMap;
import java.util.ArrayList;
public class Solution {
    //Insert one char from stringstream
    HashMap<Character,Integer> map=new HashMap<>();
    ArrayList<Character> list=new ArrayList<Character>();
    public void Insert(char ch)
    {
        if(map.containsKey(ch)){
            map.put(ch,map.get(ch)+1);
        }else{
            map.put(ch,1);
        }
         
        list.add(ch);
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for(char key:list){
            if(map.get(key)==1)
                return key;
        }
        return '#';
    }
}

下一篇:牛客网剑指offer 67题之55-67题
上一篇:牛客网剑指offer 67题之33-42题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值