《剑指offer》-- 把数组排成最小的数、丑数、二进制中1的个数、表示数值的字符串、替换空格
一、把數(shù)組排成最小的數(shù):
1、題目:
輸入一個(gè)正整數(shù)數(shù)組,把數(shù)組里所有數(shù)字拼接起來(lái)排成一個(gè)數(shù),打印能拼接出的所有數(shù)字中最小的一個(gè)。例如輸入數(shù)組{3,32,321},則打印出這三個(gè)數(shù)字能排成的最小數(shù)字為321323。
2、解題思路:
重寫Comparator的compare()方法。
3、代碼實(shí)現(xiàn):
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>(){@Overridepublic 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();} }?
?
二、丑數(shù):
1、題目:
把只包含質(zhì)因子2、3和5的數(shù)稱作丑數(shù)(Ugly Number)。例如6、8都是丑數(shù),但14不是,因?yàn)樗|(zhì)因子7。 習(xí)慣上我們把1當(dāng)做是第一個(gè)丑數(shù)。求按從小到大的順序的第N個(gè)丑數(shù)。
2、解題思路:
參考??途W(wǎng)的“事無(wú)巨細(xì),悉究本末”:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b
2.1 通俗易懂的解釋:
首先從丑數(shù)的定義我們知道,一個(gè)丑數(shù)的因子只有2,3,5,那么丑數(shù)p = 2 ^ x * 3 ^ y * 5 ^ z,換句話說一個(gè)丑數(shù)一定由另一個(gè)丑數(shù)乘以2或者乘以3或者乘以5得到,那么我們從1開始乘以2,3,5,就得到2,3,5三個(gè)丑數(shù),在從這三個(gè)丑數(shù)出發(fā)乘以2,3,5就得到4,6,10,6,9,15,10,15,25九個(gè)丑數(shù),我們發(fā)現(xiàn)這種方法會(huì)得到重復(fù)的丑數(shù),而且我們題目要求第N個(gè)丑數(shù),這樣的方法得到的丑數(shù)也是無(wú)序的。那么我們可以維護(hù)三個(gè)隊(duì)列:
(1)丑數(shù)數(shù)組: 1
乘以2的隊(duì)列:2
乘以3的隊(duì)列:3
乘以5的隊(duì)列:5
選擇三個(gè)隊(duì)列頭最小的數(shù)2加入丑數(shù)數(shù)組,同時(shí)將該最小的數(shù)乘以2,3,5放入三個(gè)隊(duì)列;
(2)丑數(shù)數(shù)組:1,2
乘以2的隊(duì)列:4
乘以3的隊(duì)列:3,6
乘以5的隊(duì)列:5,10
選擇三個(gè)隊(duì)列頭最小的數(shù)3加入丑數(shù)數(shù)組,同時(shí)將該最小的數(shù)乘以2,3,5放入三個(gè)隊(duì)列;
(3)丑數(shù)數(shù)組:1,2,3
乘以2的隊(duì)列:4,6
乘以3的隊(duì)列:6,9
乘以5的隊(duì)列:5,10,15
選擇三個(gè)隊(duì)列頭里最小的數(shù)4加入丑數(shù)數(shù)組,同時(shí)將該最小的數(shù)乘以2,3,5放入三個(gè)隊(duì)列;
(4)丑數(shù)數(shù)組:1,2,3,4
乘以2的隊(duì)列:6,8
乘以3的隊(duì)列:6,9,12
乘以5的隊(duì)列:5,10,15,20
選擇三個(gè)隊(duì)列頭里最小的數(shù)5加入丑數(shù)數(shù)組,同時(shí)將該最小的數(shù)乘以2,3,5放入三個(gè)隊(duì)列;
(5)丑數(shù)數(shù)組:1,2,3,4,5
乘以2的隊(duì)列:6,8,10,
乘以3的隊(duì)列:6,9,12,15
乘以5的隊(duì)列:10,15,20,25
選擇三個(gè)隊(duì)列頭里最小的數(shù)6加入丑數(shù)數(shù)組,但我們發(fā)現(xiàn),有兩個(gè)隊(duì)列頭都為6,所以我們彈出兩個(gè)隊(duì)列頭,同時(shí)將12,18,30放入三個(gè)隊(duì)列;
……………………
2.2 疑問:
2.2.1 為什么分三個(gè)隊(duì)列?
丑數(shù)數(shù)組里的數(shù)一定是有序的,因?yàn)槲覀兪菑某髷?shù)數(shù)組里的數(shù)乘以2,3,5選出的最小數(shù),一定比以前未乘以2,3,5大,同時(shí)對(duì)于三個(gè)隊(duì)列內(nèi)部,按先后順序乘以2,3,5分別放入,所以同一個(gè)隊(duì)列內(nèi)部也是有序的;
2.2.2 為什么比較三個(gè)隊(duì)列頭部最小的數(shù)放入丑數(shù)數(shù)組?
因?yàn)槿齻€(gè)隊(duì)列是有序的,所以取出三個(gè)頭中最小的,等同于找到了三個(gè)隊(duì)列所有數(shù)中最小的。
2.3 實(shí)現(xiàn)思路:
我們沒有必要維護(hù)三個(gè)隊(duì)列,只需要記錄三個(gè)指針顯示到達(dá)哪一步;“|”表示指針,arr表示丑數(shù)數(shù)組;
(1)1
|2
|3
|5
目前指針指向0,0,0,隊(duì)列頭arr[0] * 2 = 2,? arr[0] * 3 = 3,? arr[0] * 5 = 5
(2)1 2
2 |4
|3 6
|5 10
目前指針指向1,0,0,隊(duì)列頭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,隊(duì)列頭arr[1] * 2 = 4,? arr[1] * 3 = 6, arr[0] * 5 = 5
………………
3、代碼實(shí)現(xiàn):
public class Test24 {//解題思路:創(chuàng)建三個(gè)隊(duì)列 https://www.nowcoder.com/ta/coding-interviewspublic int GetUglyNumber_Solution(int index) {if(index<7)return index;//創(chuàng)建三個(gè)隊(duì)列的頭指針,newNum代表的是選出的最小數(shù)int t2=0,t3=0,t5=0;int newNum=1;//創(chuàng)建一個(gè)數(shù)組,存放丑數(shù)ArrayList<Integer> list = new ArrayList<>();list.add(newNum);for(int i=1;i<index;i++){//選出三個(gè)隊(duì)列里面最小的數(shù) newNum=Math.min(list.get(t2)*2,Math.min(list.get(t3)*3,list.get(t5)*5));//這三個(gè)if有可能進(jìn)入一個(gè)或者多個(gè),進(jìn)入多個(gè)是三個(gè)隊(duì)列頭最小的數(shù)有多個(gè)的情況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;} }?
?
三、二進(jìn)制中1的個(gè)數(shù):
1、題目:
輸入一個(gè)整數(shù),輸出該數(shù)二進(jìn)制表示中1的個(gè)數(shù)。其中負(fù)數(shù)用補(bǔ)碼表示。
2、思路:
第一種:使用java內(nèi)置的的toBinaryString方法來(lái)實(shí)現(xiàn);
第二種:利用位運(yùn)算符來(lái)實(shí)現(xiàn):
(1)將n與n-1做與運(yùn)算,會(huì)把最右邊的1去掉。比如: 1100 & 1011 = 1000 ,即 12 & 11 = 8,再利用計(jì)算器count++計(jì)算出有多少個(gè) 1 即可。
(2)詳細(xì)說明:如果一個(gè)整數(shù)不為0,那么這個(gè)整數(shù)至少有一位是1。如果我們把這個(gè)整數(shù)減1,那么原來(lái)他的二進(jìn)制處在整數(shù)最右邊的1就會(huì)變?yōu)?,原來(lái)在1后面的所有的0都會(huì)變成1(如果最右邊的1后面還有0的話)。其余所有位將不會(huì)受到影響。
(3)舉個(gè)例子:一個(gè)二進(jìn)制數(shù)1100,從右邊數(shù)起第三位是處于最右邊的一個(gè)1。減去1后,第三位變成0,它后面的兩位0變成了1,而前面的1保持不變,因此得到的結(jié)果是1011。我們發(fā)現(xiàn)減1的結(jié)果是把最右邊的一個(gè)1開始的所有位都取反了。這個(gè)時(shí)候如果我們?cè)侔言瓉?lái)的整數(shù)和減去1之后的結(jié)果做與運(yùn)算,從原來(lái)整數(shù)最右邊一個(gè)1那一位開始所有位都會(huì)變成0。如1100&1011=1000.也就是說,把一個(gè)整數(shù)減去1,再和原整數(shù)做與運(yùn)算,會(huì)把該整數(shù)最右邊一個(gè)1變成0.那么一個(gè)整數(shù)的二進(jìn)制有多少個(gè)1,就可以進(jìn)行多少次這樣的操作。
3、代碼實(shí)現(xiàn):
//利用java內(nèi)置的toBinaryString方法來(lái)實(shí)現(xiàn):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;}//利用位運(yùn)算符來(lái)實(shí)現(xiàn):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;}?
?
四、表示數(shù)值的字符串:
1、題目:
實(shí)現(xiàn)一個(gè)函數(shù)用來(lái)判斷字符串是否表示數(shù)值(包括整數(shù)和小數(shù))。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數(shù)值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
2、代碼實(shí)現(xiàn):
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;//是否存在整數(shù)}//第一種解法:正則表達(dá)式public boolean isNumeric1(char[] str) {String string = String.valueOf(str);return string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");}/*以下對(duì)正則進(jìn)行解釋:[\\+\\-]? -> 正或負(fù)符號(hào)出現(xiàn)與否\\d* -> 整數(shù)部分是否出現(xiàn),如-.34 或 +3.34均符合(\\.\\d+)? -> 如果出現(xiàn)小數(shù)點(diǎn),那么小數(shù)點(diǎn)后面必須有數(shù)字;否則一起不出現(xiàn)([eE][\\+\\-]?\\d+)? -> 如果存在指數(shù)部分,那么e或E肯定出現(xiàn),+或-可以不出現(xiàn),緊接著必須跟著整數(shù);或者整個(gè)部分都不出現(xiàn)*/ }?
?
四、替換空格:
1、題目描述:
請(qǐng)實(shí)現(xiàn)一個(gè)函數(shù),將一個(gè)字符串中的每個(gè)空格替換成“%20”。例如,當(dāng)字符串為We Are Happy.則經(jīng)過替換之后的字符串為We%20Are%20Happy。
2、解題思路:
第一種:從前往后替換,后面的字符要不斷往后移動(dòng),要多次移動(dòng),所以效率低下;
第二種:從后往前,先計(jì)算需要多少空間,然后從后往前移動(dòng),則每個(gè)字符只為移動(dòng)一次,這樣效率更高一點(diǎn)。
3、代碼實(shí)現(xiàn):
public String replaceSpace(StringBuffer str) {int spaceNum = 0;//原字符串中空格的個(gè)數(shù)for(int i = 0;i<str.length();i++){if(str.charAt(i)==' '){spaceNum++;}}int indexOld = str.length()-1;//indexOld為為替換前的str下標(biāo)int newLength = str.length() + spaceNum*2;//計(jì)算空格轉(zhuǎn)換成%20之后的str長(zhǎng)度int indexNew = newLength -1;//indexNew為為把空格替換為%20后的str下標(biāo)str.setLength(newLength);;//使str的長(zhǎng)度擴(kuò)大到轉(zhuǎn)換成%20之后的長(zhǎng)度,防止下標(biāo)越界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();} }?
?
總結(jié)
以上是生活随笔為你收集整理的《剑指offer》-- 把数组排成最小的数、丑数、二进制中1的个数、表示数值的字符串、替换空格的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《剑指offer》-- 两个链表的第一个
- 下一篇: 《剑指offer》-- 和为S的连续整数