【劍指offer】1~10題

第一題:

在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

????這是非常簡單的一道題,最常規的一種方式就是兩個方向上掃描,但是要保證其中一個掃描方向數字是遞增的,而另外一個方向上數字是遞減的。
????下面給出我的解法,從矩陣中的右上角開始,從右往左掃,當橫向當前數的下一個數小于目標數的時候往下少,找到返回true,越界返回false。

public class Solution {
    public boolean Find(int target, int [][] array) {
        for(int i = array[0].length-1; i >= 0; i--) {
            if(array[0][i] <= target) {
                for(int j = 0; j < array.length; j++) {
                    if(array[j][i] == target) return true;
                }
            }
        }
        return false;
    }
}

第二題

請實現一個函數,將一個字符串中的空格替換成“%20”。例如,當字符串為We Are Happy.則經過替換之后的字符串為We%20Are%20Happy。

????這題不解釋了,太簡單,很多編程語言直接用字符串處理函數就能做了,用正則也能做,當然,題目本身可能并不希望你這么干。

public class Solution {
    public String replaceSpace(StringBuffer str) {
        char[] arr = str.toString().toCharArray();
        int len = arr.length;
        int blankCount = 0;
        for(int i = 0; i < len; i++) {
            if(arr[i] == ' ')
                blankCount++;
        }
         
        int[] res = new int[len+blankCount];
        int j = 0;
        for(int i = 0; i < len; i++) {
            if(arr[i] == ' ') {
                res[j++] = '%';
                res[j++] = '2';
                res[j++] = '0';
            } else {
                arr[j++] = arr[i];
            }
        }
         
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < res.length; i++) {
            sb.append(res[i]);
        }
        return sb.toString();
    }
}

第三題

輸入一個鏈表,從尾到頭打印鏈表每個節點的值。

????最簡單的思路就是用棧倒序輸出。

public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        Stack<Integer> stack = new Stack<>();
        while(listNode != null) {
            stack.push(listNode.val);
            listNode = listNode.next;
        }
        while(!stack.isEmpty()) {
            list.add(stack.pop());
        }
        return list;
    }
}

第四題

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹并返回。

????總算來了個比較有意思的題目。給你前序和中序,讓你重構二叉樹返回。我們直接按題目中給的例子來講。
????前序遍歷是{1,2,4,7,3,5,6,8},也就是說 {1}這個數字就是根。而中序遍歷是{4,7,2,1,5,3,8,6},從而可以劃出兩個子集{4,7,2,1} {5,3,8,6}分別為組成以{1}為根的左右子樹。
????依照這個邏輯下去,就可以得出整棵樹的結構了。這顯然就是一些重復步驟,用遞歸去做是最好的方式。再進一步思考,我們只需要知道前序和中序遍歷的邊界下標,就能在遞歸過程中計算出我們要得到的數據。接下來,讓我們直接看代碼。

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        return rebuilder(pre, in, 0, pre.length-1, 0, in.length-1);
    }
    
    public TreeNode rebuilder(int[] pre, int[] in, int prel, int prer, int inl, int inr) {
        if(prel > prer) return null;
        TreeNode node = new TreeNode(pre[prel]);
        for(int i = inl; i <= inr; i++) {
            if(in[i] == pre[prel]) {
                node.left = rebuilder(pre, in, prel+1, prel+i-inl, inl, i-1);
                node.right = rebuilder(pre, in, prel+i-inl+1, prer, i+1, inr);
                break;
            }
        }
        return node;
    }
}

第五題

用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素為int類型。

????也是非常簡單的一題。要用兩個棧模擬隊列,我們只需要保持兩個原則。第一,當你要push一個元素到其中一個棧時候,另外一個棧必須為空。第二,當你要pop一個元素的時候,必須把棧中的所有元素傾倒到另外一個空棧,然后再pop數據出去。

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        while(!stack2.isEmpty()) {
            stack1.push(stack2.pop());
        }
        stack1.push(node);
    }
    
    public int pop() {
        while(!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }
}

第六題

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。 NOTE:給出的所有元素都大于0,若數組大小為0,請返回0。

????咋看之下,這題似乎掃描一遍數組就行了。沒錯,確實如此,但是題目本身是希望你用二分去做的,不然給你非遞減這個特性就沒有意義了。題目說,非遞減數列做旋轉,旋轉之后其實主要有兩種情況,一種就是旋轉后和旋轉前一樣,一種是旋轉后和選擇前發生了變化(這里暫時忽略數列中過的數全部相等的情況),第一種情況,數列的最后一個數一定是大于第一個數的。第二種情況,最小的數一定在數列中。而這個數列也會分成兩個遞增的子數列,且兩個子序列相連的邊界數字一定是遞減的,比如:{3,4,5} {1, 2}兩個遞增數列,5和1這兩個邊界是下降的,從而我們可以得到二分的條件,假設:當前位置為i,那么當a[i-1] > a[i]時, i就是最小的數。

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        return binarySearch(array, 0, array.length-1);
    }
    /**
          大體上就是一個二分搜索,注意搜索條件即可
    */
    public int binarySearch(int[] arr, int l, int r) {
        
        if (arr[0] < arr[arr.length - 1])
            return arr[0];

        int mid = (l + r) / 2;
        // 這一句就是為了覆蓋數列數字全部相等的情況
        if(mid == r) return arr[r];
        if (arr[mid] < arr[mid - 1])
            return arr[mid];
        
        int res = 0;
        if(l <= r) {
            if (arr[mid] < arr[l])
                res = binarySearch(arr, l, mid);
            else
                res = binarySearch(arr, mid, r);
        }
        return res;
    }
}

第七題

大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。
n<=39
????這題沒啥好講的,為了避免爆棧,應該采用迭代的方式。

public class Solution {
    public int Fibonacci(int n) {
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
         
        int count = 1;
        Queue<Integer> queue = new LinkedList<Integer>();
        queue.add(0);
        queue.add(1);
        int res = -1;
        while(count != n) {
            res = queue.poll() + queue.peek();
            queue.add(res);
            count++;
        }
        return res;
    }
}

第八題

一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

????這題考察的就是一個遞推的是個思想。假設i表示青蛙要跳上的臺階,當i==1時,只有一種方法;當i==2時,可以從上一個臺階,或上上一個臺階跳上去,所有有兩種方式,從而可以知道,當i > 1時,res = step1 + step2(這里的step1和step2分別表示跳到上一個臺階和跳到上上一個臺階的跳法)。

public class Solution {
    public int JumpFloor(int target) {
        if(target == 1)
            return 1;
        if(target == 2)
            return 2;
        int result = 0;
        int step1 = 1, step2 = 1;
        target -= 1;
        while(target-- != 0) {
            result = step1 + step2;
            step1 = step2;
            step2 = result;
        }
        return result;
    }
}

第九題

一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

????這道題,最簡單最快速的方法,就是直接用排列組合去做。把n個臺階分成1部分、2部分、3...n部分,c(N,0)+c(N,1)+...+C(N,N) = 2^N。

public class Solution {
    public int JumpFloorII(int target) {
        return (int)Math.pow(2, target-1);
    }
}

第十題

我們可以用21的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個21的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

????這道題的思路其實和第八題非常類似,假設n=5,我們可以看成n=3和n=4兩種情況,但是注意:n=3時,剩下2個小矩陣,所以還可以排成2種,但是有其中一種(豎著排列的)會與n=3時候的重復,所以其實也只能排一種。所以我們可以知道:當 n==1時 res = 1;當n==2時,res=2,當n>2時,res = cnt[n-1] + cnt[n-2]。

public class Solution {
    public int RectCover(int target) {
        if(target == 0)
            return 0;
            
        if(target == 1)
            return 1;
        
        if(target == 2)
            return 2;
        
        return RectCover(target-1) + RectCover(target-2);
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。