华为一面算法题:打印螺旋三角形


写在前面

最近面试了华为go开发,一面中遇到一个算法题,也不是很难,在leetcode上应该属于中等难道。20分钟问问题,40分钟写算法题。

这个算法题最终没有写出来,思路是对的,边界情况考虑有点问题,事后大概花了半个小时才调试处理。

整理一下后发现,其实当时变量定义有点乱,多个地方公用了变量,导致有点绕,边界清空改了这里,那里有出现问题了。其实应该要让自己思路清晰一点,逻辑要再清晰一点,不能公用变量的地方不要公用。

下面来看一下具体的题目

题目:螺旋打印出三角形

输入一个数字n(2<=n<=100),按三角形方向从外向内打印出数字,数字从1开始,同一行数字之间用一个空格隔开。

示例1:

n=2

结果:

1 2
3

示例2:

n=3

结果:

1 2 3 
6 4 
5 

示例3:

n=10

结果:

1 2 3 4 5 6 7 8 9 10 
27 28 29 30 31 32 33 34 11 
26 45 46 47 48 49 35 12 
25 44 54 55 50 36 13 
24 43 53 51 37 14 
23 42 52 38 15 
22 41 39 16 
21 40 17 
20 18 
19 

分析

题目意思应该很清楚明了,从示例3中n=10可以发现规律,数字是一层一层、从外向内循环的,而且每层循环都是从对角线开始,如第一层循环从(0,0),第二层循环从(1,1)开始,第三层循环从(2,2)开始,第四层循环从(3,3)开始。因此写代码可以按层开始,写一个循环,每次处理一层数据。

对于每一层,分成3个边,下面先看第一层

  • 第一个边,是三角形的行线,从(0,0)开始,向右打印10个数字。
  • 第二个边,是三角形的斜线,从(1,8)开始,下左下打印9个数字。
  • 第三个边,是三角形的竖线,从(8,0)开始,向上打印9个数字。

至此可以写代码了

代码

func LuoXuanArray(n int) {
	idx := 1 // 从1开始递增
	// countN 表示每一层三角形单边数据量. 假设n=10,则每层数量为10,7,4,1
	countN := n
	// 定义结果数组
	result := make([][]int, n)
	for i := range result {
		result[i] = make([]int, n)
	}
	cycle := 1
	for true {
		// 处理第 cycle 层
		// 1.处理行。1 2 3 4 5 6 7 8 9 10
		// (i,j)表示当前行的第一个位置
		i := cycle - 1
		j := cycle - 1
		if i > n/2 || j > n/2 || result[i][j] != 0 {
			// 如果某一层开始的数字不等于0或者超过了边界,则可以退出了
			break
		}
		for c := 0; c < countN; c++ {
			result[i][j] = idx
			j++
			idx++
		}
		// 2.处理斜线,从 (i+1,j-2)开始,需要处理countN-1个数字
		// 循环处理countN-1次
		i++
		j -= 2
		for c := 0; c < countN-1; c++ {
			result[i][j] = idx
			idx++
			i++
			j--
		}
		// 3.处理列,从 (i-2,j+1)开始,需要处理countN-2个数字
		i -= 2
		j++
		for c := 1; c <= countN-2; c++ {
			result[i][j] = idx
			idx++
			i--
		}
		countN -= 3 // 每一层都比上一层的数字少3个
		cycle++
	}
	// 输出结果
	for i := range result {
		for j := 0; j < n-i; j++ {
			fmt.Printf("%d ", result[i][j])
		}
		fmt.Println()
	}
}

难点

这道题有3个点需要注意

  • 1.层的数量。作者一开始计算层的数量level:=(n-1)/2,用的测试用例是n=3,n=10,这2个是对的,但是当n=2,level=0,显然不对。后面改成从1开始,for循环用true,在循环里面通过i > n/2 || j > n/2 || result[i][j] != 0判断循环是否需要结束
  • 2.对于三条边的处理,开始节点一定要注意
      - 行线是从(cycle-1,cycle-1)开始,向右走,并且数量是countN
      - 斜线是从行线最后一个节点的基础上,(i+1,j-1)开始的,向左下走,由于在行线处理的for循环中j++了,所以斜线第一个节点是(i+1,j-2),数量是countN-1
      - 竖线是从斜线最后一个节点的基础上,(i-1,j)开始,向上走,由于斜线的处理中for循环中i++,j–了,所以竖线第一个节点是(i-2,j++),数量是countN-2
  • 3.三个for循环处理三个边的时候,临时变量最好定义成一个单独的,不要用i或者j去遍历。(用i或者j也没问题,但是非常容易出错,代码中出现了很多+1,+2,-1,-2的边界情况,作者就是在这个地方出了问题,导致在规定时间内没有做出来)

leetcode上有个类似的题目:螺旋矩阵,感兴趣的同学可以去做一做,这里是作者写的题解

br>


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录