训练题:矩阵路径

题目:http://t.cn/A62ObxiQ

  • 时间复杂度:O(3kMN),k为字符串长度
  • 空间复杂度:O(k) ```cpp

bool exist( vector> &board, string word ) { const auto countRow = board.size(); const auto countCol = board[0].size(); const auto wordSize = word.size(); for( int i = 0; i < countRow; ++i ) { for( int j = 0; j < countCol; ++j ) { if( dfs( board, word, countRow, countCol, wordSize, 0, i, j ) ) { return true; } } } return false; }

bool dfs( vector> &board, const string &word, const int &countRow, const int &countCol, const int &wordSize, const int &start, int i, int j ) { if( start == wordSize ) { return true; } if( i == countRow || i < 0 ) { return false; } if( j == countCol || j < 0 ) { return false; } if( board[i][j] != word[start] || board[i][j] == ‘*’ ) { return false; }

  1. char shit = board[i][j];
  2. board[i][j] = '*';
  3. if( dfs( board, word, countRow, countCol, wordSize, start + 1, i + 1, j ) ) { return true; }
  4. if( dfs( board, word, countRow, countCol, wordSize, start + 1, i - 1, j ) ) { return true; }
  5. if( dfs( board, word, countRow, countCol, wordSize, start + 1, i, j + 1 ) ) { return true; }
  6. if( dfs( board, word, countRow, countCol, wordSize, start + 1, i, j - 1 ) ) { return true; }
  7. board[i][j] = shit;
  8. return false;

}

  1. <a name="pMpfj"></a>
  2. # 训练题:顺时针打印矩阵
  3. 题目:[http://t.cn/A6GkjcYi](http://t.cn/A6GkjcYi)
  4. <a name="caKLL"></a>
  5. ## 方法一:模拟法
  6. - 时间复杂度:O(m*n)
  7. - 空间复杂度:O(m*n)
  8. 模拟法,模拟顺时针的过程,递归模拟。
  9. ```cpp
  10. vector<int> spiralOrder( vector<vector<int>> &matrix )
  11. {
  12. if( matrix.empty() ) { return {}; }
  13. const auto countRow = matrix.size();
  14. const auto countCol = matrix[0].size();
  15. // 0:水平往右遍历
  16. // 1:垂直往下遍历
  17. // 2:水平往左遍历
  18. // 3:垂直往上遍历
  19. char direction = 0;
  20. vector<int> ret;
  21. ret.reserve( countRow * countCol );
  22. // 保存已经遍历的元素
  23. vector<vector<bool>> visited( countRow, vector<bool>( countCol, false ) );
  24. _print( matrix, visited, ret, countRow, countCol, direction, 0, 0 );
  25. return ret;
  26. }
  27. bool _print( vector<vector<int>> &matrix, vector<vector<bool>> &visited, vector<int> &container, const int &countRow, const int &countCol, char &direction, int i, int j )
  28. {
  29. // 索引已经越界或者已经被访问过了
  30. if( j >= countCol || j < 0 || i >= countRow || i < 0 || visited[i][j] ) { return false; }
  31. // 记录访问元素
  32. container.push_back( matrix[i][j] );
  33. visited[i][j] = true;
  34. // 先尝试继续相同方向的访问,如果失败了就切换到下一个方向尝试。
  35. if( direction % 4 == 0 && !_print( matrix, visited, container, countRow, countCol, direction, i, j + 1 ) ) { ++direction %= 4; }
  36. if( direction % 4 == 1 && !_print( matrix, visited, container, countRow, countCol, direction, i + 1, j ) ) { ++direction %= 4; }
  37. if( direction % 4 == 2 && !_print( matrix, visited, container, countRow, countCol, direction, i, j - 1 ) ) { ++direction %= 4; }
  38. if( direction % 4 == 3 && !_print( matrix, visited, container, countRow, countCol, direction, i - 1, j ) ) { ++direction %= 4; }
  39. if( direction % 4 == 0 && !_print( matrix, visited, container, countRow, countCol, direction, i, j + 1 ) ) { ++direction %= 4; }
  40. return true;
  41. }

方法二:层遍历

  • 时间复杂度:O(m*n)
  • 空间复杂度:O(1)

从外层往内层遍历,依次打印每层的上、右、下、左。

  1. vector<int> spiralOrder( vector<vector<int>> &matrix )
  2. {
  3. if( matrix.empty() ) { return {}; }
  4. const auto countRow = matrix.size();
  5. const auto countCol = matrix[0].size();
  6. // 记录当前的层索引
  7. char left = 0;
  8. char right = countCol - 1;
  9. char top = 0;
  10. char bottom = countRow - 1;
  11. vector<int> ret;
  12. ret.reserve( countRow * countCol );
  13. while( true )
  14. {
  15. for( int tmp = left; tmp <= right; ++tmp ) { ret.push_back( matrix[top][tmp] ); }
  16. if( ++top > bottom ) { break; }
  17. for( int tmp = top; tmp <= bottom; ++tmp ) { ret.push_back( matrix[tmp][right] ); }
  18. if( --right < left ) { break; }
  19. for( int tmp = right; tmp >= left; --tmp ) { ret.push_back( matrix[bottom][tmp] ); }
  20. if( --bottom < top ) { break; }
  21. for( int tmp = bottom; tmp >= top; --tmp ) { ret.push_back( matrix[tmp][left] ); }
  22. if( ++left > right ) { break; }
  23. }
  24. return ret;
  25. }

训练题:旋转矩阵

题目:http://t.cn/A6Zekoeu

方法一

  • nums[i][j],旋转之后,到了nums[j][n - i - 1]的位置。
  • 用一个相同大小的辅助数组保存旋转后的矩阵

  • 时间复杂度:O(N)

  • 空间复杂度:O(N)

    方法二:模拟旋转

  • 时间复杂度:O(N)

  • 空间复杂度:O(1)

以nums[0][0]元素为例,旋转后到了右上角,这时如果我们再将右上角这个被覆盖之前的元素再旋转到右下角,以此类推,总共旋转4次,最后又旋转到了nums[0][0],这时我们就完成了四个角元素的旋转。我们称这个为一个循环循环。
以下图描述了n分别为奇数和偶数时,我们可以对这4个部分的每个元素进行一个旋转循环就完成了整个矩阵的旋转。
算法训练_矩阵、图 - 图1算法训练_矩阵、图 - 图2

  1. void rotate(vector<vector<int>>& matrix) {
  2. if(matrix.size() < 2) return;
  3. const auto size = matrix.size();
  4. int startI = 0, startJ = 0, nextVal = 0, tmp;
  5. int row = 0, col = 0;
  6. if(size & 0x1){
  7. row = (size - 1) >> 1;
  8. col = (size + 1) >> 1;
  9. }
  10. else row = col = size >> 1;
  11. for(int i = 0; i < row; ++i){
  12. for(int j = 0; j < col; ++j){
  13. // 一个旋转循环
  14. startI = i, startJ = j;
  15. nextVal = matrix[i][j];
  16. while(true) {
  17. tmp = startI;
  18. startI = startJ;
  19. startJ = size - tmp - 1;
  20. tmp = matrix[startI][startJ];
  21. matrix[startI][startJ] = nextVal;
  22. nextVal = tmp;
  23. if(startI == i && startJ == j) break;
  24. }
  25. }
  26. }
  27. }

方法二:翻转代替旋转

  • 时间复杂度:O(N)
  • 空间复杂度:O(1)

旋转90° = 上下翻转 + 主对角线翻转。
image.png

  1. void rotate(vector<vector<int>>& matrix) {
  2. if(matrix.size() < 2) return;
  3. const auto size = matrix.size();
  4. const auto size_2 = (size >> 1);
  5. // 上下翻转
  6. for(int row = 0; row < size_2; ++row){
  7. for(int col = 0; col < size; ++col)
  8. std::swap(matrix[row][col],matrix[size - 1 - row][col]);
  9. }
  10. // 主对角线翻转一次
  11. for(int row = 0; row < size; ++row){
  12. for(int col = row + 1; col < size; ++col)
  13. std::swap(matrix[row][col], matrix[col][row]);
  14. }
  15. }

训练题:矩阵找数

题目:http://t.cn/A624BDfp

方法一:二叉搜索树

将矩阵逆时针旋转45°就是一个二叉搜索树。左节点<父节点<右节点。
我们从根节点(右上角位置)开始遍历查找,

  1. bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
  2. if( matrix.empty() || matrix[0].empty() ) { return false; }
  3. const auto n = matrix.size();
  4. for( int i = 0, j = matrix[0].size()-1; i < n && j >=0; )
  5. {
  6. if( matrix[i][j] > target ) { --j; }
  7. else if( matrix[i][j] < target ) { ++i; }
  8. else { return true; }
  9. }
  10. return false;
  11. }

方法二:同理

既然可以右上角,那也可以左下角。本质一样,只不过方向别扭。

  1. bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
  2. if( matrix.empty() || matrix[0].empty() ) { return false; }
  3. const auto m = matrix[0].size();
  4. for( int i = matrix.size() - 1, j = 0; i >= 0 && j < m; )
  5. {
  6. if( matrix[i][j] > target ) { --i; }
  7. else if( matrix[i][j] < target ) { ++j; }
  8. else { return true; }
  9. }
  10. return false;
  11. }