剑指offer(26-33题)详解
文章目錄
- 26 二叉搜索樹與雙向鏈表
- 27 字符串的排列
- 28 數字中出現次數超過一半的數字(待優化)★
- 29 最小的K個數
- 30 連續子數組最大和
- 31 整數中1出現的次數
- 32 把數組排成最小的數
- 33 丑數★
歡迎關注個人數據結構專欄哈
劍指offer(1-10題)詳解
劍指offer(11-25題)詳解
劍指offer(26-33題)詳解
劍指offer(34-40題)詳解
劍指offer(41-50題)詳解
劍指offer(51-59題)詳解
劍指offer(60-67題)詳解
微信公眾號:bigsai
聲明:大部分題基本未參考題解,基本為個人想法,如果由效率太低的或者錯誤還請指正!
聲明:大部分題基本未參考題解,基本為個人想法,如果由效率太低的或者錯誤還請指正!!
26 二叉搜索樹與雙向鏈表
題目描述
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向
思路:
關于二叉樹搜索(排序樹)需要我們轉換成一個排序的雙鏈表。我們知道二叉樹有left和right。用來表示樹的左右結構,但是它要求我們將樹形結構轉換成鏈式,并且還是有序的,并且還不能建立新的節點。二叉搜索樹(排序樹)我們知道它的中序序列是有序的,所以我們可以使用非遞歸的中序遍歷,先想辦法將節點按照中序的規則進行釋放。根據釋放的順序,記錄前一個節點互相記住左右就好了。
對于二叉樹各種遍歷以及遞歸非遞歸方式,筆者以前都梳理過,大家可以看看。
利用棧記錄一下節點,用一個prenode記錄前一個節點指針,當拋出當前節點兩個節點左右互指以下就OK了。當然,要有個headnode記錄第一個節點返回。
有使用遞歸版本的過也可以,可以自行嘗試。
實現代碼為:
27 字符串的排列
題目描述
輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。
輸入描述:
輸入一個字符串,長度不超過9(可能有字符重復),字符只包括大小寫字母。
思路:
這題我不知道別人有啥方法,但是我的第一感覺就是全排列問題。當然我知道的全排列有兩種實現方法。一種是dfs回溯法,另一種是普通遞歸+排序才能過這題,因為dfs的全排列是有序的,而普通遞歸交換的全排列是沒序的。這題兩者效率可能相差不大,但就全排列的序列而言。掌握遞歸的全排列效率更高。當然這也是筆者所采用的。至于全排列筆者以前雖然記錄過但寫的一般有興趣可以參考其他講解。兩種方式實現全排列。
當然,這題它可能有相同的字母,所以需要用個Hash去重下更好,但我懶直接用list判斷存不存在了。
實現代碼為:
import java.util.ArrayList; public class Solution {public ArrayList<String> Permutation(String str) {ArrayList<String> list=new ArrayList<String>();char a[]=str.toCharArray();arrange(a, 0, a.length - 1,list);list.sort(null);return list;}private void arrange(char[] a, int start, int end, ArrayList<String> list) {// TODO Auto-generated method stubif (start == end) { String team=getstr(a);if(!list.contains(team))list.add(getstr(a));return;}for (int i = start; i <= end; i++) {swap( i, start,a);arrange(a, start + 1, end,list);swap( i, start,a);} }private String getstr(char[] a) {String str="";for(int i=0;i<a.length;i++)str+=a[i];return str;}private void swap(int i, int j, char a[]) {char team=a[i];a[i]=a[j];a[j]=team; } }28 數字中出現次數超過一半的數字(待優化)★
題目描述
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由于數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
思路:
這題是一道經典題。我只知道有一個更好方法但是我還沒消化。二輪吸收討論區的時候肯定會加上的。而我采用笨方法就是hashmap存入判斷。有則返回。這樣就是開了更大的內存。這題后面還會優化。
實現代碼為:
import java.util.HashMap; import java.util.Map; public class Solution {public int MoreThanHalfNum_Solution(int[] array) {if(array.length==1)return array[0];Map<Integer, Integer> map = new HashMap<Integer, Integer>();int len = array.length / 2;int team = 0;for (int a : array) {if (map.containsKey(a)) {team = map.get(a);if (team >= len)return a;else {map.put(a, team + 1);}} else {map.put(a, 1);}}return 0;} }參考討論區:
這題是經典面試題,很多情況會問不允許開更大的空間完成這個要求,這就要根據數據的特點來完成:
如果有大于一半的數!那么假設所有數爭奪一個獵物!同樣的聚成群,不一樣的1v1同歸于盡,那么最后剩的一群或者一個那么就是大于一半的(如果存在一半的話)!
- 在具體實現就是設兩個數,一個數num等于遍歷的數,另一個count就是出現的次數。
- 如果遍歷的數等于num,那么count++;
- 如果不等于,那么消滅一個num,count--,如果count被減成0,那么就換個數,同時count變成1.
實現代碼為:
public int MoreThanHalfNum_Solution(int[] array) {int num=array[0];int count=0;for(int i=0;i<array.length;i++){if(num==array[i])count++;else {if(--count==0){num=array[i];count=1;}}}if(count==0)return 0;count=0;for(int i=0;i<array.length;i++){if(num==array[i])count++;}if(count>array.length/2)return num;return 0;}29 最小的K個數
題目描述
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
思路:
排序取前K個數字,當然需要討論下特殊情況。
實現代碼:
import java.util.Arrays; import java.util.ArrayList; public class Solution {public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {ArrayList<Integer> list=new ArrayList<Integer>();if(k>input.length)return list;Arrays.sort(input);for(int i=0;i<k;i++){list.add(input[i]);}return list;} }30 連續子數組最大和
題目描述
HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會后,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,并期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和為8(從第0個開始,到第3個為止)。給一個數組,返回它的最大連續子序列的和,你會不會被他忽悠住?(子向量的長度至少是1)
思路:
dp動態規劃問題問題,最長遞增子序列(LIS),和最長公共子串(LCS)都是經典問題。此題大家可以了解LIS。至于這種dp問題,要掌握它們的初始化+動態方程,至于這個動態方程。用dp[i]表示第i個節點的最大序列和。
而本題的dp方程為:dp[i]=max(array[i],array[i]+dp[i-1])
我們不考慮整個串串,只考慮連個點之間的關系。1、串串連續 2、串串長度任意可以為一.對于第i個節點,他想要最長的連續序列。那么有兩種情況。第一種就是和前面連在一起然后加上自己。當然前面的是多少我們不管,我們只知道前面的最大時dp[i-1].或者自立門戶長度為1不和前面的接起來。當然這需要兩者比較取最大。
實現代碼為:
31 整數中1出現的次數
題目描述
求出1~ 13的整數中1出現的次數,并算出100~ 1300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對于后面問題他就沒轍了。ACMer希望你們幫幫他,并把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。
思路:
最笨的方法(可過)。使用字符串,將從1道n的字符串拼湊成新的字符串,然后遍歷查找1就可以了。至于數學方法的話當初想了一會感覺考慮點挺多,后面還會再想想。
實現代碼:
public class Solution {public int NumberOf1Between1AndN_Solution(int n) {StringBuilder str=new StringBuilder();for(int i=1;i<=n;i++){str.append(i);}int va=0;for(int i=0;i<str.length();i++){if(str.charAt(i)=='1')va++;}return va;} }參考討論區:
用數學方法效率更高,不過這個規律還是得經驗,,,我還是有點菜當時想了一段時間總是bug。。
32 把數組排成最小的數
題目描述
輸入一個正整數數組,把數組里所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字為321323。
思路:
貪心算法,主要是一種貪心思想。其實也是個變形排序,其實你不用考慮具體的貪心思想是什么,你要保證整個序列排成的數字最小。那么你就要保證任意兩個數字排序的相對數字最小哦。,本質是需要我們考慮最小前綴和組合的問題,但是對于任意兩個數,在比較接口中你直接拼湊比較就可以了。但是如果有更好方法后面會學習。
實現代碼:
import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; public class Solution {public String PrintMinNumber(int [] numbers) {Integer[] nums=new Integer[numbers.length];//賦值for(int i=0;i<numbers.length;i++){nums[i]=numbers[i];}Arrays.sort(nums, cmp);StringBuilder str=new StringBuilder();for(int i=0;i<nums.length;i++){str.append(nums[i]);}return str.toString();}static Comparator<Integer>cmp=new Comparator<Integer>() {public int compare(Integer o1, Integer o2) {//51 3551//921 3//51 515String a1=o1+"";String a2=o2+"";return Integer.parseInt(a1+a2)-Integer.parseInt(a2+a1);}}; }33 丑數★
題目描述
把只包含質因子2、3和5的數稱作丑數(Ugly Number)。例如6、8都是丑數,但14不是,因為它包含質因子7。 習慣上我們把1當做是第一個丑數。求按從小到大的順序的第N個丑數。
思路:
這題自己的方法只過了87%的樣例。當數據大的時候就涼了。這題想了一段時間最終忍不住看了題解發現真的太雞兒秒了!
先說說我自己的想法,怎么想的,他首先沒說數據范圍是啥,所以我一直以為這個會是一個慢慢試探的過程。
整個過程數組這個結構都被我忽略了!!!!實在hold不出來,忍不住去參考了下討論區發現這個方法真的是太好了。其實思想還是跟我上面的很相似,只是結構用了最優結構——數組!
簡單來說就是用了三個光標,a2,a3,a5.每次比較三個位置分別*2,*3,*5誰最小,然后對應光標往右移動一位。!到結束即可!
看個圖就理解了:
實現代碼為:
總結
以上是生活随笔為你收集整理的剑指offer(26-33题)详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 剑指offer(11-25题)详解
- 下一篇: 【排序】归并类排序—归并排序(逆序数问题