【劍指offer】矩陣思路
1、順時針列印矩陣
思路:定義r=0,c=0,rows=len(arr)-1,cols=len(arr)-1,從左到右列印第一行,從上到下最後一列,從右到左最後一行,從下到上第一列,先後r++,c++,rows--,cols--,列印裡層;
#!usr/bin/python #-*- coding:utf-8 -*- '''順時針列印矩陣''' def printArr(arr): r = 0 c = 0 row = len(arr) - 1 col = len(arr[0]) - 1 # col res = [] while(r < row and c < col): for i in range( c, col): res.append( arr[r][i] ) for j in range( r, row): res.append( arr[j][col] ) for k in range( col ,c,-1): res.append( arr[row][k] ) for h in range( row ,r, -1): res.append( arr[h][c] ) r +=1 ;c+=1; row -= 1; col -= 1 if( r == row and c == col): res.append( arr[r][c]) print( res ) # m = [[1,2],[3,4],[5,6]] m =[[1, 2, 3], [4, 5, 6], [7, 8, 9]] printArr( m)
2、矩陣路徑-包含某字串
題意:請設計一個函式,用來判斷在一個矩陣中是否存在一條包含某字串所有字元的路徑。 路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。 如果一條路徑經過了矩陣中的某一個格子,則該路徑不能再進入該格子。
思路:從任意位置開始,上下左右四個方向找,找到了返回true;
def hasPath(mat, rows, cols, str, flag, i, j, k): index = i * cols + j # 當前元素所在位置 '''不滿足條件的情況: 1.出界, 2.沒找到, 3.查詢到且標記了(意思就是矩陣的一個格子只能重複利用,第一個'B’用了,同個位置第二個'B'就不能用了); 若不滿足任一條件,就返回false,繼續下一個位置查詢; 若滿足條件,即該位置未標記,且找到了一個字元,那麼從該位置開始向上下左右四個方向找; ''' if (i <0) or ( i >=rows) or (j < 0) or (j >= cols) or\ mat[index] != str[k] or flag[index] == 1 : return False if k == (len(str)-1): #子串全找到了,返回True return True # 從該位置開始找,不用糾結這裡,每個位置都會遍歷到的 flag[index] = 1 if \ hasPath(mat, rows, cols, str, flag, i+1, j, k + 1 ) or \ hasPath(mat, rows, cols, str, flag, i-1, j, k + 1) or\ hasPath(mat, rows, cols, str, flag, i, j+1, k + 1) or\ hasPath(mat, rows, cols, str, flag, i, j-1, k + 1): return True else: # 該位置四個方向都沒找到,則該位置重置為0,返回false,繼續下一個位置 flag[index] = 0 return False def find( mat ,rows, cols, str): if mat == '' or str == '': return False flag = [0]*rows*cols # 記錄是否查詢過, 1為查詢過,0為未查詢過 for i in range( rows ): for j in range( cols ): if hasPath(mat, rows, cols, str, flag, i, j, 0): return True return False arr = 'ABCESFCSADEE' key = 'ABCB' # m = ' '.join( s ).split(' ') # arr = np.array( m ).reshape(3,4) # key = ' '.join( key ).split(' ') print(find( arr, 3, 4 ,key))
3、矩陣的路徑-左上角到右下角的總路徑數
題意,只能向右、向下
思路一、動態規劃:達到每個位置的路徑數都是由它上面一個位置和左邊一個位置的到達路徑數決定的,狀態方程為:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
import numpy as np '''動態規劃''' def findPath( m, n ): # 初始化 dp = np.ones([m,n]) for i in range( 1, m ): for j in range(1, n ): dp[i][j] = dp[i-1][j] + dp[i][j-1] print(dp[m-1][n-1]) findPath( 3,4)
思路二、數學方法:C(M,N)的組合問題;m 行,n 列,總共需要走 M = m + n -2 步,其中總共需要向下走 N = m - 2 步, 可以看成一個組合問題,即從 M 個位置選 N 個出來, C(M,N);
def mathPath( m, n):
M = m + n -2 #總共需要走這麼多步
N = m - 1
num = 1
for i in range(1, N+1):
num = num * ( ( M - N + i ) / i) # 組合問題,計算出來的一定是整數,中間過長不要用 "//"取餘操作
print(num)
mathPath( 3,4)
4、矩陣路徑-左上角到右下角的最小路徑和
題意:求從矩陣的左上角到右下角的最小路徑和,每次只能向右和向下移動。
#!/usr/bin/python
# -*- coding:utf-8 -*-
'''矩陣最小路徑和'''
'''
當前位置的最小路徑和
思路:只考慮向下向右走,向左向上相當於走回頭路,不存在負數的情況下是多餘的;
那麼當前位置之和左一上一有關
'''
import numpy as np
def MatrixPathSum( nums,m,n ):
if nums == []:
return 0
# dp = np.zeros([m,n])
# dp[0][0] = nums[0][0]
dp = nums
for i in range( 1, m ):
dp[i][0] = dp[i-1][0] + nums[i][0]
for j in range( 1, n):
dp[0][j] = dp[0][j-1] + nums[0][j]
for i in range(1, m ):
for j in range(1, n ):
dp[i][j] = min(dp[i-1][j] , dp[i][j-1]) + nums[i][j]
print( "最小路徑和:", dp[m-1][n-1] )
print(dp)
return dp[m-1][n-1]
arr = [[1,3,1],[1,5,1],[4,2,1]]
MatrixPathSum(arr, 3,3)
5、矩陣覆蓋
題意:我們可以用 2*1 的小矩形橫著或者豎著去覆蓋更大的矩形。請問用 n 個 2*1 的小矩形無重疊地覆蓋一個 2*n 的大矩形,總共有多少種方法?
思路:斐波那契數列問題
def MatrixRectCover( m ):
if m <= 2:
return m
dp = [0] * (m+1)
dp[1] = 1
dp[2] = 2
for i in range( 3, m+1 ):
dp[i] = dp[i-1] + dp[i-2]
print(dp[m])
return dp[m]
MatrixRectCover(3)
6、機器人的移動範圍
題意:地上有一個 m 行和 n 列的方格。一個機器人從座標 (0, 0) 的格子開始移動,每一次只能向左右上下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於 k 的格子。
例如,當 k 為 18 時,機器人能夠進入方格 (35,37),因為 3+5+3+7=18。但是,它不能進入方格 (35,37),因為 3+5+3+8=19。請問該機器人能夠達到多少個格子?
思路:回朔法,前後左右四個方向找,四個方向移動的步數求和,走過的格子標記為1,不再走第二遍;
【java版本】
package Solution;
public class Solution {
//回溯法
public int movingCount(int threshold, int rows, int cols) {
if(rows<=0||cols<=0||threshold<0) return 0;
int[] visited=new int[rows*cols];
return MovingCount(threshold,rows,cols,0,0,visited);
}
private int MovingCount(int threshold,int rows,int cols,int row,int col,int[] visited){
int count=0;
if(canWalkInto(threshold, rows, cols, row, col, visited)){
visited[row*cols+col]=1;
count=1+MovingCount(threshold,rows,cols,row-1,col,visited) //往上
+MovingCount(threshold,rows,cols,row+1,col,visited) //往下
+MovingCount(threshold, rows, cols, row, col-1, visited) //往左
+MovingCount(threshold, rows, cols, row, col+1, visited); //往右
}
return count;
}
private boolean canWalkInto(int threshold,int rows,int cols,int row,int col,int[] visited){
if(row>=0 && row<rows && col>=0 && col<cols
&& getSumOfDigits(row)+getSumOfDigits(col)<=threshold
&& visited[row*cols+col]==0)
return true;
else
return false;
}
// 個位 + 十位 + ...
private int getSumOfDigits(int number){
int sum=0;
while(number!=0){
sum+=number%10;
number/=10;
}
return sum;
}
}
【python 版本】
#!/usr/bin/python
#-*- coding:utf-8 -*-
'''
地上有一個 m 行和 n 列的方格。
一個機器人從座標 (0, 0) 的格子開始移動,每一次只能向左右上下四個方向移動一格,
但是不能進入行座標和列座標的數位之和大於 k 的格子。
例如,當 k 為 18 時,機器人能夠進入方格 (35,37),因為 3+5+3+7=18。
但是,它不能進入方格 (35,37),因為 3+5+3+8=19。
請問該機器人能夠達到多少個格子?'''
class Solution:
def movingCount(self, threshold, rows, cols):
if rows <= 0 or cols <= 0 or threshold < 0:
return 0
flag = [0] * rows * cols
return self.Moving(threshold, rows, cols, flag, 0, 0)
def Moving(self, threshold, rows, cols, flag, i, j):
count = 0
index = i * cols + j
if (i <0 or i>=rows or j<0 or j>=cols or \
self.getSum(i,j) > threshold or flag[index] == 1):
return count
flag[index] = 1
count = 1 + self.Moving( threshold, rows, cols, flag, i + 1, j) + \
self.Moving( threshold, rows, cols, flag, i - 1, j) + \
self.Moving( threshold, rows, cols, flag, i , j + 1) + \
self.Moving( threshold, rows, cols, flag, i , j - 1)
return count
def getSum(self, m, n):
sum = 0
for i in [m, n]:
while i != 0:
sum += i%10
i = i//10
return sum
w = Solution()
count = w.movingCount(5,10,10)
print( count ) # 21