1. 程式人生 > >在牛客網刷劍指offer

在牛客網刷劍指offer

重建二叉樹

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

分析:

前序遍歷序列的第一個數字是根節點,在中序遍歷序列中找到這個數字的位置,那麼中序遍歷序列中在這個數字左邊的序列是左子樹結點,右邊的是右子樹結點。而在前序遍歷序列中從第二個數字開始有一段連續的序列是左子樹結點,長度與在中序遍歷中的一樣。既然知道了左、右子樹的前、中序遍歷,那麼通過遞迴實現即可,函式返回根節點。

code:

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;	
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    struct TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in) {
		if(pre.size() <= 0) return NULL;
        TreeNode* T = new TreeNode(pre[0]);
        vector<int> prel, prer, inl, inr;
        int index = 0;
        for(int i=0; i<in.size(); i++) {
            if(in[i] == pre[0]) {
                index = i; break;
            }
        }
        for(int i=0; i<in.size(); i++) if(in[i] != pre[0]) {
            if(i < index) inl.push_back(in[i]);
            else inr.push_back(in[i]);
        }
        for(int i=1; i<pre.size(); i++) {
            if(i <= index) prel.push_back(pre[i]);
            else prer.push_back(pre[i]);
        }
        T->left = reConstructBinaryTree(prel,inl);
        T->right = reConstructBinaryTree(prer,inr);
        return T;
    }
};

用兩個棧實現佇列

題目描述
用兩個棧來實現一個佇列,完成佇列的Push和Pop操作。 佇列中的元素為int型別。

解析

push操作:將數字放進stack1裡,pop操作:彈出stack2.top元素即可,假如stack2為空,操作之前應將stack1所有元素內容放到stack2中。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty()) {
            while(!stack1.empty()) {
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int value = stack2.top();
        stack2.pop();
        return value;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

旋轉陣列的最小數字

題目描述:

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

解析:

這個二分挺好的,我寫錯了好多次,要千萬注意二分的條件以及不同的情況下這個條件是否成立。

code:

class Solution {
public:
    int minNumberInRotateArray(vector<int> v) {
        int l = 0, r = v.size()-1, mid; 
        while(l < r) {
            mid = (l+r)/2;
            if(v[mid] > v[r]) l = mid+1; //這裡一開始我寫的是if(v[l] < v[mid])。當最小值在頂端時出錯
            else r = mid;
        }
        return v[l];
    }
};

樹的子結構

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

解析:

直接遍歷遞迴,時間複雜度為O(n*m)。有人說將它們先序遍歷變成字串,然後看b是否是a的子串,用kmp演算法可達到時間複雜度為O(n+m)。但我覺得這樣不行。舉個例子:A是一個深度為2的二叉樹,共3個結點,父結點值為1,左兒子結點值為2,右兒子結點值為3。B也是一個深度為2的二叉樹,共2個結點,父結點值為1,右兒子結點為3。B是A的子樹,但A的前序遍歷為{1,2,3},而B的前序遍歷為{1,3},後面的根本不是前面的子串,故不成立。不知道此題是否有更好的解法,還請各位大牛告知。

code:

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
    bool isSame(TreeNode* p1, TreeNode* p2) {
        if(p2 == NULL) return true; //P2為空表示子樹這條線路已經走完了
        if(p1 == NULL) return false; //當子樹的這條線路還沒走完你A樹的卻走完了肯定不行
        if(p1->val == p2->val) {
            if(isSame(p1->left,p2->left) && isSame(p1->right,p2->right)) {
                return true;
            }
        }
        return false;
    }
public:
    bool HasSubtree(TreeNode* p1, TreeNode* p2)
    {
        if(p1 == NULL || p2 == NULL) return false;
		if(isSame(p1,p2) || HasSubtree(p1->left,p2) || HasSubtree(p1->right,p2)) return true;
        return false;
    }
};

順時針列印矩陣

題目描述
輸入一個矩陣,按照從外向裡以順時針的順序依次打印出每一個數字,例如,如果輸入如下矩陣: 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.

分析

分四個方向走,走到不能走的地方90度轉彎,遍歷過的位置進行標記。其實用dfs挺好寫的。

code:

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
		vector<int> v;
        int x = 0, y = 0, t = 0, n = matrix.size(), m = matrix[0].size();
        while(t < n*m) {
            while(t < n*m && y < m && matrix[x][y] != -1) {v.push_back(matrix[x][y]); matrix[x][y++] = -1; t++;} y--, x++;
            while(t < n*m && x < n && matrix[x][y] != -1) {v.push_back(matrix[x][y]); matrix[x++][y] = -1; t++;} x--, y--;
            while(t < n*m && y >= 0 && matrix[x][y] != -1) {v.push_back(matrix[x][y]); matrix[x][y--] = -1; t++;} y++, x--;
            while(t < n*m && x >= 0 && matrix[x][y] != -1) {v.push_back(matrix[x][y]); matrix[x--][y] = -1; t++;} x++, y++;
        }
        return v;
    }
};

棧的壓入、彈出序列

題目描述
輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)

分析

模擬一下入棧出棧,其實就是兩種情況,某個元素出棧後,那麼下一次出棧的要麼是接下來的棧頂,要麼是還未進棧的元素

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        stack<int> s1;
        int index = 0;
        for(int i=0; i<pushV.size(); i++) {
            s1.push(pushV[i]);
            while(!s1.empty() && s1.top() == popV[index]) {
                s1.pop(); index++;
            }
        }
        return s1.empty();
    }
};