您的位置:首页 > 其它

LintCode 解题记录 Matrix专题

2017-07-26 21:19 441 查看

LintCode Matrix Zig Traversal

按照之字形遍历输出矩阵。跟之前按照螺旋稳的方式遍历输出矩阵的题目很相似。

但是自己做起来就觉得总是这考虑不全那儿考虑不全,改来改去才能AC。

看了网上的解法,觉得比自己的要好,于是记录一下。

思路:

首先要明白之字形是怎么样的一种走法,可以概括为如下方式:

先沿斜右上方走到头,然后选择向右移动一位或者是像下移动一位。

然后沿斜左下方走到头,然后选择向右移动一位或者是像下移动一位。

然后重复上述过程,直到填满res数组。

代码:

vector<int> printZMatrix(vector<vector<int> > &matrix) {
// write your code here
vector<int> res;
if (matrix.size() == 0 || matrix[0].size() == 0) return res;
int m = matrix.size(), n = matrix[0].size(), cnt = m * n;
res = vector<int>(cnt, 0);
res[0] = matrix[0][0];
for (int i = 1, r = 0, c = 0; i < cnt;) {
while (r > 0 && c+1 < n) res[i++] = matrix[--r][++c]; //斜右上方
if (c+1 < n) res[i++] = matrix[r][++c];
else if (r+1 < m) res[i++] = matrix[++r][c];
while (c > 0 && r+1 < m) res[i++] = matrix[++r][--c]; //斜左下方
if (r+1 < m) res[i++] = matrix[++r][c];
else if (c+1 < n) res[i++] = matrix[r][++c];
}
return res;
}


LintCode Search a 2D Matrix

给定一个mxn矩阵,每一行都按照从小到大排列,下一行的第一个元素总比上一行的最后一个元素大,要求查找一个数target,时间复杂度是O(logn+ logm)。

思路:

典型的两次二分查找。简要回忆一下三种二分查找的形式。

1)查找给定的数,找不到则返回-1。

2)查找序列中第一个不小于目标值的数,返回其下标。即C++STL之lower_bound。同理于最后一个小于目标值的数。

3)查找序列中第一个大于目标值的数,返回其下标。即C++STL之upper_bound。同理于最后一个小于等于目标值的数。

那么此题就是现在第一列中找到最后一个大于目标值的数,然后在此行再用一次二分查找查找该数,找到则返回true,找不到则返回false。

bool searchMatrix(vector<vector<int> > &matrix, int target) {
// write your code here
if (matrix.size() == 0 || matrix[0].size() == 0) return false;
int row = matrix.size(), col = matrix[0].size();
int l = 0, h = row, mid = 0;
while (l < h) {
mid = (l+h) >> 1;
if (matrix[mid][0] <= target) l = mid+1; //小于等于目标值就跳过
else h = mid;
}
int currh = h-1;
if (currh < 0) return false;
l = 0, h = col-1;
while (l <= h) {
mid = (l + h) >> 1;
if (matrix[currh][mid] < target) l = mid+1;
else if (matrix[currh][mid] > target) h = mid-1;
else return true;
}
return false;
}


LintCode Valid Sudoku

判断给定的矩阵是不是合法的数独。就按照数独的定义分别判断每一行、每一列、每一个小3x3矩阵是不是符合题意就好了。

bool isValidSudoku(const vector<vector<char>>& board) {
if (board.size() == 0 || board[0].size() == 0) return false;
int row = board.size(), col = board[0].size();
unordered_map<char, int> hash;
for (int i = 0; i < row; i++) {
hash.clear();
for (int j = 0; j < col; j++) {
if (board[i][j] == '.') continue;
if (hash.find(board[i][j]) != hash.end()) return false;
hash[board[i][j]] = 1;
}
}
for (int i = 0; i < col; i++) {
hash.clear();
for (int j = 0; j < row; j++) {
if (board[j][i] == '.') continue;
if (hash.find(board[j][i]) != hash.end()) return false;
hash[board[j][i]] = 1;
}
}
for (int i = 0; i < 9; i++) {
int x = i/3, y = 3*(x%3);
hash.clear();
for (int m = x; m < x+3; m++) {
for (int n = y; n < y+3; n++) {
if (board[m]
== '.') continue;
if (hash.find(board[m]
) != hash.end()) return false;
hash[board[m]
] = 1;
}
}
}
return true;
}


LintCode Kth Smallest Number in Sorted Matrix

在一个行和列内元素都是有序的矩阵中找到第k小的数。

时间复杂度O(klogn),其中n = max(row, col)

思路: 一维数组的topK问题用堆来解决,这里也采用类似的优先队列。但是遍历矩阵的方式却不一样,因为矩阵都是有序的,如果已知matrix[i][j],那么显然紧跟其后的数是matrix[i+1][j]和matrix[i][j+1],出栈k次,此时的队首即是答案。

int kthSmallest(vector<vector<int> > &matrix, int k) {
// write your code here
if (matrix.size() == 0 || matrix[0].size() == 0) return 0;
int m = matrix.size(), n = matrix[0].size();
priority_queue<pair<int, pair<int, int> >, vector<pair<int, pair<int, int> > >,
greater<pair<int, pair<int, int> > > > min_heap; //这个小堆的声明还是很麻烦的
vector<vector<int>> vis(m, vector<int>(n, 0));

min_heap.push(make_pair(matrix[0][0], make_pair(0, 0)));
vis[0][0] = 1;
while (--k) {
int i = min_heap.top().second.first;
int j = min_heap.top().second.second;
min_heap.pop();
if (i+1 < m && vis[i+1][j] == 0) {
vis[i+1][j] = 1;
min_heap.push(make_pair(matrix[i+1][j], make_pair(i+1, j)));
}
if (j+1 < n && vis[i][j+1] == 0) {
vis[i][j+1] = 1;
min_heap.push(make_pair(matrix[i][j+1], make_pair(i, j+1)));
}
}
return min_heap.top().first;
}


LintCode Rotate Image

给定一个二维矩阵,返回其顺时针旋转90度的新矩阵。

一想到这道题我就想到了看图软件的旋转图片是不是就是用了这样的算法呢??

思路:

其实就是相当于从最外层顺时针旋转,然后跳到里面一层旋转,简单举例一下便可知道旋转的层数是2/n。

注意理清旋转中四个位置的坐标关系即可。

void rotate(vector<vector<int> > &matrix) {
// write your code here
if (matrix.size() == 0 || matrix[0].size() == 0) return;
int n = matrix.size();
int layerNum = n / 2;
for (int i = 0; i < layerNum; i++) {
for (int j = i; j < n - 1 - i; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - 1 - j][i];
matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j];
matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i];
matrix[j][n - 1 - i] = temp;
}
}
}


LintCode Search a 2D Matrix II

给定一个每一行、每一列都有序的矩阵,求给定的target出现了多少次。

思路:

这题用倒序法更好一点,比如从最后一行开始考虑,如果最后一行第一个元素都小于等于第一个元素,那么说明第一列的元素都不可能存在target(每一行或每一列都没有重复元素),那么就跳到最后一行第二个元素。反之,则说明第一列可能存在target元素,则向上跳一个元素。时间复杂度为O(m+n)。

代码:

思路是看的别人的,所以我自己写了从最后一列开始考虑的。

int searchMatrix(vector<vector<int> > &matrix, int target) {
// write your code here
if (matrix.size() == 0 || matrix[0].size() == 0) return 0;
int m = matrix.size(), n = matrix[0].size();
int i = 0, j = n-1, res = 0;
while (i < m && j >= 0) {
if (matrix[i][j] == target) res++;
if (matrix[i][j] > target) j--;
else i++;
}
return res;
}


LintCode Set Matrix Zeros

对于一个矩阵matrix,matrix[i][j]为0,则将其所在的行和列的所有元素都设置0。要求空间复杂度为O(1)。

刚拿到这道题时遇到的困难就是如果遍历到一个元素为0,就进行修改的话,肯定会破坏还没有遍历到的数的信息,从而再遍历到0时不知道是原本就是的还是被改为0的。因为就需要额外的空间来告诉我这个信息。

O(mn)的空间复杂度就是开一个跟原矩阵一样大小的矩阵,这样我在遍历到一个0时就知道是原本是0还是被修改为0。

O(m+n)的空间复杂度就是开一个m长的数组和一个n长的数组,表示m[i]就表示哪一行有0,n[j]就表示哪一列有0,最后再修改原矩阵。

题目要求的空间复杂度是O(1),那么我们借助O(m+n)的思想,把这两个数组选为第一行与第一列。如果遍历到某一个元素为0,那么就把该元素所在行的第一个元素置为0,所在列的第一个元素置为0。那么同样的,对于第一行的元素,若为0我怎么知道是原本就是还是被修改的呢?只需要先单独遍历第一行与第一列,用两个指针标志一下就好了。

代码:

void setZeroes(vector<vector<int> > &matrix) {
// write your code here
if (matrix.size() == 0 || matrix[0].size() == 0)  return;
bool firstRow = false, firstCol = false;
int m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; i++) {
if (matrix[i][0] == 0) {
firstCol = true;
break;
}
}
for (int j = 0; j < n; j++) {
if (matrix[0][j] == 0) {
firstRow = true;
break;
}
}

for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][j] == 0) {
matrix[0][j] = 0;
matrix[i][0] = 0;
}
}
}

for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}

for (int i = 0; i < m && firstCol; i++) matrix[i][0] = 0;
for (int j = 0; j < n && firstRow; j++) matrix[0][j] = 0;
}


LintCode Submatrix Sum

给定一个矩阵,求其一个子矩阵,其元素和为0。

先来看一个简单的一点:给定一个数组,求其一个子数组,其和为0。

暴力O(n2)解决,这里不谈,这里说一下O(n)的解法,这种方法很好希望可以记住。

就是在遍历数组的时候,用一个sum记录从第一个元素到第i个元素的值,然后继续向后遍历,如果出现sum2 == sum1,也就是说在位置j出现了之前已经出现的sum,那么就能说明 从第i+1到第j个数的和为0。

这道题的思路同样如此。

首先声明一个二维sum数组,sum[i+1][j+1]代表的是matrix[0][0]到matrix[i][j]的求和,递推关系为

sum[i+1][j+1] = sum[i+1][j]+sum[i][j+1]+matrix[i][j]-sum[i][j];

这里为什么用i+1呢?我猜测就是可以避免i-1为负的情况。

其次,考虑两行i1,i2,对于k列,sum[i2][k] - sum[i1][k]就是i1到i2-1行,k-1列的矩阵的值,记此值为diff。同理随着k向右移动,如果在位置k’再次出现了diff,就说明第k列到当前k’-1列就是值为0的矩阵。

此时的矩阵的左上角就是(i1, k),右下角是(i2-1, k’-1)。

代码:

vector<vector<int>> submatrixSum(vector<vector<int>>& matrix) {
// Write your code here
vector<vector<int>> res;
if (matrix.size() == 0 || matrix[0].size() == 0) return res;
int m = matrix.size(), n = matrix[0].size();
vector<vector<int >> sum(m+1, vector<int>(n+1, 0));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
sum[i+1][j+1] = sum[i][j+1] + sum[i+1][j] + matrix[i][j] - sum[i][j];
}
}

unordered_map<int, int> hash;
for (int i1 = 0; i1 < m; i1++) {
for (int i2 = i1+1; i2 <= m; i2++) {
hash.clear();
for (int j = 0; j <= n; j++) {
int diff =  sum[i2][j] - sum[i1][j];
//diff是从第i2-1行到i1行,第0列到第j-1列围成的矩阵的值
if (hash.find(diff) != hash.end()) {
vector<int> left_up{i1, hash[diff]};
vector<int> right_down{i2-1, j-1};
res.push_back(left_up);
res.push_back(right_down);
return res;
} else {
hash[diff] = j;
}
}
}
}
return res;
}


总结:

暑假在家过得太浪了,游泳、健身、打球还有农药,很难静下心去学习- -。

最近在听JJ的 不为谁而做的歌,单曲循环了好久了~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: