1. 程式人生 > >劍指offer所有題目java版

劍指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的個數