Leetcode-2021.03记录-part2

三月刷题记录-part2

此博文为个人学习笔记,记录2021年3月Leetcode每日一题刷题记录

54. 螺旋矩阵

题目链接

描述

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例

示例1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

提示

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 10
  • -100 <= matrix [i][j] <= 100

notes

算法题一定要留意提示信息
像这题,m和n的范围都给定的,就不需要做matrix的非空判断,m,n的有效性检查等无效判断了
另外,每个值的范围给定了也是有用的。我的方法就可以利用到这一个条件

分析

螺旋矩阵其实是蛇形矩阵系列题之一,他的遍历不像常规的正序遍历,涉及到"转弯"的一个问题。

  1. 那么就以这个"转弯"作为切入点,设置四个方向坐标
    right: 表示向右,其他类推
    right = {0,1},down = {1,0}, left = {0,-1}, up = {-1, 0}
  2. 遍历过的坐标设置值为101,利用提示给定的信息 范围-100—100
    那么,后续就可以在遍历时判断坐标是否走过了
    能够理解这个方向变化,剩下的看代码注释就好

代码

    func spiralOrder(matrix [][]int) []int {
    	row := len(matrix)
    	col := len(matrix[0])
        // 返回结果
    	ret := make([]int, row*col)
    	// 定义方向
    	// right = {0,1},down = {1,0}, left = {0,-1}, up = {-1, 0}
    	// directs 是上述的方向集
    	directs := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
    	// 初始向右,定义方向索引p 为0
    	p := 0
    	direct := directs[p]
    	// m, n为遍历matrix的行列指针
    	m := 0
    	n := 0
        // 行列范围提示已经给定了,我们这里可以直接对第一个进行赋值
    	ret[0] = matrix[0][0]
        matrix[0][0] = 101
        // flag用来判断转方向后是否发生了移动
        // 每转一次方向,flag变为false,移动一次,flag变为true
        // 如果连续转了两次方向,那么说明遍历结束了
    	flag := true
    	for i := 1; i < len(ret); {
    		// m, n移动
    		m += direct[0]
    		n += direct[1]
    		// 如果当前值为101,说明遍历过了,==row,==col 说明到达边界了,转方向
    		if m == row || n == col || m < 0 || n < 0 || matrix[m][n] == 101 {
    			// 当前位置不对,退一步
    			m -= direct[0]
    			n -= direct[1]
    			// 退一步之后判断flag,为true那么转方向继续
    			if flag {
    				p++
    				p = p % 4
    				direct = directs[p]
    				flag = false
    				continue
    			}
    			// 如果flag 为false,那么就是连续发生了两次转向,未移动,那么说明遍历结束了
    			return ret
    		}
            // 说明是有效移动,将值放入ret,i++,继续循环遍历
    		ret[i] = matrix[m][n]
    		i++
    		matrix[m][n] = 101
    		// flag = true,表示有移动
    		flag = true
    	}
    	return ret
    }

执行结果
由于定义了方向矩阵,存在部分空间消耗

59. 螺旋矩阵Ⅱ

题目链接

描述

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

示例

示例1:

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例2:

输入:n = 1
输出:[[1]]

提示

  • 1 <= n <= 20

分析

  1. 首先,看提示:n的范围给定了1到20,所以不必在进行有效性的判定
  2. 此题和第54题是类似题,都是蛇形矩阵相关题
  3. 区别在于54题是取值,本题是放值,直接将54的代码进行修改就能满足需求

代码

func generateMatrix(n int) [][]int {
	// 初始化
	ret := make([][]int, n)
	for i := 0; i < n; i++ {
		ret[i] = make([]int, n)
	}
	// flag用来判断转方向后是否发生了移动,如果连续转了两次方向,那么说明遍历结束了
	flag := true
	// 定义方向
	// right = {0,1},down = {1,0}, left = {0,-1}, up = {-1, 0}
	// directs 是上述的方向集
	directs := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
    // aim表示要放的目标个数
	aim := n * n
	// 初始向右,定义方向索引p 为0
	p := 0
	direct := directs[p]
	// x, y为遍历matrix的行列指针
	x := 0
	y := 0
	ret[0][0] = 1
	for i := 1; i < aim; {
		// m, n移动
		x += direct[0]
		y += direct[1]
		// 如果当前值为101,说明遍历过了,==row,==col 说明到达边界了,转方向
		if x == n || y == n || x < 0 || y < 0 || ret[x][y] != 0 {
			// 当前位置不对,退一步
			x -= direct[0]
			y -= direct[1]
			// 退一步之后判断flag,为true那么转方向继续
			if flag {
				p++
				p = p % 4
				direct = directs[p]
				flag = false
				continue
			}
			// 如果flag 为false,那么就是连续发生了两次转向,未移动,那么说明遍历结束了
			return ret
		}
		ret[x][y] = i+1
		i++
		// flag = true,表示有移动
		flag = true
	}
	return ret
}

提交结果

蛇形矩阵可以使用不断缩小循环范围的方式去遍历,不设置方向,这样可以在空间上也得到节省。

190.颠倒二进制位

题目链接
分析见代码注释
代码

func reverseBits(num uint32) uint32 {
	// 定义返回值类型,赋初值为0
	var ret uint32
	ret = 0
	// uint32,循环32次拿到每一位
	for i := 0; i < 32; i++ {
		// 将ret左移1位
		ret <<= 1
		// 如果当前num末位为1,那么ret+1
		if num&1 == 1 {
			ret += 1
		}
		// num右移1位
		num >>= 1
	}
	return ret
}

运行结果
在这里插入图片描述

61.旋转链表

题解看代码注释

方式一

不借助额外空间,对k取模,移动有限次 核心在于首尾相连+断链

代码

func rotateRight(head *ListNode, k int) *ListNode {
	if head == nil {
		return head
	}
	p := head
	// 节点总数
	size := 1
	for p.Next != nil {
		p = p.Next
		size++
	}
	// 得到最终要移动的次数
	k %= size
	// 链首尾相连
	p.Next = head
	// 记录当前的p: 最后一个位置
	last := p
	p = head
	// 移动值 k次
	for i := 0; i < k; i++ {
		cur := p.Val
		nextVal := p.Next.Val
		for j := 0; j < size; j++ {
			p.Next.Val = cur
			p = p.Next
			cur = nextVal
			nextVal = p.Next.Val
		}
	}
	// 断链
	last.Next = nil
	return head
}

运行结果

方式二

借助额外的切片存储所有的节点值,核心在于记录初始位置,可以直接得到移动k步后的值,性能理论上来说或许更快

代码

func rotateRight(head *ListNode, k int) *ListNode {
	if head == nil {
		return head
	}
	p := head
	// 存储各位置的初始节点
	vals := []int{p.Val}
	for p.Next != nil {
		p = p.Next
		vals = append(vals, p.Val)
	}
	// 得到最终要移动的次数
	k %= len(vals)
	p = head
	for i := 0; i < len(vals); i++ {
		// 当前值来源于当前值-k
		p.Val = vals[(len(vals)-k+i) %len(vals)]
		p = p.Next
	}
	return head
}

运行结果

82.删除排序链表中的重复元素

func deleteDuplicates(head *ListNode) *ListNode {
	first := &ListNode{
		-1, head,
	}
	p := head
	pre := first
	// 判断当前值是不是连续相同值, true表示不连续
	var flag bool
	for p != nil {
		flag = true
		for p.Next != nil && p.Val == p.Next.Val {
			// 如果连续相同,那么flag变为false
			flag = false
			p = p.Next
		}
		// 如果是不同值
		if flag {
			// true表示非连续相同,那么值需要保留,追加到pre后
			pre.Next = p
			pre = pre.Next
		}
		p = p.Next
	}
	pre.Next = nil
	return first.Next
}

在这里插入图片描述

456.132模式

代码

// 简易stack
type stack struct {
	values []int
	top int
}
func find132pattern(nums []int) bool {
	/*
		解题分析:
		nums需满足索引:
		i < j < k 时, ai < ak < aj
		分析:
		1. 有效性:若nums.length<3,一定不满足
		2. 由最后一个元素开始进行判断:
			首先寻找 j < k时, ak < aj的关系
			可以想到通过栈结构来维护, 不断将 nums[i] 入栈,如果存在一个 ak<aj的关系,那么存储ak,再将aj入栈
	*/
	// 有效性判断
	if len(nums) < 3 {
		return false
	}
	// 初始化ak, stack
	// ak初始 = min,只有当存在 之前出现的值 < 之后出现的值即 ak < aj时, 才给ak赋上元素值
	ak, stack := math.MinInt32, stack{make([]int, len(nums)), -1}
	// 循环判断
	for i := len(nums) - 1; i >= 0; i-- {
		// 往前遍历发现nums[i] < ak时,说明 存在 i < j < k, ai < ak < aj了
		if nums[i] < ak {
			return true
		}
		// 如果栈不为空, 且当前值>栈顶值,那么就说明 ak<aj存在了 栈里面的值是之前出现过的
		// 循环给ak赋值,保证 ak是当前满足关系的 ak最大值
		for stack.top > -1 && nums[i] > stack.values[stack.top]{
			ak = stack.values[stack.top]
			stack.top--
		}
		// 循环结束以后,将当前值作为 aj入栈
		stack.top++
		stack.values[stack.top] = nums[i]
	}
	// 如果遍历完不满足关系,那么返回false
	return false
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值