给定一个 m x n
的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
提示:
m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1
进阶:
一个直观的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个仅使用常量空间的解决方案吗?
我的题解:
func setZeroes(matrix [][]int) {
m := len(matrix)
n := len(matrix[0])
ans := make([][]int, m)
for i := range ans{
ans[i] = make([]int, n)
}
for i := 0; i < m; i++{
for j := 0; j < n; j++{
ans[i][j] = matrix[i][j]
}
}
for i := 0; i < m; i++{
for j := 0; j < n; j++{
if matrix[i][j] == 0{
for l := 0; l < n; l++{
ans[i][l] = 0
}
for k := 0; k < m; k++{
ans[k][j] = 0
}
}
}
}
copy(matrix, ans)
}
注意:
Go语言的内置函数copy()可以将一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。
下面的代码展示了使用copy()函数将一个切片复制到另一个切片的过程:
slice1:=[]int{1,2,3,4,5}slice2:=[]int{5,4,3}copy(slice2,slice1)//只会复制slice1的前3个元素到slice2中copy(slice1,slice2)//只会复制slice2的3个元素到slice1的前3个位置
虽然通过循环复制元素更直接,不过内置的copy函数使用起来更加方便。
copy函数的第一个参数是要复制的目标slice,第二个参数是源slice,目标和源的位置顺序和dst=src赋值语句是一致的。两个slice可以共享同一个底层数组,甚至有重叠也没有问题。copy函数将返回成功复制的元素的个数,等于两个slice中较小的长度,所以我们不用担心覆盖会超出目标slice的范围。
两个切片共享一个底层数组,改变一个会影响另一个
官方题解:
方法一:使用标记数组
思路和算法
我们可以用两个标记数组分别记录每一行和每一列是否有零出现。
具体地,我们首先遍历该数组一次,如果某个元素为 0,那么就将该元素所在的行和列所对应标记数组的位置置为true。最后我们再次遍历该数组,用标记数组更新原数组即可。
1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。 2.当把切片a赋给切片b时,改变切片b会改变切片a
func setZeroes(matrix [][]int){
row := make([]bool, len(matrix))//一维切片 用来表示每一行是否有0存在 只统计一次即可,所以用一维切片就行
col := make([]bool, len(matrix[0]))//一维切片 用来表示每列行是否有0存在 只统计一次即可,所以用一维切片就行
for i, r := range matrix{//i表示行号,r表示行号对应的一维数组
for j, v := range r{//j表示列号,v表示i、j对应的元素
if v == 0{
//所在行列值为TRUE
row[i] = true
col[j] = true
}
}
}
for i, r := range matrix{
for j := range r{
//遍历所有元素
if row[i] || col[j]{//i所在行,j所在列为0
r[j] = 0
}
}
}
}
方法二:使用两个标记变量
思路和算法
我们可以用矩阵的第一行和第一列代替方法一中的两个标记数组,以达到 O(1) 的额外空间。但这样会导致原数组的第一行和第一列被修改,无法记录它们是否原本包含 0。因此我们需要额外使用两个标记变量分别记录第一行和第一列是否原本包含 0。
在实际代码中,我们首先预处理出两个标记变量,接着使用其他行与列去处理第一行与第一列,然后反过来使用第一行与第一列去更新其他行与列,最后使用两个标记变量更新第一行与第一列即可。
func setZeroes(matrix [][]int){
n, m := len(matrix), len(matrix[0])//记录行数、列数
row0, col0 := false, false//初始默认第一行和第一列本身无0
for _, v := range matrix[0]{//遍历第一行,判断是否有0
if v == 0{
row0 = true
break
}
}
for _, r := range matrix{//遍历第一列,判断是否有0
if r[0] == 0{
col0 = true
break
}
}
for i := 1; i < n; i++{//遍历除了第一行和第一列以外的元素
for j := 1; j < m; j++{
if matrix[i][j] == 0{//如果该元素为0,则该元素对应的第一行和第一列的相应位置置为0
matrix[i][0] = 0
matrix[0][j] = 0
}
}
}
for i := 1; i < n; i++{
for j := 1; j < m; j++{//遍历除了第一行和第一列以外的元素
if matrix[i][0] == 0 || matrix[0][j] == 0{//如果该元素对应的第一行或第一列的相应位置置为0,将该元素置为0
matrix[i][j] = 0
}
}
}
if row0{//row0若为TRUE,则将第一行全部元素置为0
for j := 0; j < m; j++{
matrix[0][j] = 0
}
}
if col0{
for _, r := range matrix{
r[0] = 0
}
}
}
方法三:使用一个标记变量
思路和算法
我们可以对方法二进一步优化,只使用一个标记变量记录第一列是否原本存在 0。这样,第一列的第一个元素即可以标记第一行是否出现 0。但为了防止每一列的第一个元素被提前更新,我们需要从最后一行开始,倒序地处理矩阵元素。
func setZeroes(matrix [][]int) {
n, m := len(matrix), len(matrix[0])
col0 := false
for _, r := range matrix {
if r[0] == 0 {//遍历每一行的第一个元素是否为0,即第一列是否有元素为0
col0 = true//第一列是否有0
}
for j := 1; j < m; j++ {//遍历除第一列以外的所有元素
if r[j] == 0 {
//对应的第一行和第一列的相应元素为0
r[0] = 0
matrix[0][j] = 0
}
}
}
for i := n - 1; i >= 0; i-- {//从最后一行开始遍历
for j := 1; j < m; j++ {//除第一列以外的其他列
if matrix[i][0] == 0 || matrix[0][j] == 0 {
matrix[i][j] = 0
}
}
if col0 {
matrix[i][0] = 0
}
}
}