《剑指offer》-- 和为S的连续整数序列、和为S的两个数字、左旋转字符串、翻转单词顺序列
一、和為S的連續整數序列:
1、題目:
小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他并不滿足于此,他在想究竟有多少種連續的正數序列的和為100(至少包括兩個數)。沒多久,他就得到另一組連續正數和為100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和為S的連續正數序列?
2、解題思路:
參考牛客網的“丁滿歷險記”:https://www.nowcoder.com/questionTerminal/c451a3fd84b64cb19485dad758a55ebe
1)由于我們要找的是和為S的連續正數序列,因此這個序列是個公差為1的等差數列,而這個序列的中間值代表了平均值的大小。假設序列長度為n,那么這個序列的中間值可以通過(S / n)得到,知道序列的中間值和長度,也就不難求出這段序列了。
2)滿足條件的n分兩種情況:
n為奇數時,序列中間的數正好是序列的平均值,所以條件為:(n & 1) == 1 && sum % n == 0;
n為偶數時,序列中間兩個數的平均值是序列的平均值,而這個平均值的小數部分為0.5,所以條件為:(sum % n) * 2 == n.
3)由題可知n >= 2,那么n的最大值是多少呢?我們完全可以將n從2到S全部遍歷一次,但是大部分遍歷是不必要的。為了讓n盡可能大,我們讓序列從1開始,
根據等差數列的求和公式:S = (1 + n) * n / 2,得到.
4)最后舉一個例子,假設輸入sum = 100,我們只需遍歷n = 13~2的情況(按題意應從大到小遍歷),n = 8時,得到序列[9, 10, 11, 12, 13, 14, 15, 16];n? = 5時,得到序列[18, 19, 20, 21, 22]。
3、代碼實現:時間復雜度為
public class Test28 {public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();for(int n = (int) Math.sqrt(2*sum);n>=2;n--){if((n&1)==1 && sum%n==0 || (sum%n)*2==n){ArrayList<Integer> list1 = new ArrayList<Integer>();for(int j=0,k=(sum/n)-(n-1)/2;j<n;j++,k++){list1.add(k);}list.add(list1);}}return list;} }4、第二種解法:雙指針技術:
就是相當于有一個窗口,窗口的左右兩邊就是兩個指針,我們根據窗口內值之和來確定窗口的位置和寬度。
public class Solution {public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {//存放結果ArrayList<ArrayList<Integer> > result = new ArrayList<>();//兩個起點,相當于動態窗口的兩邊,根據其窗口內的值的和來確定窗口的位置和大小int plow = 1,phigh = 2;while(phigh > plow){//由于是連續的,差為1的一個序列,那么求和公式是(a0+an)*n/2int cur = (phigh + plow) * (phigh - plow + 1) / 2;//相等,那么就將窗口范圍的所有數添加進結果集if(cur == sum){ArrayList<Integer> list = new ArrayList<>();for(int i=plow;i<=phigh;i++){list.add(i);}result.add(list);plow++;//如果當前窗口內的值之和小于sum,那么右邊窗口右移一下}else if(cur < sum){phigh++;}else{//如果當前窗口內的值之和大于sum,那么左邊窗口右移一下plow++;}}return result;} }?
二、和為S的兩個數字:
1、題目:
輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等于S,輸出兩個數的乘積最小的。
2、解題思路:
找到的第一組(相差最大的)就是乘積最小的。可以這樣證明:考慮x+y=C(C是常數),x*y的大小。
不妨設y>=x,y-x=d>=0,即y=x+d, 2x+d=C, x=(C-d)/2, x*y=x(x+d)=(C-d)(C+d)/4=(C^2-d^2)/4,
也就是x*y是一個關于變量d的二次函數,對稱軸是y軸,開口向下。d是>=0的,d越大, x*y也就越小。
3、代碼實現:
public class Test29 {public static ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {ArrayList<Integer> list = new ArrayList<Integer>();int start = 0;int end = array.length-1;while(start<end){if(array[start]+array[end] == sum){list.add(array[start]);list.add(array[end]);break;}while(start<end && array[start]+array[end]>sum){end--;}while(start<end && array[start]+array[end]<sum){start++;}}return list;} }?
?
三、左旋轉字符串:
1、題目:
匯編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對于一個給定的字符序列S,請你把其循環左移K位后的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位后的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!
2、代碼實現:
public class Test27 {public String LeftRotateString(String str,int n) {int len=str.length();if(str==null || len==0){return str;}n=n%len;len=len+n;str+=str;return str.substring(n,len);} }3、其他解法:
很顯然,上面的解法雖然能夠能到我們想要的答案,但是這道題想考察的知識點應該不是這個,
這道題考的核心是應聘者是不是可以靈活利用字符串翻轉。假設字符串abcdef,n=3,設X=abc,Y=def,所以字符串可以表示成XY,如題干,問如何求得YX。假設X的翻轉為XT,XT=cba,同理YT=fed,那么YX = (XT YT)T,三次翻轉后可得結果。
//第二種解法:利用YX=(XTYT)Tpublic String LeftRotateString(String str,int n) {int len = str.length();if(str == null || n==0 || len==0)return str;n=n%len;char[] array = str.toCharArray();reverse(array,0,n-1);reverse(array, n, len-1);reverse(array, 0, len-1);StringBuilder stringBuilder = new StringBuilder();for(char c:array)stringBuilder.append(c);return stringBuilder.toString();}public void reverse(char[] array,int low,int high){char temp;while(low<high){temp = array[low];array[low]=array[high];array[high]=temp;low++;high--;}}?
四、翻轉單詞順序列:
1、題目:
牛客最近來了一個新員工Fish,每天早晨總是會拿著一本英文雜志,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。后來才意識到,這家伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他么?
2、解題方法:
看代碼注釋。
3、代碼實現:
public class Test26 {//第二種解法:先反轉整個個句子,在反轉每個單詞public String ReverseSentence(String str) {if(str.trim().equals("")) return str;char[] chars=str.toCharArray();reverse(chars,0,chars.length-1);//翻轉整個句子int blank=-1;//第一個空格符的位置int nextBlank=-1;第二個空格符的位置//翻轉每個單詞for(int i=0;i<chars.length;i++){if(chars[i]==' '){nextBlank = i;reverse(chars,blank+1,nextBlank-1);blank=nextBlank;}}//翻轉最后一個單詞reverse(chars,blank+1,chars.length-1);return new String(chars);}public void reverse(char[] chars,int low,int high){while(high>low){char temp = chars[low];chars[low]=chars[high];chars[high]=temp;low++;high--;}}//第一種解法:public String ReverseSentence1(String str) {if(str.trim().equals("")) return str;String[] strArray=str.split(" ");StringBuilder builder = new StringBuilder();for(int i=strArray.length;i>0;i--){builder.append(strArray[i-1]+" ");}return builder.substring(0,builder.length()-1);} }?
總結
以上是生活随笔為你收集整理的《剑指offer》-- 和为S的连续整数序列、和为S的两个数字、左旋转字符串、翻转单词顺序列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《剑指offer》-- 把数组排成最小的
- 下一篇: 《剑指offer》-- 构建乘积数组、求