1. 程式人生 > >algorithm 題集八 (18.03.25)

algorithm 題集八 (18.03.25)

本文一共12題,均來自《劍指offer》,不復雜。在平時的練習中收集了部分組合而成。

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

(2)用兩個棧來實現一個佇列,完成佇列的Push和Pop操作。佇列中的元素為int型別。

(3)用兩個佇列實現一個棧。完成棧的Push,Pop,top等操作。

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

(5)大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。n<=39。(斐波那契數列:1、1、2、3、5、8、13、21、34、…… 在數學上,斐波納契數列以如下被以遞迴的方法定義:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2))

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

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

(8)連結串列反轉。

(9)輸入兩個單調遞增的連結串列,輸出兩個連結串列合成後的連結串列,當然我們需要合成後的連結串列滿足單調不減規則。

(10)輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

(11)輸入一個矩陣,按照從外向裡以順時針的順序依次打印出每一個數字,例如,如果輸入如下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 則依次打印出數字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

(12)定義棧的資料結構,請在該型別中實現一個能夠得到棧最小元素的min函式。


思路:
(1)遞迴查詢頂點。
(2)一個棧專門負責進,另一個棧專門負責出。
(3)一個佇列負責主儲存,另一個佇列負責臨時儲存(用於輔助實現pop操作)。
(4)第一種思路:暴力,直接從後面向前遍歷,直到找到最小值。第二種思路:由於原始陣列是非遞減陣列,那麼旋轉後的陣列後面第二部分的任意一個元素一定是小於等於前面任何一個元素。然後,我們可以使用二分不斷縮小比較的空間。
思路二的大致過程:
先確定left = 0, right = size -1。二分得到mid = (left+right)>>1
如果array[mid] > array[left], 那麼array[mid]屬於第一部分,left = mid
如果array[mid] < array[left],那麼array[mid]屬於第二部分,right = mid
如果array[mid] == array[left],那麼有可能是3 1 3 3 3,也有可能是 3 3 3 1 3。兩種情況均滿足條件。故無法判斷,直接暴力搜尋。
(5)個人覺得,最快的方法是在建構函式中打表,然後解答函式直接輸出array[n]。
(6)動態規劃問題,當青蛙跳到第n級,可供選擇的路徑共有F(n-1) + F[n-2]。所以遞推式就是菲波那切數列。
(7)尋找規律:

f[1] = 1
f[2] = f[1]+1 = 2
f[3] = f[1]+f[2]+1 = 4
f[4] = f[1]+f[2]+f[3]+1 = 8
f[5] = f[1]+f[2]+f[3]+f[4]+1 = 16
==> f[n] = 2^(n-1)

(8)放兩個指標記錄遍歷指標的前節點和後節點。然後改變遍歷指標的next指向。一趟遍歷完整就完成了任務。

    ListNode* ReverseList(ListNode* pHead) {
        ListNode* next = NULL;
        ListNode* pre = NULL;
        while(pHead){
            next = pHead->next;
            pHead->next = pre;
            pre = pHead;
            pHead = next;
        }
        return pre;
    }

(9)將麻煩事交給計算機,我們指出關鍵步驟即可。遞迴無敵!

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL) return pHead2;
        else if(pHead2 == NULL) return pHead1;
        else {
            if(pHead1->val <= pHead2->val){
                pHead1->next = Merge(pHead1->next,pHead2);
                return pHead1;
            }
            else {
                pHead2->next = Merge(pHead1,pHead2->next);
                return pHead2;
            }
        }
    }
};

(10)首先需要確定滿足“相同(子結構)”
然後左右子樹遞迴查詢。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool isSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
        if(pRoot2 == NULL) return true;
        if(pRoot1 == NULL) return false;
        if(pRoot1->val == pRoot2->val){
            return isSubtree(pRoot1->left, pRoot2->left) && isSubtree(pRoot1->right, pRoot2->right);
        }
        else  return false;
    }
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL || pRoot2 == NULL) return false;
        return isSubtree(pRoot1, pRoot2) || \
                HasSubtree(pRoot1->left,pRoot2) || \
                HasSubtree(pRoot1->right,pRoot2); 
    }
};

(11)我的方案:找到需要列印的圈數和左上頂點,列印外圍數字。這樣由外向內迴圈下去。
然後,看到其他網友的解法。有一個非常的巧妙:列印第一行,然後刪除此行,接著逆時針旋轉90度構造新的矩形。重新這個步驟。

(12)新增一個輔助棧,用於每次push元素壓入新的最小元素,每次pop元素彈出棧頂元素,min函式直接查詢輔助棧的棧頂元素即可。
當然,我沒想那麼多(呵呵),直接去暴力實現了。

class Solution {
    int *data;
    unsigned int length;
    unsigned int size;

public:
    Solution(){
        length = 0;
        size = 0;
        data = NULL;
    }
    ~Solution(){
        length = 0;
        size = 0;
        delete data;
        data = NULL;
    }
    unsigned int _align_it(unsigned int newSize){
        return newSize+(sizeof(int)-1) & ~(sizeof(int)-1);
    }
    unsigned int recommend(unsigned int newSize){
        return std::max(2*length, _align_it(newSize));
    }
    void reserve(unsigned int size){
        if(size > length) {
            int old_length = length;
            int *old_data = data;
            length = recommend(size);
            data = new int[length];
            for(int i=0; i<old_length; i++){
                data[i] = old_data[i];
            }
            delete old_data;
        }
    }
    void push(int value) {
        reserve(++size);
        data[size-1] = value;
    }
    void pop() {
        if(size == 0){
            return ;
        }
        size--;
    }
    int top() {
        try{
            if(size == 0) throw "there is no number in data.";
            return data[size-1];
        }
        catch(char *str){
            cout<<str<<endl;
            return -1;
        }
    }
    int min() {
        try{
            if(length == 0) throw "length is equal to zero";
            int minVal = data[0];
            for(int i=1;i<size;i++){
                minVal = minVal<data[i]?minVal:data[i];
            }
            return minVal;
        }
        catch(char *str){
            cout<<str<<endl;
            return -1;
        }
    }
};