对角线遍历矩阵

8 篇文章 1 订阅

开心一刻

        一次,领导要去精神病院去检查,院长非常紧张,怕病人出什么差错,于是就把病人召集在一起说:“大家都听好了,领导来了,都看我的如果我咳嗽就鼓掌,跺脚就停,如果做的好晚上就给大家吃包子,要是有一个人没做好就谁也别想吃,明白了吗?”大家都点头。
        领导来了,大家列队欢迎,这是院长咳嗽了,大家开始鼓掌,领导很高兴边鼓掌边往前走,突然院长跺脚了,掌声立刻停了,这时只有领导还在鼓掌,此时从病人中冲出一人,跑到领导面前就给了他一巴掌,嘴里还念叨说:“你丫不想吃包子啦?”

题目介绍

       给定一个大小为 n*m 的矩阵,请以对角线遍历并返回遍历结果。牛客链接
       做这道题目的时候遇到了一些感觉不好理解的地方,因此写下这篇博客记录一些心得。

做法

package main
import "fmt"
func diagonalOrder( mat [][]int ) []int {
    m, n := len(mat), len(mat[0])
    i, j, dir_i, dir_j := 0, 0, -1, 1
    var ans = make([]int, m * n)
    var idx int
    for idx < m * n {
        ans[idx] = mat[i][j]
        idx++
        i, j = i + dir_i, j + dir_j
        if i < 0 || j < 0 || i == m || j == n {
            if dir_i == 1 {//左下方向
                if i < m {
                    j = 0
                } else {
                    i, j = i - 1, j + 2
                }
            } else {//右上方向
                if j < n {
                    i = 0
                } else {
                    i, j = i + 2, j - 1
                }
            }
            dir_i, dir_j = dir_j, dir_i
        }
    }
    return ans
}
func main() {
	fmt.Println(diagonalOrder([][]int{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}))
}

       算法主要是要能够模仿对角线遍历时方向的改变。我这里用dir_i和dir_j模拟方向,初始化时为-1和1,代表右上方向。最终打印的数组的大小可以确定,因此我直接申请固定大小的数组空间,然后使用idx代表每次填充的数据。每次填充一个数据后,模拟i、j在矩阵中的移动,然后判断是否需要改变方向。
       这里需要注意的是14行和20行的代码,往左下方向遍历时,只有遍历最左下角元素后是一种特殊情况,往右上方向遍历时,只有遍历最右上角元素后是一种特殊情况,需要特殊判断。
       循环结束的条件为idx < m * n,这个将会导致多计算一次i、j,且肯定是越界的,在本题目中没有问题,但是在下面的扩展题目中将会出现问题。

题目扩展

       对角线遍历非标准m*n的矩阵,m、n表示矩阵中最长的行和列。

package main

import "fmt"
func diagonalOrder( mat [][]int ) []int {
	m, n := len(mat), 0
	count := 0
	for i := 0; i < m; i++ {
		count += len(mat[i])
		if n < len(mat[i]) {
			n = len(mat[i])
		}
	}
	i, j, dir_i, dir_j := 0, 0, -1, 1
	var ans = make([]int, count)
	var idx int
	var f = func() {
		if i < 0 || j < 0 || i == m || j == n {
			if dir_i == 1 {
				if i < m {//左下方向
					j = 0
				} else {
					i, j = i-1, j+2
				}
			} else {
				if j < n {//右上方向
					i = 0
				} else {
					i, j = i+2, j-1
				}
			}
			dir_i, dir_j = dir_j, dir_i
		}
	}
	for {
		ans[idx] = mat[i][j]
		idx++
		if idx >= count {
			break
		}
		i, j = i + dir_i, j + dir_j
		f()
		for j >= len(mat[i]) {
			i, j = i + dir_i, j + dir_j
			f()
		}
	}
	return ans
}
func main() {
	fmt.Println(diagonalOrder([][]int{{1,2,3,4,5,6,7,8,9,0},{5,6,7},{8,9,10,11,12,17,18},{13,14,15,16,2,3,4,5}}))
}

       扩展题目中,外循环结束的条件变为用if语句判断idx是否大于等于count,将不会计算多余的那一次i和j的值,否则会造成死循环。
       改变方向的判断因为需要使用多次,所以我这里使用了一个内部函数来替换,减少代码行数。
       这种非标准的矩阵的对角线遍历其实就是多调用了几次40行和41行的代码,主要就是为了能够找出下一个合法的可访问的位置。

文中都是我个人的理解,如有错误的地方欢迎下方评论告诉我,我及时更正,大家共同进步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值