原题描述
LeetCode 1424 对角线遍历 II: 给定一个二维数组,按照下图所示的方式遍历,按遍历顺序输出为数组。

方法一、优先级队列
思路简述
看到这一道题目,顺其自然地想到按照图中的规律老老实实地进行遍历,但这种方法的时间复杂度高达 O ( m ∗ n ) O(m * n) O(m∗n),考虑到题目给出的m和n的数据范围最高可以到 1 0 5 10^5 105,这中方法是会超时的。实际上,这个二维数组里面最多只有 1 0 5 10^5 105个数据,因为每一列的长度不一样。观察到遍历的位置的变化,是否每一轮的遍历的点的xy坐标之和是相等的,且先从y坐标小的开始遍历,因此,可以考虑将所有元素加入到一个优先级队列中,每一次去队列中位置最小的点来遍历(首先考虑xy坐标之和最小,然后相等的话再比较y)。
代码实现
type Pos struct {
x, y, v int
}
type HeapPos []*Pos
func (h HeapPos) Len() int {
return len(h)
}
func (h HeapPos) Less(i, j int) bool {
if h[i].x + h[i].y != h[j].x + h[j].y {
return h[i].x + h[i].y < h[j].x + h[j].y
}
return h[i].y < h[j].y
}
func (h HeapPos) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h *HeapPos) Push(v interface{}) {
*h = append(*h, v.(*Pos))
}
func (h *HeapPos) Pop() interface{} {
ret := (*h)[len(*h) - 1]
*h = (*h)[: len(*h) - 1]
return ret
}
func findDiagonalOrder(nums [][]int) []int {
h := HeapPos{}
k := 0
for i := range nums {
k += len(nums[i])
for j := range nums[i] {
heap.Push(&h, &Pos{i, j, nums[i][j]})
}
}
res := make([]int, k)
k = 0
for len(h) > 0 {
pos := heap.Pop(&h).(*Pos)
res[k] = pos.v
k++
}
return res
}

复杂度分析
- 时间复杂度: O ( k l o g k ) O(klogk) O(klogk),其中k为元素的数量
- 空间复杂度: O ( k ) O(k) O(k)
方法二、哈希表
思路简述
还是基于每一轮遍历的点的xy坐标之和相等,使用一个哈希表,聚合每一轮遍历的元素在一个列表中。最后在遍历的时候,先对key值排序,然后在列表里,需要从后往前取数据。
代码实现
func findDiagonalOrder(nums [][]int) []int {
record := make(map[int][]int, 0)
m := len(nums)
n := 0
count := 0
for i := range nums {
n = max(n, len(nums[i]))
count += len(nums[i])
for j := range nums[i] {
if record[i + j] == nil {
record[i + j] = make([]int, 0)
}
record[i + j] = append(record[i+j], nums[i][j])
}
}
res := make([]int, count)
k := 0
for i := 0; i <= m + n - 2; i++ {
if arr, ok := record[i]; ok {
for j := len(arr) - 1; j >= 0; j-- {
res[k] = arr[j]
k++
}
}
}
return res
}

复杂度分析
- 时间复杂度: O ( k ) O(k) O(k),每一个元素只会被访问一次
- 空间复杂度: O ( k ) O(k) O(k)
方法三、广度优先搜索
思路简述
该题也可以使用bfs来做,将原题描述中的图在这里插入图片描述稍微倾斜一点来看,如下图,那么数组的遍历可以看成是一种层序遍历。
但是这种方法在实现的时候要注意,需要使用一个哈希表来去重,并且是在进入队列前去重,否则效率都是比较低的。
代码实现
type Pos struct {
x, y int
}
func findDiagonalOrder(nums [][]int) []int {
m := len(nums)
oldQueue := make([]*Pos, 0, m)
queue := make([]*Pos, 0, m)
oldQueue = append(oldQueue, &Pos{0, 0})
res := make([]int, 0, m)
visited := make(map[string]struct{})
visited["0-0"] = struct{}{}
for len(oldQueue) > 0 {
for i := 0; i < len(oldQueue); i++ {
pos := oldQueue[i]
res = append(res, nums[pos.x][pos.y])
key := fmt.Sprintf("%d-%d", pos.x + 1, pos.y)
_, ok := visited[key]
if pos.x + 1 < m && pos.y < len(nums[pos.x + 1]) && !ok {
queue = append(queue, &Pos{pos.x + 1, pos.y})
visited[key] = struct{}{}
}
key = fmt.Sprintf("%d-%d", pos.x, pos.y + 1)
_, ok = visited[key]
if pos.y + 1 < len(nums[pos.x]) && !ok {
queue = append(queue, &Pos{pos.x, pos.y + 1})
visited[key] = struct{}{}
}
}
oldQueue, queue = queue, oldQueue
queue = queue[0:0]
}
return res
}

复杂度分析
- 时间复杂度: O ( k ) O(k) O(k), 每个元素只会被访问一次
- 空间复杂度: O ( k ) O(k) O(k)
1014

被折叠的 条评论
为什么被折叠?



