《剑指offer》– 把数组排成最小的数、丑数、二进制中1的个数、表示数值的字符串、替换空格

《剑指offer》– 把数组排成最小的数、丑数、二进制中1的个数、表示数值的字符串、替换空格

一、把数组排成最小的数:

1、题目:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

2、解题思路:

重写Comparator的compare()方法。

3、代码实现:

public class Test23 {

	public static void main(String[] args) {
		int[] array = new int[]{6,2,4,41,5};
		PrintMinNumber(array);
	}
	
	//解题思路:
	//通过比较器重写compare方法
	public static String PrintMinNumber(int [] numbers) {
		
		ArrayList<Integer> list = new ArrayList<>();
		
		for(int i=0;i<numbers.length;i++){
			list.add(numbers[i]);
		}
		
		Collections.sort(list,new Comparator<Integer>(){
			@Override
			public int compare(Integer o1, Integer o2) {
				String str1=o1+""+o2;
				String str2=o2+""+o1;
				
				return str1.compareTo(str2);
			}
		});
		
		StringBuilder builder = new StringBuilder();
		for(Integer num:list){
			builder.append(num);
		}
		
		System.out.println(builder.toString());
		return builder.toString();
	}
}

 

 

二、丑数:

1、题目:

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

2、解题思路:

参考牛客网的“事无巨细,悉究本末”:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b

2.1 通俗易懂的解释:

首先从丑数的定义我们知道,一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z,换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到,那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到4,6,10,6,9,15,10,15,25九个丑数,我们发现这种方法会得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。那么我们可以维护三个队列:

(1)丑数数组: 1

乘以2的队列:2

乘以3的队列:3

乘以5的队列:5

选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(2)丑数数组:1,2

乘以2的队列:4

乘以3的队列:3,6

乘以5的队列:5,10

选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(3)丑数数组:1,2,3

乘以2的队列:4,6

乘以3的队列:6,9

乘以5的队列:5,10,15

选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(4)丑数数组:1,2,3,4

乘以2的队列:6,8

乘以3的队列:6,9,12

乘以5的队列:5,10,15,20

选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(5)丑数数组:1,2,3,4,5

乘以2的队列:6,8,10,

乘以3的队列:6,9,12,15

乘以5的队列:10,15,20,25

选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;

……………………

2.2 疑问:

2.2.1 为什么分三个队列?

丑数数组里的数一定是有序的,因为我们是从丑数数组里的数乘以2,3,5选出的最小数,一定比以前未乘以2,3,5大,同时对于三个队列内部,按先后顺序乘以2,3,5分别放入,所以同一个队列内部也是有序的;

2.2.2 为什么比较三个队列头部最小的数放入丑数数组?

因为三个队列是有序的,所以取出三个头中最小的,等同于找到了三个队列所有数中最小的。

2.3 实现思路:

我们没有必要维护三个队列,只需要记录三个指针显示到达哪一步;“|”表示指针,arr表示丑数数组;

(1)1

|2

|3

|5

目前指针指向0,0,0,队列头arr[0] * 2 = 2,  arr[0] * 3 = 3,  arr[0] * 5 = 5

(2)1 2

2 |4

|3 6

|5 10

目前指针指向1,0,0,队列头arr[1] * 2 = 4,  arr[0] * 3 = 3, arr[0] * 5 = 5

(3)1 2 3

2| 4 6

3 |6 9

|5 10 15

目前指针指向1,1,0,队列头arr[1] * 2 = 4,  arr[1] * 3 = 6, arr[0] * 5 = 5

………………

3、代码实现:

public class Test24 {
	
	//解题思路:创建三个队列 https://www.nowcoder.com/ta/coding-interviews
	public int GetUglyNumber_Solution(int index) {
		
		if(index<7)
			return index;
		
		//创建三个队列的头指针,newNum代表的是选出的最小数
		int t2=0,t3=0,t5=0;
		int newNum=1;
		
		//创建一个数组,存放丑数
		ArrayList<Integer> list = new ArrayList<>();
		list.add(newNum);
		
		for(int i=1;i<index;i++){
			//选出三个队列里面最小的数 
			newNum=Math.min(list.get(t2)*2,Math.min(list.get(t3)*3,list.get(t5)*5));
			//这三个if有可能进入一个或者多个,进入多个是三个队列头最小的数有多个的情况
			if(newNum == list.get(t2)*2) t2++; 
			if(newNum == list.get(t3)*3) t3++; 
			if(newNum == list.get(t5)*5) t5++; 
			
			list.add(newNum);
		}
        return newNum;
    }
}

 

 

三、二进制中1的个数:

1、题目:

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

2、思路:

第一种:使用java内置的的toBinaryString方法来实现;

第二种:利用位运算符来实现:

(1)将n与n-1做与运算,会把最右边的1去掉。比如: 1100 & 1011 = 1000 ,即 12 & 11 = 8,再利用计算器count++计算出有多少个 1 即可。

(2)详细说明:如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来他的二进制处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
(3)举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011。我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

3、代码实现:

//利用java内置的toBinaryString方法来实现:
	public static int NumberOf1(int n){
		
		int count = 0;
		String str=Integer.toBinaryString(n);
		for(int i=0;i<str.length();i++){
			if(str.charAt(i)=='1'){
				count++;
			}
		}
		return count;
	}
	
	//利用位运算符来实现:
	public static int NumberOf2(int n){
		
		int count = 0;
		if(n==0)
			return count;
		
		if(n!=0){
			while(n!=0){
				count++;
				n=n&(n-1);
			}
		}
		return count;
	}

 

 

四、表示数值的字符串:

1、题目:

实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100″,”5e2″,”-123″,”3.1416″和”-1E-16″都表示数值。 但是”12e”,”1a3.14″,”1.2.3″,”+-5″和”12e+4.3″都不是。

2、代码实现:

public class Test11 {
	
	private int index = 0;
	//第二种解法:剑指offer解法:
	public boolean isNumeric(char[] str){
		if(str.length<1){
			return false;
		}
		
		boolean flag = scanInteger(str);
		if(index<str.length && str[index]=='.'){
			index++;
			flag = scanUnsignedInteger(str) || flag;
		}
		
		if(index < str.length && (str[index] =='E' || str[index]=='e')){
			index++;
			flag = flag && scanInteger(str);
		}
		
		return flag && index == str.length;
	}
	
	private boolean scanInteger(char[] str){
		if(index<str.length && (str[index]=='+' || str[index]=='-') )
			index++;
		return scanUnsignedInteger(str);
	}
	
	private boolean scanUnsignedInteger(char[] str) {
		int start = index;
		while(index < str.length && str[index]>='0' && str[index] <='9')
			index++;
		
		return start<index;//是否存在整数
	}


	
	//第一种解法:正则表达式
	public boolean isNumeric1(char[] str) {
        String string = String.valueOf(str);
        return string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
	}
	/*
	以下对正则进行解释:
	[\\+\\-]?            -> 正或负符号出现与否
	\\d*                 -> 整数部分是否出现,如-.34 或 +3.34均符合
	(\\.\\d+)?           -> 如果出现小数点,那么小数点后面必须有数字;
	                        否则一起不出现
	([eE][\\+\\-]?\\d+)? -> 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,
	                        紧接着必须跟着整数;或者整个部分都不出现
	*/
}

 

 

四、替换空格:

1、题目描述:

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

2、解题思路:

第一种:从前往后替换,后面的字符要不断往后移动,要多次移动,所以效率低下;

第二种:从后往前,先计算需要多少空间,然后从后往前移动,则每个字符只为移动一次,这样效率更高一点。

3、代码实现:

 public String replaceSpace(StringBuffer str) {

		 int spaceNum = 0;//原字符串中空格的个数
		 for(int i = 0;i<str.length();i++){
			 if(str.charAt(i)==' '){
				 spaceNum++;
			 }
		 }
		 
		 int indexOld = str.length()-1;//indexOld为为替换前的str下标
		 int newLength = str.length() + spaceNum*2;//计算空格转换成%20之后的str长度
		 int indexNew = newLength -1;//indexNew为为把空格替换为%20后的str下标
		 str.setLength(newLength);;//使str的长度扩大到转换成%20之后的长度,防止下标越界
		 
		 for(;indexOld>=0 && indexOld<indexNew;--indexOld){
			 if(str.charAt(indexOld)==' '){
				 str.setCharAt(indexNew--, '0');
				 str.setCharAt(indexNew--, '2');
				 str.setCharAt(indexNew--, '%');
			 }else{
				 str.setCharAt(indexNew--, str.charAt(indexOld));
			 }
		 }
		 
		 return str.toString();
	 }
}

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/114689.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • CultureInfo中重要的InvariantCulture[通俗易懂]

    CultureInfo中重要的InvariantCulture[通俗易懂]CultureInfo简述CultureInfo类位于System.Globalization命名空间内,这个类和这个命名空间许多人都不了解也认为不需要太多了解,实际上,你写的程序中会经常间接得使用这些类。简单的说:当进行数字,日期时间,字符串匹配时,都会进行CultureInfo的操作,也就是不同的CultureInfo下,这些操作的结果可能会不一样。这里要介绍一下非常容易被忽视的In…

  • vim取消搜索后高亮持续_vue搜索高亮

    vim取消搜索后高亮持续_vue搜索高亮vim搜索以及取消高亮搜索字符串:/abc(不需要输入双引号)按N选择下一个shift+N,选择上一个搜索后打开别的文件,也发现被高亮了,此时可以取消高亮命令模式:setnohlsearchno,即关闭,不要hl,即highlight,高亮的意思search,即搜索…

  • Jlink或者stlink用于SWD接口下载程序「建议收藏」

    Jlink或者stlink用于SWD接口下载程序「建议收藏」最近要使用stm32f103c8t6最小系统板,直接ISP串口下载程序太麻烦,就想着使用swd接口来调试。结果:通过SWD接口下载程序成功,但调试失败,还不知原因,会的的人麻烦交流一下。SWD接口:3.3VDIO(数据)CLK(时钟)GND1.首先声明jlink和stlink都有jtag和swd调试功能。jlink接口如下:如图,我使用的就是VCC…

  • 怎么看是虚拟机还是物理机_虚拟机和真实机的区别

    怎么看是虚拟机还是物理机_虚拟机和真实机的区别如何判断当前主机是物理机还是虚拟机?

  • phpstrom2019.3.3激活码破解方法

    phpstrom2019.3.3激活码破解方法,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 亿图图示mac版激活码 csdn(JetBrains全家桶)2022.01.24

    (亿图图示mac版激活码 csdn)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html1TCF2R91JZ-eyJsaWNlbnNlSW…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号