算法编程题-对角线遍历 II

原题描述

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

方法一、优先级队列

思路简述

看到这一道题目,顺其自然地想到按照图中的规律老老实实地进行遍历,但这种方法的时间复杂度高达 O ( m ∗ n ) O(m * n) O(mn),考虑到题目给出的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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值