劍指offer【11-13】

面試題11. 旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組 [3,4,5,1,2] 爲 [1,2,3,4,5] 的一個旋轉,該數組的最小值爲1。

示例 1:
輸入:[3,4,5,1,2]
輸出:1
示例 2:
輸入:[2,2,2,0,1]
輸出:0

排序數組的查找問題首先考慮使用二分法解決,將遍歷的線性級別時間複雜度降低至 對數級別

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int n = numbers.size();
        int left = 0, right = n - 1,mid;
        while(left < right){
            mid = (left + right)>>1;
            //這裏用除2超出了時間限制了,隨後想到除2相當於二進制右移一位,比除法更快【乘2也如此】
            if(numbers[mid] > numbers[right]) left = mid + 1;
            else if(numbers[mid] == numbers[right]) right --;
            else right = mid;
        }
        return numbers[left];
    }
};

當然也可以排序後直接返回num[0]

    int minArray(vector<int>& numbers) {
        sort(numbers.begin(),numbers.end());
        return numbers[0];
    }
面試題12. 矩陣中的路徑

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一格開始,每一步可以在矩陣中向左、右、上、下移動一格。如果一條路徑經過了矩陣的某一格,那麼該路徑不能再次進入該格子。例如,在下面的3×4的矩陣中包含一條字符串“bfce”的路徑(路徑中的字母用加粗標出)。

[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]

但矩陣中不包含字符串“abfb”的路徑,因爲字符串的第一個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入這個格子。

示例 1:
輸入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
輸出:true
示例 2:
輸入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
輸出:false
提示:
1 <= board.length <= 200
1 <= board[i].length <= 200

可以用回溯法【DFS】
由於遞歸特性,路徑可以被看成一個棧。當在矩陣中定位了路徑中前n個字符的位置之後,在與第n個字符對應的格子的周圍都沒有找到第n+1個字符,這時候只好在路徑上回到第n-1個字符,重新定位第n個字符。

class Solution {
public:
		int rows,cols;
        bool exist(vector<vector<char>>& board, string word) {
        rows = board.size();
        cols = board[0].size();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == word[0]) {
                    if (dfs(board,word,i,j,1)) 
                        return true;
                }
            }
        }
        return false;
    }
        int x[4] = {0,0,-1,1};
        int y[4] = {-1,1,0,0};
        //進行上下左右移動,如下移:x+x下標,y+y下標.對於二維數組中的移動都可以這麼做

        bool dfs(vector<vector<char>>& board,string word,int i,int j,int num) {
        if (num == word.size()) 
            return true;
        char tmp = board[i][j]; 
        board[i][j] = '.'; //標記被遍歷過了
        for (int k = 0; k < 4; k++) {
            int d_x = x[k] + i; 
            int d_y = y[k] + j;
            if (d_x>=0&&d_x<rows&&d_y>=0&&d_y<cols&&word[num]==board[d_x][d_y]) {
                if (dfs(board,word,d_x,d_y,num + 1)) 
                    return true;
            }
        }
        board[i][j] = tmp;//如果周圍字符不匹配則還原被標記字符
        return false;
    }
};
面試題13. 機器人的運動範圍

地上有一個m行n列的方格,從座標 [0,0] 到座標 [m-1,n-1] 。一個機器人從座標 [0, 0] 的格子開始移動,它每次可以向左、右、上、下移動一格(不能移動到方格外),也不能進入行座標和列座標的數位之和大於k的格子。
例如,當k爲18時,機器人能夠進入方格 [35, 37] ,因爲3+5+3+7=18。但它不能進入方格 [35, 38],因爲3+5+3+8=19。請問該機器人能夠到達多少個格子?

示例 1:
輸入:m = 2, n = 3, k = 1
輸出:3
示例 2:
輸入:m = 3, n = 1, k = 0
輸出:1
提示:
1 <= n,m <= 100
0 <= k <= 20

廣度優先解法:

class Solution {
public:
    int movingCount(int m, int n, int k) {
        vector<vector<int>> visited(m, vector<int>(n, 0));
        queue<pair<int, int>> q;
        q.push({0, 0});
        int res = 0;
        visited[0][0] = 1;
        while (!q.empty()) {
            auto front = q.front(); q.pop();
            int x = front.first;
            int y = front.second;
            res += 1;
            for (auto d : directions) {
                int new_x = x + d.first;
                int new_y = y + d.second;
                if (new_x < 0 || new_x >= m || new_y < 0 || new_y >= n 
                    || visited[new_x][new_y] == 1 ||
                    sumDigit(new_x, new_y) > k) {
                    continue;
                }
                q.push({new_x, new_y});
                visited[new_x][new_y] = 1;
            }
        }
        return res;
    }
    int sumDigit(int i, int j) {
        int sum = 0;
        while (i > 0) {
            sum += i % 10;
            i /= 10;
        }
        while (j > 0) {
            sum += j % 10;
            j /= 10;
        }
        return sum;
    }
private:
    vector<pair<int, int>> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
};

回溯(深度優先

class Solution {
public:
    void DFS(int m, int n, int k, int &res, int row, int col, vector<vector<int>> &vis, vector<vector<int>> &huisu){
    huisu[row][col]=1;

    //統計合法的且沒有訪問過的格子。
    if(vis[row][col]==0){
        res++;
        vis[row][col]=1;
    }

    int i, j;
    vector<vector<int>> offset={{-1,0},{1,0},{0,-1},{0,1}};
    for(i=0;i<offset.size();i++){
        int x=row+offset[i][0];
        int y=col+offset[i][1];
        if(x<0||x>=m||y<0||y>=n||!sumDigit(x,y,k)||vis[x][y]==1){
            continue;
        }
        else{
            DFS(m, n, k, res, x, y, vis, huisu);
        }
    }
    huisu[row][col]=0;
}

bool sumDigit(int x, int y, int k){
    int sum=0;
    while(x>0){
        sum+=x%10;
        x=x/10;
    }
    while(y>0){
        sum+=y%10;
        y=y/10;
    }
    return sum>k?false:true;
}
int movingCount(int m, int n, int k) {
    int res=0;
    vector<vector<int>> vis(m, vector<int>(n,0));
    //把已經走過的合法格子標記爲1,使得res不重複記錄合法的格子
    vector<vector<int>> huisu(m, vector<int>(n,0));
    //在DFS中,z某條路徑走過的格子標記爲1,避免下一次重複走回歷史的格子。
    DFS(m, n, k, res, 0, 0, vis, huisu);
    return res;
}
};

對於矩陣搜索,常用深度優先搜索,因爲其較爲直觀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章