劍指offer所有題目java版
//面試題1:賦值運算子函式 /** * 賦值運算子函式 * 1.對於傳入的引數,不應該被修改,使用final修飾; * 2.如果兩個物件相同或值相等,不進行操作,直接返回; * 3.返回值最好為this,這樣可以使賦值連結起來。 * 一個缺點:此賦值從左到右進行,a=b=c等價於a=c,b不會被賦值; * 而如果是String的=運算,a,b都會被賦成c的值。 */ public class P25_AssignmentOperator { public static class MyString{ private String data; public MyString(String data) { this.data = data; } public MyString assign(final MyString another){ if(this==another || this.data.equals(another.data)) return this; else{ this.data = another.data; return this; } } @Override public String toString() { return "MyString{" + "data='" + data + '\'' + '}'; } } public static void main(String[] args) { MyString s1 = new MyString("a"); MyString s2 = new MyString("b"); MyString s3 = new MyString("c"); System.out.println(s1.assign(s2).assign(s3)); System.out.println("s1:" + s1); System.out.println("s2:" + s2); System.out.println("s3:" + s3); } } //面試題2:實現Singleton模式 /** * Created by ryder on 2017/6/7. * 單例模式 * 定義:指實現了特殊模式的類,該類僅能被例項化一次,產生唯一的一個物件 * 應用舉例:windows的工作管理員,回收站,web應用的配置物件,spring中的bean預設也是單例 * 分類:餓漢式,懶漢式,雙檢鎖,靜態內部類,列舉 * 評價指標有:單例(必須),執行緒安全,延遲載入,防止反序列化產生新物件,防止反射攻擊 * 實現方法的選擇:一般情況下直接使用餓漢式就好了,要求延遲載入時傾向於用靜態內部類,涉及到反序列化建立物件或反射問題最好選擇列舉 */ public class P32_Singleton { public static void main(String[] args){ //呼叫方式 Singleton1 singleton1 = Singleton1.getInstance(); Singleton2 singleton2 = Singleton2.getInstance(); Singleton3 singleton3 = Singleton3.getInstance(); Singleton4 singleton4 = Singleton4.getInstance(); Singleton5 singleton5 = Singleton5.getInstance(); Singleton6 singleton6 = Singleton6.getInstance(); Singleton7 singleton7 = Singleton7.instance; singleton7.setAttribute("aaa"); } }
//版本一:餓漢式 //特點:執行緒安全;在類初始化執行到靜態屬性時就分配了資源,有資源浪費問題; class Singleton1{ //或者將私有靜態final成員設為公有成員,可省去getInstance公有函式 private static final Singleton1 instance = new Singleton1(); private Singleton1(){} public static Singleton1 getInstance(){ return instance; } }
//版本二:懶漢式(非執行緒安全) //特點:在第一次呼叫獲取例項方法時分配記憶體,實現了懶載入;非執行緒安全; class Singleton2{ private static Singleton2 instance= null; private Singleton2(){} public static Singleton2 getInstance(){ if(instance==null){ instance = new Singleton2(); } return instance; } }
//版本三:懶漢式變種(synchronized同步方法,支援多執行緒) //特點:執行緒安全;synchronized而造成的阻塞致使效率低,而且很多的阻塞都是沒必要的。 class Singleton3{ private static Singleton3 instance = null; private Singleton3(){} public static synchronized Singleton3 getInstance(){ if(instance == null) instance = new Singleton3(); return instance; } }
//版本四:懶漢式變種(synchronized同步塊,支援多執行緒) //特點:寫法不同,但與版本三有一樣的問題 class Singleton4{ private static Singleton4 instance = null; private Singleton4(){} public static Singleton4 getInstance(){ synchronized(Singleton4.class) { if (instance == null) instance = new Singleton4(); } return instance; } }
//版本五:雙檢鎖DCL,支援多執行緒-懶漢式 //特點:執行緒安全;多進行一次if判斷,加入volatile修飾,優點是隻有在第一次例項化時加鎖,之後不會加鎖,提升了效率,缺點寫法複雜 //不加入volatile,可能出現第一個if判斷不為null,但還並未執行建構函式的情況,因為java編譯器會進行指令重排; //volatile的兩大作用: //1防止編譯器對被修飾變數相關程式碼進行指令重排;2讀寫操作都不會呼叫工作記憶體而是直接取主存,保證了記憶體可見性 //指令重排: //instance = new Singleton5()可主要分為三步:1分配記憶體,2呼叫建構函式,3instance指向被分配的記憶體(此時instance不為null了) //正常順序為123,指令重排可能執行順序為132,會造成已不為null但未執行建構函式的問題 //記憶體可見性: //如果欄位是被volatile修飾的,Java記憶體模型將在寫操作後插入一個寫屏障指令,在讀操作前插入一個讀屏障指令。 //這意味著:1一旦完成寫入,任何訪問這個欄位的執行緒將會得到最新的;2在寫入前,任何更新過的資料值是可見的,因為記憶體屏障會把之前的寫入值都重新整理到快取。 //因此volatile可提供一定的執行緒安全,但不適用於寫操作依賴於當前值的情況,如自增,自減 //簡單來說,volatile適合這種場景:一個變數被多個執行緒共享,執行緒直接給這個變數賦值。 //還能在雙檢鎖上進行優化,引入一個區域性變數,但個人覺得效率提成並不大,不再贅述。 //volatile參考:http://blog.csdn.net/qq_29923439/article/details/51273812 class Singleton5{ private volatile static Singleton5 instance = null; private Singleton5(){} public static Singleton5 getInstance(){ if(instance==null){ synchronized (Singleton5.class){ if(instance==null) instance = new Singleton5(); } } return instance; } }
//版本六:靜態內部類,支援多執行緒-懶漢式 //特點:利用靜態內部類(只有在出現它的引用時才被載入),完成懶載入;final保證執行緒安全; //類的載入順序:http://blog.csdn.net/u012123160/article/details/53224469 //final的作用: //1. 在建構函式內對一個final域的寫入,與隨後把這個被構造物件的引用賦值給一個引用變數,這兩個操作之間不能重排序。 //2. 初次讀一個包含final域的物件的引用,與隨後讀這個final域,這兩個操作之間不能重排序。 //擴充套件:static變數初始化遵循以下規則: //1.靜態變數會按照宣告的順序先依次宣告並設定為該型別的預設值,但不賦值為初始化的值。 //2.宣告完畢後,再按宣告的順序依次設定為初始化的值,如果沒有初始化的值就跳過。 //static變數初始化參考:http://www.jb51.net/article/86629.htm class Singleton6{ private Singleton6(){} public static Singleton6 getInstance(){ return Singleton6Holder.instance; } private static class Singleton6Holder{ public static final Singleton6 instance = new Singleton6(); } }
//版本七:通過列舉實現 //一個完美的單例需要做到:單例,懶載入,執行緒安全,防止反序列化產生新物件,防止反射攻擊 //而列舉的特性保證了以上除了懶載入以外的所有要求,而且實現程式碼及其簡單 //Enum的單例模式參考:http://www.jianshu.com/p/83f7958b0944 enum Singleton7{ instance; private String attribute; void setAttribute(String attribute){ this.attribute = attribute; } String getAttribute(){ return this.attribute; } }
//面試題3:陣列中重複的數字 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[] k=new boolean[length]; for(int i=0;i<k.length;i++){ if(k[numbers[i]]==true){ duplication[0]=numbers[i]; return true; } k[numbers[i]] = true; } return false; } } /** 最簡單的方法:我最直接的想法就是構造一個容量為N的輔助陣列B,原陣列A中每個數對應B中下標,首次命中,B中對應元素+1。如果某次命中時,B中對應的不為0,說明,前邊已經有一樣數字了,那它就是重複的了。 舉例:A{1,2,3,3,4,5},剛開始B是{0,0,0,0,0,0},開始掃描A。 A[0] = 1 {0,1,0,0,0,0} A[1] = 2 {0,1,1,0,0,0} A[2] = 3 {0,1,1,1,0,0} A[3] = 3 {0,1,1,2,0,0},到這一步,就已經找到了重複數字。 A[4] = 4 {0,1,1,2,1,0} A[5] = 5 {0,1,1,2,1,1} 時間複雜度O(n),空間複雜度O(n),演算法優點是簡單快速,比用set更輕量更快,不打亂原陣列順序。 如果不能用輔助空間,可以參照劍指。 */ public class Solution { public boolean duplicate(int numbers[],int length,int [] duplication) { int[] assist = new int [length]; for(int i = 0; i < length; i++){ if(assist[numbers[i]] == 0){ assist[numbers[i]] ++; }else{ duplication[0] = numbers[i]; return true; } } return false; } }
//面試題4:二維陣列中的查詢 public class Solution { public boolean Find(int target, int [][] array) { int row=array.length; int col=array[0].length; int i=row-1; int j=0; while(i>=0&&j<col){ if(target<array[i][j]) i--; else if(target>array[i][j]) j++; else return true; } return false; } }
//面試題5:替換空格 public class Solution { public String replaceSpace(StringBuffer str) { return str.toString().replaceAll("\\s","%20"); } } public class Solution { 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; int newlength=str.length()+spacenum*2; int indexnew=newlength-1; str.setLength(newlength); for(;indexold>=0&&indexold<newlength;--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(); } }
//面試題6:從尾到頭列印連結串列 /** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { * this.val = val; * } * } * */ import java.util.ArrayList; public class Solution { ArrayList<Integer> arrayList=new ArrayList<Integer>(); public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { if(listNode!=null){ this.printListFromTailToHead(listNode.next); arrayList.add(listNode.val); } return arrayList; } } //用堆疊的後進後出實現,時間複雜度低 /** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { * this.val = val; * } * } * */ import java.util.ArrayList; import java.util.Stack; public class Solution { // ArrayList<Integer> arrayList=new ArrayList<Integer>(); public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { Stack<Integer> stack=new Stack<Integer>(); while(listNode!=null){ stack.push(listNode.val); listNode=listNode.next; } ArrayList<Integer> list=new ArrayList<Integer>(); while(!stack.isEmpty()){ list.add(stack.pop()); } return list; } }
//面試題7:重建二叉樹 public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1); return root; } //前序遍歷{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6} private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) { if(startPre>endPre||startIn>endIn) return null; TreeNode root=new TreeNode(pre[startPre]); for(int i=startIn;i<=endIn;i++) if(in[i]==pre[startPre]){ root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1); root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn); break; } return root; } } //面試題8:二叉樹的下一個結點 /* public class TreeLinkNode { int val; TreeLinkNode left = null; TreeLinkNode right = null; TreeLinkNode next = null;
TreeLinkNode(int val) { this.val = val; } } */ public class Solution { public TreeLinkNode GetNext(TreeLinkNode node) { if(node==null) return null; if(node.right!=null) { node=node.right; while(node.left!=null) node=node.left; return node; } while(node.next!=null) { if(node.next.left==node) return node.next; node=node.next; } return null; } } //面試題9:用兩個棧實現佇列 import java.util.Stack;
public class Solution { Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); public void push(int node) { stack1.push(node); } public int pop() { if(stack1.empty()&&stack2.empty()){ throw new RuntimeException("Queue is empty"); } if(stack2.empty()){ while(!stack1.empty()){ stack2.push(stack1.pop()); } } return stack2.pop(); } } //面試題10:斐波那契數列 public class Solution { public int Fibonacci(int n) { int a=1; int b=0; int result=0; if(n==0) return 0; if(n==1) return 1; for(int i=2;i<=n;i++){ result=a+b; b=a; a=result; } return result; } } //面試題11:旋轉陣列的最小數字 import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { int low=0; int high=array.length-1; while(low<high){ int mid=low+(high-low)/2; if(array[mid]>array[high]){ low=mid+1; }else if(array[mid]==array[high]){ high=high-1; }else{ high=mid; } } return array[low]; } } //面試題12:矩陣中的路徑 public class Solution { public boolean hasPath(char[] matrix,int rows,int cols,char[] str) { int flag[]=new int[matrix.length]; for(int i=0;i<rows;i++) { for(int j=0;j<cols;j++) { if(helper(matrix,rows,cols,i,j,str,0,flag)) return true; } } return false; } public boolean helper(char[] matrix,int rows,int cols,int i,int j,char[] str,int k,int[] flag) { int index=i*cols+j; if(i<0||i>=rows||j<0||j>=cols||matrix[index]!=str[k]||flag[index]==1) return false; if(k==str.length-1) return true; flag[index]=1; if(helper(matrix,rows,cols,i-1,j,str,k+1,flag) ||helper(matrix,rows,cols,i+1,j,str,k+1,flag) ||helper(matrix,rows,cols,i,j-1,str,k+1,flag) ||helper(matrix,rows,cols,i,j+1,str,k+1,flag)){ return true; } flag[index]=0; return false;
}
} //面試題13:機器人的運動範圍 public class Solution { public int movingCount(int threshold,int rows,int cols) { int flag[][]=new int[rows][cols]; return helper(0,0,rows,cols,flag,threshold); } private int helper(int i,int j,int rows,int cols,int [][] flag,int threshold) { if(i<0||i>=rows||j<0||j>=cols||numSum(i)+numSum(j)>threshold||flag[i][j]==1) return 0; flag[i][j]=1; return helper(i-1,j,rows,cols,flag,threshold) +helper(i+1,j,rows,cols,flag,threshold) +helper(i,j-1,rows,cols,flag,threshold) +helper(i,j+1,rows,cols,flag,threshold) +1; } private int numSum(int i) { int sum=0; do { sum+=i%10; }while((i=i/10)>0); return sum; } } //面試題14:剪繩子 /* * 面試題14:剪繩子 * 題目:給你一根長度為n的繩子,請把繩子剪成m段(m和n都是整數,n>1並且m>1)每段繩子的長度記為k[0],k[1],...,k[m]. * 請問k[0]*k[1]*...*k[m]可能的最大乘積是多少? * 例如,當繩子的長度為8時,我們把它剪成長度分別為2,3,3的三段,此時得到的最大乘積是18. */ /* 外層迴圈從繩子長度為4開始,由題意可知最少剪一刀,所以內層迴圈從j= 1開始, 分別計算f(j)*f(i-j)的值,並且與當前記錄的最大值max進行比較。 本題是一道典型的動態規劃演算法題,為了避免遞迴產生的重複計算,採用了從下而上的計算順序實現。 */ public class Test{ public static void main(String[] args) { System.out.println(maxAfterCutting(8)); } /** * 常規的需要O(n2)的時間複雜度和O(n)的空間複雜度的動態規劃思路 * 題目的意思是:繩子至少是2米,並且必須最少剪一刀。 */ public static int maxAfterCutting(int length){ if(length<2) return 0; if(length==2) return 1; if(length==3) return 2; // 子問題的最優解儲存在f陣列中,陣列中的第i個元素表示把長度為i的繩子剪成若干段後各段長度乘積的最大值。 int[] f = new int[length+1]; f[0] = 0; f[1] = 1; f[2] = 2; f[3] = 3; int result = 0; for(int i = 4;i<=length;i++){ int max = 0; for(int j = 1;j<=i/2;j++){ int num = f[j]*f[i-j]; if(max<num) max = num; f[i] = max; } } result = f[length]; return result; } }
//面試題15:二進位制中1的個數 public class Solution { public int NumberOf1(int n) { int count=0; while(n!=0){ ++count; n=(n-1)&n; } return count; } } //面試題16:數值的整數次方 public class Solution { public double Power(double base, int n) { double res=1; double curr=base; int exponent; if(n>0) { exponent=n; }else if(n<0) { if(base==0) throw new RuntimeException("分母不能為0"); exponent=-n; }else { return 1; }while(exponent!=0) { if((exponent&1)==1) res*=curr; curr*=curr; exponent>>=1; } return n>=0?res:(1/res); } } //面試題17:列印從1到最大的n位數 public static void main(String[] args) { // TODO Auto-generated method stub Main test = new Main(); test.printToMax(3); //test.printToMax2(3); } /**字串上模擬加法 * * @param n */ public void printToMax(int n){ if(n < 0) return; char[] number = new char[n]; // 初始化 // for(int i = 0; i < n; i++) // number[i] = '0'; Arrays.fill(number, '0'); while(!increment(number)){ printNumber(number); } return; } public boolean increment(char[] num){ boolean isOverflow = false; int size = num.length; int carry = 0; for(int i = size - 1; i >= 0; i--){ int temp = num[i] - '0' + carry; if(i == size - 1) temp++; if(temp >= 10){ if(i == 0) //最高位溢位 isOverflow = true; else{ temp -= 10; carry = 1; num[i] = (char) ('0' + temp); } }else{ num[i] = (char)('0' + temp); break; } } return isOverflow; } public void printNumber(char[] num){ int size = num.length; int i = 0; while(i < size && num[i] == '0') //i < size在前,否則越界 i++; //char[] printNum = new char[size - i]; //System.arraycopy(num, i, printNum, 0, size - i);//複製陣列 if(i == size)//不列印全0 return; char[] printNum = Arrays.copyOfRange(num, i, size);//複製陣列 System.out.println(printNum); } /**字元每一位進行全排列 * * @param n */ public void printToMax2(int n){ if(n <= 0) return; char[] number = new char[n]; Arrays.fill(number, '0'); printOrder(number,n,0); } public void printOrder(char[] number, int n, int loc){ if(loc == n) return; for(int i = 0; i <= 9; i++){ number[loc] = (char)('0' + i); if(loc == n - 1){ printNumber(number); } printOrder(number,n,loc + 1); } } //面試題18:刪除連結串列的節點 /* public class ListNode { int val; ListNode next = null;
ListNode(int val) { this.val = val; } } */ public class Solution { public ListNode deleteDuplication(ListNode pHead){ if(pHead==null||pHead.next==null) { return pHead; } if(pHead.val==pHead.next.val) { ListNode pNode=pHead.next; while(pNode!=null&&pNode.val==pHead.val) { pNode=pNode.next; } return deleteDuplication(pNode); }else { pHead.next=deleteDuplication(pHead.next); return pHead; }
} } //面試題19:正則表示式匹配 public class Solution { public boolean match(char[] str, char[] pattern) { if (str == null || pattern == null) { return false; } int strIndex = 0; int patternIndex = 0; return matchCore(str, strIndex, pattern, patternIndex); }
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) { if (strIndex == str.length && patternIndex == pattern.length) { return true; } if (strIndex != str.length && patternIndex == pattern.length) { return false; } if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') { if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { return matchCore(str, strIndex, pattern, patternIndex + 2) || matchCore(str, strIndex + 1, pattern, patternIndex + 2) || matchCore(str, strIndex + 1, pattern, patternIndex); } else { return matchCore(str, strIndex, pattern, patternIndex + 2); } } if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { return matchCore(str, strIndex + 1, pattern, patternIndex + 1); } return false; } } //面試題20:表示數值的字串 //正則表示式解法 public class Solution { public boolean isNumeric(char[] str) { String string = String.valueOf(str); return string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?"); } } /* 以下對正則進行解釋: [\\+\\-]? -> 正或負符號出現與否 \\d* -> 整數部分是否出現,如-.34 或 +3.34均符合 (\\.\\d+)? -> 如果出現小數點,那麼小數點後面必須有數字; 否則一起不出現 ([eE][\\+\\-]?\\d+)? -> 如果存在指數部分,那麼e或E肯定出現,+或-可以不出現, 緊接著必須跟著整數;或者整個部分都不出現 */ //參見劍指offer public class Solution { private int index = 0; 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; //是否存在整數 } } //面試題21:調整陣列順序使奇數位於偶數前面 public class Solution { /* 整體思路: 首先統計奇數的個數 然後新建一個等長陣列,設定兩個指標,奇數指標從0開始,偶數指標從奇數個數的末尾開始 遍歷,填數 */ public void reOrderArray(int [] array) { if(array.length==0||array.length==1) return; int oddCount=0,oddBegin=0; int[] newArray=new int[array.length]; for(int i=0;i<array.length;i++){ if((array[i]&1)==1) oddCount++; } for(int i=0;i<array.length;i++){ if((array[i]&1)==1) newArray[oddBegin++]=array[i]; else newArray[oddCount++]=array[i]; } for(int i=0;i<array.length;i++){ array[i]=newArray[i]; } } } //面試題22:連結串列中倒數第k個節點 //程式碼思路如下:兩個指標,先讓第一個指標和第二個指標都指向頭結點 //然後再讓第一個指正走(k-1)步,到達第k個節點。 //然後兩個指標同時往後移動,當第一個結點到達末尾的時候,第二個結點所在位置就是倒數第k個節點了。。 /* public class ListNode { int val; ListNode next = null;
ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode FindKthToTail(ListNode head,int k) { if(head==null||k<=0){ return null; } ListNode pre=head; ListNode last=head; for(int i=1;i<k;i++){ if(pre.next!=null){ pre=pre.next; }else{ return null; } } while(pre.next!=null){ pre = pre.next; last=last.next; } return last; } } //面試題23:連結串列中環的入口節點 /* public class ListNode { int val; ListNode next = null;
ListNode(int val) { this.val = val; } } */ //第一步,找環中相匯點 //分別用p1,p2指向連結串列頭部,p1每次走一步,p2每次走二步,直到p1==p2找到在環中的相匯點。 //第二步,找環的入口 //當p1==p2時,p2所經過節點數為2x,p1所經過節點數為x //設環中有n個節點,p2比p1多走一圈有2x=n+x; n=x //可以看出p1實際走了一個環的步數,再讓p2指向連結串列頭部,p1位置不變,p1,p2每次走一步直到p1==p2; 此時p1指向環的入口。 public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead){ if(pHead == null || pHead.next == null) return null; ListNode p1 = pHead; ListNode p2 = pHead; while(p2 != null && p2.next != null ){ p1 = p1.next; p2 = p2.next.next; if(p1 == p2){ p2 = pHead; while(p1 != p2){ p1 = p1.next; p2 = p2.next; } if(p1 == p2) return p1; } } return null; } } //面試題24:反轉連結串列 /* public class ListNode { int val; ListNode next = null;
ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode ReverseList(ListNode head) { ListNode pre = null; ListNode next = null; while (head != null) { next = head.next; head.next = pre; pre = head; head = next; } return pre; } } //面試題25:合併兩個排序的連結串列 /* public class ListNode { int val; ListNode next = null;
ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { if(list1 == null){ return list2; } if(list2 == null){ return list1; } ListNode mergeHead = null; ListNode current = null; while(list1!=null && list2!=null){ if(list1.val <= list2.val){ if(mergeHead == null){ mergeHead = current = list1; }else{ current.next = list1; current = current.next; } list1 = list1.next; }else{ if(mergeHead == null){ mergeHead = current = list2; }else{ current.next = list2; current = current.next; } list2 = list2.next; } } if(list1 == null){ current.next = list2; }else{ current.next = list1; } return mergeHead; } } //面試題26:樹的子結構 /** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null;
public TreeNode(int val) { this.val = val;
}
} */ public class Solution { public static boolean HasSubtree(TreeNode root1, TreeNode root2) { boolean result = false; //當Tree1和Tree2都不為零的時候,才進行比較。否則直接返回false if (root2 != null && root1 != null) { //如果找到了對應Tree2的根節點的點 if(root1.val == root2.val){ //以這個根節點為為起點判斷是否包含Tree2 result = doesTree1HaveTree2(root1,root2); } //如果找不到,那麼就再去root的左兒子當作起點,去判斷時候包含Tree2 if (!result) { result = HasSubtree(root1.left,root2); } //如果還找不到,那麼就再去root的右兒子當作起點,去判斷時候包含Tree2 if (!result) { result = HasSubtree(root1.right,root2); } } //返回結果 return result; } public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) { //如果Tree2已經遍歷完了都能對應的上,返回true if (node2 == null) { return true; } //如果Tree2還沒有遍歷完,Tree1卻遍歷完了。返回false if (node1 == null) { return false; } //如果其中有一個點沒有對應上,返回false if (node1.val != node2.val) { return false; } //如果根節點對應的上,那麼就分別去子節點裡面匹配 return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right); } } //面試題27:二叉樹的映象 /** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null;
public TreeNode(int val) { this.val = val;
}
} */ public class Solution { public void Mirror(TreeNode root){ if(root==null) return; if(root.left==null&&root.right==null) return; TreeNode temp=root.left; root.left=root.right; root.right=temp; if(root.left!=null) Mirror(root.left); if(root.right!=null) Mirror(root.right); } } //面試題28:對稱的二叉樹 /* public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null;
public TreeNode(int val) { this.val = val;
}
} */ public class Solution { //遞迴 boolean isSymmetrical(TreeNode pRoot) { if(pRoot == null) return true; return isSymmetrical(pRoot.left, pRoot.right); } private boolean isSymmetrical(TreeNode left, TreeNode right) { if(left == null && right == null) return true; if(left == null || right == null) return false; return left.val == right.val //為映象的條件:左右節點值相等 && isSymmetrical(left.left, right.right) //2.對稱的子樹也是映象 && isSymmetrical(left.right, right.left); } } //面試題29:順時針列印矩陣 //主體迴圈部分才5行。其實是有規律可循的。將每一層的四個邊角搞清楚就可以打印出來了 import java.util.ArrayList; public class Solution { public ArrayList<Integer> printMatrix(int [][] array) { ArrayList<Integer> result = new ArrayList<Integer> (); if(array.length==0) return result; int n = array.length,m = array[0].length; if(m==0) return result; int layers = (Math.min(n,m)-1)/2+1;//這個是層數 for(int i=0;i<layers;i++){ for(int k = i;k<m-i;k++) result.add(array[i][k]);//左至右 for(int j=i+1;j<n-i;j++) result.add(array[j][m-i-1]);//右上至右下 for(int k=m-i-2;(k>=i)&&(n-i-1!=i);k--) result.add(array[n-i-1][k]);//右至左 for(int j=n-i-2;(j>i)&&(m-i-1!=i);j--) result.add(array[j][i]);//左下至左上 } return result; } }
//面試題30:包含min函式的棧 import java.util.Stack; /* 思路:用一個棧data儲存資料,用另外一個棧min儲存依次入棧最小的數 比如,data中依次入棧,5, 4, 3, 8, 10, 11, 12, 1 則min依次入棧,5, 4, 3,no,no, no, no, 1 no代表此次不如棧 每次入棧的時候,如果入棧的元素比min中的棧頂元素小或等於則入棧,否則不如棧。 */ public class Solution { Stack<Integer> data = new Stack<Integer>(); Stack<Integer> min = new Stack<Integer>(); Integer temp = null; public void push(int node) { if(temp != null){ if(node <= temp ){ temp = node; min.push(node); } data.push(node); }else{ temp = node; data.push(node); min.push(node); } } public void pop() { int num = data.pop(); int num2 = min.pop(); if(num != num2){ min.push(num2); } } public int top() { int num = data.pop(); data.push(num); return num; } public int min() { int num = min.pop(); min.push(num); return num; } } //面試題31:棧的壓入、彈出序列 /* 【思路】借用一個輔助的棧,遍歷壓棧順序,先講第一個放入棧中,這裡是1,然後判斷棧頂元素是不是出棧順序的第一個元素,這裡是4,很顯然1≠4,所以我們繼續壓棧,直到相等以後開始出棧,出棧一個元素,則將出棧順序向後移動一位,直到不相等,這樣迴圈等壓棧順序遍歷完成,如果輔助棧還不為空,說明彈出序列不是該棧的彈出順序。
舉例:
入棧1,2,3,4,5
出棧4,5,3,2,1
首先1入輔助棧,此時棧頂1≠4,繼續入棧2
此時棧頂2≠4,繼續入棧3
此時棧頂3≠4,繼續入棧4
此時棧頂4=4,出棧4,彈出序列向後一位,此時為5,,輔助棧裡面是1,2,3
此時棧頂3≠5,繼續入棧5
此時棧頂5=5,出棧5,彈出序列向後一位,此時為3,,輔助棧裡面是1,2,3
….
依次執行,最後輔助棧為空。如果不為空說明彈出序列不是該棧的彈出順序。 */ import java.util.ArrayList; import java.util.Stack; public class Solution { public boolean IsPopOrder(int [] pushA,int [] popA) { if(pushA.length == 0 || popA.length == 0) return false; Stack<Integer> s = new Stack<Integer>(); //用於標識彈出序列的位置 int popIndex = 0; for(int i = 0; i< pushA.length;i++){ s.push(pushA[i]); //如果棧不為空,且棧頂元素等於彈出序列 while(!s.empty() &&s.peek() == popA[popIndex]){ //出棧 s.pop(); //彈出序列向後一位 popIndex++; } } return s.empty(); } } //面試題32:從上到下列印二叉樹 /** 思路是用arraylist模擬一個佇列來儲存相應的TreeNode */ import java.util.*; public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList<Integer> list = new ArrayList<>(); ArrayList<TreeNode> queue = new ArrayList<>(); if (root == null) { return list; } queue.add(root); while (queue.size() != 0) { TreeNode temp = queue.remove(0); if (temp.left != null){ queue.add(temp.left); } if (temp.right != null) { queue.add(temp.right); } list.add(temp.val); } return list; } } //面試題33:二叉搜尋樹的後序遍歷序列 public class Solution { public boolean VerifySquenceOfBST(int [] sequence) { if(sequence.length==0) return false; if(sequence.length==1) return true; return ju(sequence, 0, sequence.length-1); } public boolean ju(int[] a,int star,int root){ if(star>=root) return true; int i = root; //從後面開始找 while(i>star&&a[i-1]>a[root]) i--;//找到比根小的座標 //從前面開始找 star到i-1應該比根小 for(int j = star;j<i-1;j++) if(a[j]>a[root]) return false;; return ju(a,star,i-1)&&ju(a, i, root-1); } } //面試題34:二叉樹中和為某一值的路徑 import java.util.*; public class Solution { private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>(); private ArrayList<Integer> list = new ArrayList<Integer>(); public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) { if(root == null) return listAll; list.add(root.val); target -= root.val; if(target == 0 && root.left == null && root.right == null) listAll.add(new ArrayList<Integer>(list)); FindPath(root.left, target); FindPath(root.right, target); list.remove(list.size()-1); return listAll; } } //面試題35:複雜連結串列的複製 /* public class RandomListNode { int label; RandomListNode next = null; RandomListNode random = null;
RandomListNode(int label) { this.label = label; } } */ /* *解題思路: *1、遍歷連結串列,複製每個結點,如複製結點A得到A1,將結點A1插到結點A後面; *2、重新遍歷連結串列,複製老結點的隨機指標給新結點,如A1.random = A.random.next; *3、拆分連結串列,將連結串列拆分為原連結串列和複製後的連結串列 */ public class Solution { public RandomListNode Clone(RandomListNode pHead) { if(pHead == null) { return null; } RandomListNode currentNode = pHead; //1、複製每個結點,如複製結點A得到A1,將結點A1插到結點A後面; while(currentNode != null){ RandomListNode cloneNode = new RandomListNode(currentNode.label); RandomListNode nextNode = currentNode.next; currentNode.next = cloneNode; cloneNode.next = nextNode; currentNode = nextNode; } currentNode = pHead; //2、重新遍歷連結串列,複製老結點的隨機指標給新結點,如A1.random = A.random.next; while(currentNode != null) { currentNode.next.random = currentNode.random==null?null:currentNode.random.next; currentNode = currentNode.next.next; } //3、拆分連結串列,將連結串列拆分為原連結串列和複製後的連結串列 currentNode = pHead; RandomListNode pCloneHead = pHead.next; while(currentNode != null) { RandomListNode cloneNode = currentNode.next; currentNode.next = cloneNode.next; cloneNode.next = cloneNode.next==null?null:cloneNode.next.next; currentNode = currentNode.next; } return pCloneHead; } } //面試題36:二叉搜尋樹與雙向連結串列 //直接用中序遍歷 public class Solution { TreeNode head = null; TreeNode realHead = null; public TreeNode Convert(TreeNode pRootOfTree) { ConvertSub(pRootOfTree); return realHead; } private void ConvertSub(TreeNode pRootOfTree) { if(pRootOfTree==null) return; ConvertSub(pRootOfTree.left); if (head == null) { head = pRootOfTree; realHead = pRootOfTree; } else { head.right = pRootOfTree; pRootOfTree.left = head; head = pRootOfTree; } ConvertSub(pRootOfTree.right); } } //面試題37:序列化二叉樹 /* public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { public int index = -1; String Serialize(TreeNode root) { StringBuffer sb = new StringBuffer(); if(root == null){ sb.append("#,"); return sb.toString(); } sb.append(root.val + ","); sb.append(Serialize(root.left)); sb.append(Serialize(root.right)); return sb.toString(); } TreeNode Deserialize(String str) { index++; int len = str.length(); if(index >= len){ return null; } String[] strr = str.split(","); TreeNode node = null; if(!strr[index].equals("#")){ node = new TreeNode(Integer.valueOf(strr[index])); node.left = Deserialize(str); node.right = Deserialize(str); } return node; } } //面試題38:字串的排列 import java.util.List; import java.util.Collections; import java.util.ArrayList; public class Solution { public static void main(String[] args) { Solution p = new Solution(); System.out.println(p.Permutation("abc").toString()); } public ArrayList<String> Permutation(String str) { List<String> res = new ArrayList<>(); if (str != null && str.length() > 0) { PermutationHelper(str.toCharArray(), 0, res); Collections.sort(res); } return (ArrayList)res; } public void PermutationHelper(char[] cs, int i, List<String> list) { if (i == cs.length - 1) { String val = String.valueOf(cs); if (!list.contains(val)) list.add(val); } else { for (int j = i; j < cs.length; j++) { swap(cs, i, j); PermutationHelper(cs, i+1, list); swap(cs, i, j); } } } public void swap(char[] cs, int i, int j) { char temp = cs[i]; cs[i] = cs[j]; cs[j] = temp; } } //面試題39:陣列中出現次數超過一半的數字 public class Solution { public int MoreThanHalfNum_Solution(int [] array) { HashMap<Integer,Integer> map = new HashMap<Integer,Integer>(); for(int i=0;i<array.length;i++){ if(!map.containsKey(array[i])){ map.put(array[i],1); }else{ int count = map.get(array[i]); map.put(array[i],++count); } } Iterator iter = map.entrySet().iterator(); while(iter.hasNext()){ Map.Entry entry = (Map.Entry)iter.next(); Integer key =(Integer)entry.getKey(); Integer val = (Integer)entry.getValue(); if(val>array.length/2){ return key; } } return 0; } //面試題40:最小的k個數 //用最大堆儲存這k個數,每次只和堆頂比,如果比堆頂小,刪除堆頂,新數入堆。 import java.util.ArrayList; import java.util.PriorityQueue; import java.util.Comparator; public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) { ArrayList<Integer> result = new ArrayList<Integer>(); int length = input.length; if(k > length || k == 0){ return result; } PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2.compareTo(o1); } }); for (int i = 0; i < length; i++) { if (maxHeap.size() != k) { maxHeap.offer(input[i]); } else if (maxHeap.peek() > input[i]) { Integer temp = maxHeap.poll(); temp = null; maxHeap.offer(input[i]); } } for (Integer integer : maxHeap) { result.add(integer); } return result; } }
//面試題41:資料流中的中位數 import java.util.PriorityQueue; import java.util.Comparator; public class Solution { /***********方式一、用兩個優先佇列來模擬兩個堆---主要思路************************ 1.先用java集合PriorityQueue來設定一個小頂堆和大頂堆,大頂堆需要先重寫一下里面的比較器 2.主要的思想是:因為要求的是中位數,那麼這兩個堆,大頂堆用來存較小的數,從大到小排列; 小頂堆存較大的數,從小到大的順序排序, 顯然中位數就是大頂堆的根節點與小頂堆的根節點和的平均數。 保證:小頂堆中的元素都大於等於大頂堆中的元素,所以每次塞值,並不是直接塞進去,而是從另一個堆中poll出一個最大(最小)的塞值 3.當數目為偶數的時候,將這個值插入大頂堆中,再將大頂堆中根節點(即最大值)插入到小頂堆中; 當數目為奇數的時候,將這個值插入小頂堆中,再講小頂堆中根節點(即最小值)插入到大頂堆中; 這樣就可以保證,每次插入新值時,都保證小頂堆中值大於大頂堆中的值,並且都是有序的。 4.由於第一個數是插入到小頂堆中的,所以在最後取中位數的時候,若是奇數,就從小頂堆中取即可。 這樣,當count為奇數的時候,中位數就是小頂堆的根節點;當count為偶數的時候,中位數為大頂堆和小頂堆兩個根節點之和的平均數 5.例如,傳入的資料為:[5,2,3,4,1,6,7,0,8],那麼按照要求,輸出是"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 " a.那麼,第一個數為5,count=0,那麼存到小頂堆中, 步驟是:先存到大頂堆;然後彈出大頂堆root,就是最大值給小頂堆,第一次執行完,就是小頂堆為5,count+1=1; 此時若要輸出中位數,那麼就是5.0,因為直接返回的是小頂堆最小值(第一次塞入到小頂堆中,是從大頂堆中找到最大的給他的) b.繼續傳入一個數為2,那麼先存到小頂堆中,將小頂堆最小值彈出給大頂堆,即2,那麼這次執行完,小頂堆為5,大頂堆為2,count+1=2 此時若要輸出中位數,因為是偶數,那麼取兩個頭的平均值,即(5+2)/2=3.5(第二次塞入到大頂堆中,是從小頂堆中找到最小的給他的) c.繼續傳入一個數為3,那麼此時count為偶數,那麼執行第一個if,先存到大頂堆中,大頂堆彈出最大值,那麼3>2,就是彈出3 3存到小頂堆中,那麼此時小頂堆為3,5,大頂堆為2,count+1=3(第三次塞入到小頂堆中,是從大頂堆中找到最大的給他的) 此時若要輸出中位數,因為是奇數,那麼取小頂堆的最小值,即3.0 d.繼續傳入一個數為4,先存到小頂堆中,小頂堆此時為3,4,5,彈出最小值為3,給大頂堆 此時大頂堆為3,2,小頂堆為4,5,(第四次塞入到小頂堆中,是從大頂堆中找到最大的給他的) 此時若要輸出中位數,因為是偶數,那麼取兩個頭的平均值,即(3+4)/2=3.5 e.依次類推。。。 ******************************************/ /***************方式二、ArrayList*********************** 用ArrayList來存輸入的資料流,然後每次用Collections.sort(list)來保證資料流有序,然後再取中位數 思想非常簡單,但是每次都要進行排序,時間複雜度可想而知 ****************************************/ /***************方式三、插入排序,插入到對應的位置*********************** LinkedList<Integer> data = new LinkedList<Integer>(); public void Insert(Integer num) { for (int i = data.size() - 1; i >= 0 ; i--) { if (num >= data.get(i)){ data.add(i+1,num); return; } } data.addFirst(num); } ****************************************/ int count = 0; private PriorityQueue<Integer> minHeap = new PriorityQueue<>();//預設是小根堆 private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); public void Insert(Integer num) { if(count%2 == 0){ //數目為偶數時,插入到小根堆中 maxHeap.offer(num); int filteredMaxNum = maxHeap.poll(); minHeap.offer(filteredMaxNum); }else{ //數目為奇數時,插入到大根堆中 minHeap.offer(num); int filteredMinNum = minHeap.poll(); maxHeap.offer(filteredMinNum); } count++; } public Double GetMedian() { if (count %2 == 0) { return new Double((minHeap.peek() + maxHeap.peek())) / 2; } else { return new Double(minHeap.peek()); } } } //面試題42:連續子陣列的最大和 public class Solution { public int FindGreatestSumOfSubArray(int[] array) { //記錄當前所有子陣列的和的最大值 int res=array[0]; //包含array[i]的連續陣列最大值 int max=array[0]; for(int i=1;i<array.length;i++){ max=Math.max(max+array[i],array[i]); res=Math.max(max,res); } return res; } } //面試題43:1-n整數中1出現的次數(整數中1出現的次數) /* 設N = abcde ,其中abcde分別為十進位制中各位上的數字。 如果要計算百位上1出現的次數,它要受到3方面的影響:百位上的數字,百位以下(低位)的數字,百位以上(高位)的數字。 ① 如果百位上數字為0,百位上可能出現1的次數由更高位決定。比如:12013,則可以知道百位出現1的情況可能是:100~199,1100~1199,2100~2199,,...,11100~11199,一共1200個。可以看出是由更高位數字(12)決定,並且等於更高位數字(12)乘以 當前位數(100)。 ② 如果百位上數字為1,百位上可能出現1的次數不僅受更高位影響還受低位影響。比如:12113,則可以知道百位受高位影響出現的情況是:100~199,1100~1199,2100~2199,,....,11100~11199,一共1200個。和上面情況一樣,並且等於更高位數字(12)乘以 當前位數(100)。但同時它還受低位影響,百位出現1的情況是:12100~12113,一共114個,等於低位數字(113)+1。 ③ 如果百位上數字大於1(2~9),則百位上出現1的情況僅由更高位決定,比如12213,則百位出現1的情況是:100~199,1100~1199,2100~2199,...,11100~11199,12100~12199,一共有1300個,並且等於更高位數字+1(12+1)乘以當前位數(100)。 */ public class Solution { public int NumberOf1Between1AndN_Solution(int n) { int count = 0;//1的個數