劍指offer 題目詳解

面試題3:二維數組中的查找

題目: 一個二維數組,每一行按照從左到右遞增,每一列按照從上到下遞增,查找數組中是否存在某個數。如數組:java

1  2  8    9node

2  4  9   12c++

4  7  10  13面試

6  8  11  15算法

 

思路:這道題有其特殊性,從右上角或者左下角開始查找。好比是查找7,咱們從右上角開始,9大於7,則排除最右邊一列,減小列下標,查找13的話就增長行下標,減小一行,查找的方向是肯定的,這樣就容易實現了。數組

 

    public static void main(String args[]) {  
        // 測試用的例子  
        int a[][] = { { 1, 2, 8, 9 }, { 2, 4, 9, 12 }, { 4, 7, 10, 13 },  
                { 6, 8, 11, 15 } };  
        System.out.println(find(a, 7));  
    }  
    public static boolean find(int array[][], int number) {
        int row=array.length;
        int col=array[0].length;
        int start=0;
        int end=col-1;
        while(start<row&&end>=0) {
            if(array[start][end]==number) {
                return true;
            }else if(array[start][end]<number){
                start++;
            }else {
                end--;
            }
            
        }
        return false;
    }

 

二、面試題4:替換空格

 

題目大體爲:app

    實現一個函數,把字符串中的每一個空格替換成"%20"。函數

思路:這道題其實考察的是替換成新的值後,字符串變長會覆蓋後面原有的值。可是c++的字符串和java是不同的,c++是用數組生成的字符串,因此會覆蓋後面的值。所以本題咱們首先實現java下的要求,並增長一例,數組狀況下的替換方法。測試

首先是字符串的形式:this

    public static void main(String args[]) {  
        String s = "We are happy.";
        String a=replace(s);
        System.out.println(a);
    }  
    public static String replace(String s) {
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<s.length();i++) {
            if(s.charAt(i)==' ') {
                sb.append("%20");
            }else {
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }

 而後是數組形式:

    public static void main(String args[]) {  
        String s = "We are  happy.  ";
        char[] a=s.toCharArray();
        System.out.println(replace1(a));
    }  
    public static char[] replace1(char[] a) {
        int sum=0;
        for(int i=0;i<a.length;i++) {
            if(a[i]==' ') {
                sum++;
            }
        }
        char[] newLength=new char[a.length+sum*2];
        int index=newLength.length-1;
        for(int i=a.length-1;i>=0;i--) {
            if(a[i]==' ') {
                newLength[index--]='0';
                newLength[index--]='2';
                newLength[index--]='%';
            }else {
                newLength[index--]=a[i];
            }
        }
        return newLength;
    }

3.面試題5:從尾到頭打印鏈表

題目: 輸入一個鏈表的頭結點,從尾到頭反過來打印每一個結點的值。

用一個棧去存儲當前節點的值,順序遍歷下去,最後從棧中pop中便可。

此處給出兩種解法,一種遞歸,一種迭代。

    public static void main(String args[]) {  
            ListNode head=new ListNode(0);
        ListNode one=new ListNode(1);
        ListNode two=new ListNode(2);
        ListNode three=new ListNode(3);
        ListNode four=new ListNode(4);
        ListNode five=new ListNode(5);
        head.setNext(one);
        one.setNext(two);
        two.setNext(three);
        three.setNext(four);
        four.setNext(five);
        five.setNext(null);
        printListReverse_1(head);
        System.out.println();
        printListReverse_2(head);
        System.out.println();
    }
    public static void printListReverse_1(ListNode head) {
        Stack<ListNode> stack=new Stack<>();
        while(head!=null) {
            stack.push(head);
            head=head.getNext();
        }
        while(!stack.isEmpty()) {
            System.out.print(stack.pop().getValue()+ " ");
        }
        
    }
    public static void printListReverse_2(ListNode head) {
        if(head!=null) {
            if(head.getNext()!=null) {
                printListReverse_2(head.getNext());
            }
            System.out.print(head.getValue()+", ");
        }
    }

 

四、面試題6:重建二叉樹

題目:已知前序遍歷序列和中序遍歷序列,要求重建二叉樹

思路:(leetcode中相似)

假設樹的先序遍歷是12453687,中序遍歷是42516837。

這裏最重要的一點就是先序遍歷能夠提供根的所在,而根據中序遍歷的性質知道根的所在就能夠將序列分爲左右子樹。

好比上述例子,咱們知道1是根,因此根據中序遍歷的結果425是左子樹,而6837就是右子樹。

接下來根據切出來的左右子樹的長度又能夠在先序便利中肯定左右子樹對應的子序列(先序遍歷也是先左子樹後右子樹)。

根據這個流程,左子樹的先序遍歷和中序遍歷分別是245和425,右子樹的先序遍歷和中序遍歷則是3687和6837,咱們重複以上方法,能夠繼續找到根和左右子樹,直到剩下一個元素。

能夠看出這是一個比較明顯的遞歸過程,對於尋找根所對應的下標,咱們能夠先創建一個HashMap,以避免後面須要進行線行搜索,這樣每次遞歸中就只須要常量操做就能夠完成對根的肯定和左右子樹的分割。

public class BinaryTreeNode {  
    private int value;  
    private BinaryTreeNode left;  
    private BinaryTreeNode right;  
  
    public BinaryTreeNode(int value) {  
        this.value = value;  
    }  
  
    public BinaryTreeNode(int value, BinaryTreeNode left, BinaryTreeNode right) {  
        this.value = value;  
        this.left = left;  
        this.right = right;  
    }  
  
    public int getValue() {  
        return value;  
    }  
  
    public void setValue(int value) {  
        this.value = value;  
    }  
  
    public BinaryTreeNode getLeft() {  
        return left;  
    }  
  
    public void setLeft(BinaryTreeNode left) {  
        this.left = left;  
    }  
  
    public BinaryTreeNode getRight() {  
        return right;  
    }  
  
    public void setRight(BinaryTreeNode right) {  
        this.right = right;  
    } 
    
}
public class Item {
    public static void main(String args[]) {  
        int preOrder[] = { 1, 2, 4, 5, 3, 6, 8, 7 };  
        int inOrder[] = { 4,2,5,1,6,8,3,7 };  
        Map<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<inOrder.length;i++) {
                map.put(inOrder[i], i);
        }
        BinaryTreeNode root = constructTree(preOrder, 0,preOrder.length-1,inOrder,0,inOrder.length-1,map);  
    }

    private static BinaryTreeNode constructTree(int[] preOrder, int preStart,int preEnd,int[] inOrder,int inStart,int inEnd,Map<Integer,Integer> map) {
        if(preStart>preEnd||inStart>inEnd) {
            return null;
        }
        BinaryTreeNode root=new BinaryTreeNode(preOrder[preStart]);
        int index=map.get(root.getValue());
        root.setLeft(constructTree(preOrder,preStart+1,index-inStart+preStart,inOrder,inStart,index-1,map));
        root.setRight(constructTree(preOrder,index-inStart+preStart+1,preEnd,inOrder,index+1,inEnd,map));
        return root;
    }
    
}  

 

面試題7:用兩個棧實現隊列

題目:用兩個棧實現隊列的兩個函數appendTail和deleteHead。

思路:棧的特性是:後進先出,而隊列的特性是:先進先出。這裏使用兩個棧實現隊列有點負負得正的意思。棧1負責添加,而棧2負責刪除。

package test;

import java.util.Stack;

//基於鏈表實現的下壓堆棧


public class SQueue<T> {  
    private Stack<T> stack1;
    private Stack<T> stack2;
    public SQueue () {
        this.stack1=new Stack<>();
        this.stack2=new Stack<>();
    }
    public void appendTail(T node) {
        stack1.push(node);
    }
    public T deleteHead() {
        if(stack2.isEmpty()) {
            if(stack1.isEmpty()) {
                try{
                    throw new Exception("隊列爲空");
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }else {
                while(!stack1.isEmpty()) {
                    stack2.push(stack1.pop());
                }
                
            }
        }
        return stack2.pop();
    }
}
  

        
        
public class Item {
    public static void main(String args[]) {  
        SQueue<Integer> sq=new SQueue<>();
        for(int i=0;i<5;i++) {
            sq.appendTail(i);
        }
        for (int i = 0; i < 5; i++) {  
            System.out.print(sq.deleteHead() + "、");  
        }  
        System.out.println();  
    }
    
}  

 

面試題8:旋轉數組的最小數字

題目:一個遞增排序的數組的一個旋轉(把一個數組最開始的若干元素搬到數組的末尾,稱之爲數組的旋轉),輸出旋轉數組的最小元素。

思路:不論怎麼旋轉總有一半是排序好的。咱們就找排序好的那一半,二分解決。同時考慮重複的狀況。

假設原數組是{1,2,3,3,3,3,3},那麼旋轉以後有多是{3,3,3,3,3,1,2},或者{3,1,2,3,3,3,3},

這樣的咱們判斷左邊緣和中心的時候都是3,咱們並不知道應該截掉哪一半。

解決的辦法只能是對邊緣移動一步,直到邊緣和中間不在相等或者相遇,這就致使了會有不能切去一半的可能。

因此最壞狀況就會出現每次移動一步,總共移動n此,算法的時間複雜度變成O(n)。

    public static void main(String args[]) {  
            int a[] = {3,1,2,3,3,3,3};
        System.out.println(findMin(a));  
    }
    public static int findMin(int[] nums) {
        int left=0;
        int right=nums.length-1;
        while(left<right) {
            int mid=left+(right-left)/2;
            if(nums[mid]>nums[right]) {
                left=mid+1;
            }else if(nums[mid]<nums[left]) {
                right=mid;
            }else {
                right--;
            }
        }
        return nums[left];
    }      

 

面試題9:斐波那契數列

思路:用遞歸實現的過程當中會出現重複計算的狀況,此時,能夠利用動態規劃的思想,保存中間結果,這樣能夠避免沒必要要的計算。

public class Item09 {  
    public static void main(String args[]) {  
        int n = 3;  
        System.out.println(fibonacci(n));  
    }  
  
    public static int fibonacci(int n) {  
        if (n == 0) {  
            return 0;  
        } else if (n == 1) {  
            return 1;  
        } else {  
            //由zero和one保存中間結果  
            int zero = 0;  
            int one = 1;  
            int fN = 0;  
            for (int i = 2; i <= n; i++) {  
                fN = one + zero;  
                zero = one;  
                one = fN;  
            }  
            return fN;  
        }  
    }  
}  

面試題10:二進制中1的個數

題目:實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。
思路:先分析把一個數減去1的狀況,若是該二進制數最右邊一位爲1,那麼減去1爲0,也就是最後一位至關於作了取反操做。
接着假設該二進制數最後一位不爲1,而是0,若是最右邊的1位於第m位,那麼減去1時,第m位由1變成0,而第m位以後的全部0都變成1,整數中第m位以前的全部位都保持不變。。舉個例子:一個二進制數1100,減1後變成1011。
綜合上面兩種狀況,把一個整數減去1,都是把最右邊的1變成0,若是他的右邊還有0的話,全部的0都變成1,而它左邊的全部位保持不變。因此思路就是把一個整數減去1,再與原整數作與運算,就會把最右邊的第一個1變成0,那麼一個整數的二進制中有多少個1,就能夠進行多少次這樣的操做。
public class Item10 {  
    public static void main(String args[]) {  
  
        int n = 9;  
        System.out.println("9的二進制表示中1的個數爲:" + numberOf1(n));  
  
    }  
      
    /** 
     * 利用了與的操做 
     * @param n 
     * @return 
     */  
    public static int numberOf1(int n) {  
        int count = 0;  
  
        while (n != 0) {  
            count++;  
            n = (n - 1) & n;  
        }  
  
        return count;  
    }  
  
}  

還有一些相關的題目:

好比用一條語句判斷一個數是否是2的整數次方。一個整數若是是2的整數次方,那麼它的二進制表示中有且只有一位是1,而其餘全部位都是0。根據以前的分析,咱們只要判斷該數減一後,再跟以前的數進行與運算獲得的結果是否爲0,便可。

 

面試題11:數值的整數次方

 
題目: 實現函數double power(double base, int exponent),求base的exponent次方。不得使用庫函數,同時不須要考慮大數問題。

思路:須要考慮到 exponent爲負數或者爲0的狀況,因此用傳統的for循環就不行了,所以咱們提出以下解法,考慮對指數折半,這樣只須要計算一半的值,若指數是奇數,則-1再折半,不然直接折半。

public class Item11 {  
    public static void main(String args[]) {  
        int base = 2;  
        int exponent_1 = 9;  
        int exponent_2 = 10;  
        System.out.println("當exponent爲奇數:" + power(base, exponent_1));  
        System.out.println("當exponent爲偶數:" + power(base, exponent_2));  
    }  
  
    /** 
     * 整數次方 
     *  
     * @param base底 
     * @param exponent指數 
     * @return 
     */  
    public static double power(double base, int exponent) {  
        if (exponent == 0) {  
            return 1;  
        }  
  
        if (exponent == 1) {  
            return base;  
        }  
        if (exponent >> 1 == 0) {// 偶數  
            int exponent_1 = exponent >> 1;  
            double tmp = power(base, exponent_1);  
            return tmp * tmp;  
        } else {// 非偶數  
            int exponent_2 = exponent - 1;// -1後就是偶數  
            double tmp = power(base, exponent_2);  
            return tmp * base;// 最後還有先開始減掉的一個base  
        }  
    }  
}  
相關文章
相關標籤/搜索