剑指offer(41-50)题解

时间:2022-07-23
本文章向大家介绍剑指offer(41-50)题解,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

剑指offer(41-50)题解

  • 41题解--和为S的连续正数序列
  • 42题解--和为S的两个数字
  • 43题解--左旋转字符串
  • 44题解--翻转单词顺序列
  • 45题解--扑克牌顺子
  • 46题解--孩子们的游戏
  • 47题解--求1+2+3+。。。。+n的和
  • 48题解--不用加减乘除做加法
  • 49题解--把字符串转换成整数
  • 50题解--数组中重复的数字

41题解–和为S的连续正数序列

题目描述

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

思路解析

这道题最简单的方法就是枚举,但是这样效率很低,而且很有可能会超时,所以我们必须要做出改进。

既然有了通项公式,那么其实我们也能推出这样一个结论,sum如果在n区间长的连续区间内满足,那么这个n区间长的区间是唯一的,不会存在第二个n长的连续区间满足,从上图我们可以看出。 其次假设刚好区间满足情况,那么区间的元素数是不是只有奇数个和偶数个这两种情况。

这里我们得出结论当区间长度为奇数时,如果sum%区间不为0的话,那么就一定不存在这样一个区间满足 那么到了比较复杂的偶数个的情况了 偶数个的情况时就不能通过奇数的方式来解决了,这时候我们可以通过一开始的通项公式来解决。 既然我们已经确定了当前的区间长度,那么我们就可通过判断该sum是否满足通项公式就能确定是否有这样一个区间满足情况。 下面的代码注释有详细说明

源代码

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Solution {
   public int figure(int sum)
	{
		for(int i=1;i<=sum;i++)
		{
			if((i+1)*i==2*sum)
				return i;
			if((i+1)*i>2*sum)
				return i-1;
		}
		return 0;
	}
	//d表示区间长,n表示是第几个区间,然后计算总和
	public int jisuan(int d,int n)
	{
		return d*(d+1)/2+(n-1)*d;
	}
	//定义排序规则,帮助我们排序
	Comparator<ArrayList<Integer>>comparator=new Comparator<ArrayList<Integer>>() {

		@Override
		public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
			return o1.get(0)-o2.get(0);
		}
	};
	public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
		ArrayList<ArrayList<Integer>>list=new ArrayList<ArrayList<Integer>>();
		int n=figure(sum);
		for(int i=2;i<=n;i++)
		{
			ArrayList<Integer>list2=new ArrayList<Integer>();
			list2.clear();
			//区间长为偶数时的情况
			if(i%2==0)
			{
			    //计算sum与区间长为i的第一区间的差值
				int cha=sum-jisuan(i, 1);
				//如果差值刚好可以将区间长度整除就说明存在这样的连续区间,这里的区间长同时也是上述通项公式中的公差
				if(cha%i==0)
				{
					for(int j=1;j<i+1;j++)
						list2.add(j+(cha/i));
					list.add(list2);
				}
			}
			//区间长为奇数时的情况
			else 
			{
				if(sum%i==0)
				{
					int count=sum/i-i/2;
					for(int j=0;j<i;j++)
						list2.add(count+j);
					list.add(list2);	
				}
			}
		}
		Collections.sort(list, comparator);
		return list;
    }
}

42题解–和为S的两个数字

题目描述

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

思路解析

这题比较简单,只要循环查找就行了,中途记得比较保留最小乘积的两个元素即可。

源代码

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
	        ArrayList<Integer>list1=new ArrayList<Integer>();
	        ArrayList<Integer>list2=new ArrayList<Integer>();
	        int flag=Integer.MAX_VALUE;
	        int num=1;
	        for(int i=0;i<array.length;i++)
	        	list2.add(array[i]);
	        for(int i=0;i<array.length;i++)
	        {
	        	if(list2.contains(sum-array[i]))
	        	{
	        		num=(sum-array[i])*array[i];
	        		if(num<flag)
	        		{
	        			list1.clear();
	        			flag=num;
	        			list1.add(Math.min(sum-array[i], array[i]));
	        			list1.add(Math.max(sum-array[i], array[i]));
	        		}
	        	}
	        }
	        return list1;
	    }
}

43题解–左旋转字符串

题目描述

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

思路解析

这题也比较简单只需要通过String自带的截取函数即可实现,只要注意截取函数是左闭右开的即可。

源代码

public class Solution {
    public String LeftRotateString(String str,int n) {
         String str1="";
		 if(str==null)
			 return str1;
		 if(n>str.length())
			 return str1;
	     String str2=str.substring(0, n);
	     String str3=str.substring(n, str.length());
	     str1+=str3;
	     str1+=str2;
	     return str1;
	 }
}

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||str==null)
            return str;
        else 
		{
			String []str1=str.split(" ");
            StringBuffer str2=new StringBuffer();
            if(str1.length==0)
                return str;
			for(int i=str1.length-1;i>-1;i--)
			{
				str2.append(str1[i]);
				str2.append(" ");
			}
			if(str2.length()>0)
				str2.deleteCharAt(str2.length()-1);
            return str2.toString();
		}
 }
}

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。

思路解析

这题主要考察数学知识即可。 首先我们知道顺子代表的就是5个连续的数,所以这5个数各不相同的。但是又因为有大小王这种万能牌的存在,所以我们在排除0的情况下如果有相同的元素那么就一定不能构成顺子,这里大家可以先判断,不要像博主一样写在一个判断语句中。 找一下规律

接着我们只需要对大小王的个数进行分情况讨论即可。

源代码

import java.util.Arrays;
public class Solution {
  public boolean isContinuous(int [] numbers) {
		Arrays.sort(numbers);
		if(numbers.length==0)
			return false;
		//有四张大小王这种万能牌
		if(numbers[3]==0)
			return true;
		if(numbers[2]==0&&numbers[4]-numbers[3]<=4&&numbers[4]!=numbers[3])
			return true;
		if(numbers[1]==0&&numbers[4]-numbers[2]<=4&&numbers[4]>numbers[3]&&numbers[3]>numbers[2])
			return true;
		if(numbers[0]==0&&numbers[4]-numbers[1]<=4&&numbers[4]>numbers[3]&&numbers[3]>numbers[2]&&numbers[2]>numbers[1])
			return true;
		if(numbers[4]-numbers[0]==4)
			return true;
		return false;
    }
}

46题解–孩子们的游戏

题目描述

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

如果没有小朋友,请返回-1

思路解析

这题的本质其实是契科夫圆,有兴趣的大家可以去了解一下,这里博主是通过单链表实现的,其实很简单就是每一个轮回计数,当达到m时,就刨除相应位置的元素,如果计数的次数已经超过链表的长度,那么我们就需要通过%这个操作重新将计数归为从1开始,一直这样递归,知道链表只剩下一个元素。

源代码

import java.util.ArrayList;
public class Solution {
 public int LastRemaining_Solution(int n, int m) 
	 {
		 if(n==0)
			 return -1;
		 ArrayList<Integer>list=new ArrayList<Integer>();
		 for(int i=0;i<n;i++)
			 list.add(i); 
		 int count=0;
		 while(list.size()>1)
		 {
			 for(int i=0;i<m;i++)
			 {
				 count++;
				 if(count>list.size())
				 {
					 count%=list.size();
				 }
			 }
			 //注意这里因为是下标,所以我们必须要进行--操作,而且必须是--count不能用count--,否则还是会报越界错误
			 
			 list.remove(--count);
		 }
		 return list.get(0);
	 }
}

47题解–求1+2+3+。。。。+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 flag=(sum>0)&&((sum+=Sum_Solution(n-1))>0);
        return sum;
    }
}

48题解–不用加减乘除做加法

题目描述

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

思路解析

这里主要是运用了位求和以及进位的操作 我们先通过10进制来跟大家演示一下 假设我们计算36+19,我们就按照下面的图来皆是:

同样的这样的运算是否也适用于二进制呢,我们同样来试试: 19=10011 35=100011

显然计算下来也是符合我们的规则的,所以我们也可以通过这种方式来计算。 其次我们只要在观察得出位求和与进位操作分别有什么规律?

所以我们就可以通过异或和与操作来帮助我们更快的进行操作。

源代码

public class Solution {
    public int Add(int num1,int num2) {
    int sum,carry;  
    do{  
            sum = num1 ^ num2;  
            carry = (num1&num2) <<1;  
            num1 = sum;  
            num2 = carry;  
    }while(num2!=0);  
    return num1;
    }
}

49题解–把字符串转换成整数

题目描述

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0 输入描述: 输入一个字符串,包括数字字母符号,可以为空 输出描述: 如果是合法的数值表达则返回该数字,否则返回0

思路解析

这里我们先判断当前的字符是不是一个数,这里因为他可能会带上符号,所以我们需要将第一个位置的字符单独拿出来讨论。 谈论完成后只需要将是数字字符的部分整合成数字输出即可。

源代码

import java.util.ArrayList;
import java.util.List;
public class Solution {
  public boolean panduan(String str)
	{
      if(str.equals(""))
			return false;
		char[]ch=str.toCharArray();
		if(!((ch[0]>='0'&&ch[0]<='9')||ch[0]=='+'||ch[0]=='-'))
			return false;
		for(int i=1;i<ch.length;i++)
		{
			if(!(ch[i]>='0'&&ch[i]<='9'))
				return false;
		}
		return true;	
	}
	
	public  int  StrToInt(String str) 
	{
        if(panduan(str))
        {
        	int sum=0;
        	int count=1;
        	if(str.charAt(0)=='+')
        	{
        		String str1=str.substring(1,str.length());
        		char[]ch=str1.toCharArray();
        		List<Integer>list=new ArrayList<Integer>();
        		for(int i=ch.length-1;i>-1;i--)
        			list.add(Character.getNumericValue(ch[i]));
        		for(int i=0;i<list.size();i++)
        		{
        			sum+=list.get(i)*count;
        			count*=10;
        		}
        		return sum;
        		
        	}
        	else if(str.charAt(0)=='-')
        	{
        		String str1=str.substring(1,str.length());
        		char[]ch=str1.toCharArray();
        		List<Integer>list=new ArrayList<Integer>();
        		for(int i=ch.length-1;i>-1;i--)
        			list.add(Character.getNumericValue(ch[i]));
        		for(int i=0;i<list.size();i++)
        		{
        			sum+=list.get(i)*count;
        			count*=10;
        		}
        		return 0-sum;
        	}
        	else 
        	{
        		char[]ch=str.toCharArray();
        		List<Integer>list=new ArrayList<Integer>();
        		for(int i=ch.length-1;i>-1;i--)
        			list.add(Character.getNumericValue(ch[i]));
        		for(int i=0;i<list.size();i++)
        		{
        			sum+=list.get(i)*count;
        			count*=10;
        		}
        		return sum;
        	}
        }
        else 
        	return 0;  	
    }
}

50题解–数组中重复的数字

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

思路解析

这题比较简单循环加入列表中,列表中不存在的就直接添加,如果列表已经存在,那么说明已经是重复的元素,跳出循环输出即可。

源代码

import java.util.ArrayList;
import java.util.List;
public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
	    boolean flag=false;
	    List<Integer>list=new ArrayList<Integer>();
	    if(numbers==null){}
	    else
	    {
	    	list.add(numbers[0]);
		    for(int i=1;i<length;i++)
		    {
		    	if(!list.contains(numbers[i]))
		    		list.add(numbers[i]);
		    	else 
		    	{
						duplication[0]=numbers[i];
						flag=true;
						break;
				}
		    }
		}
	    return flag;
    }
}