week3

                                                                                 #算法Week4

爬楼梯

题目描述:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶
    示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶
    来源:力扣(LeetCode)
    链接:https://leetcode.cn/problems/climbing-stairs
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
    思路:我们用 f(x)f(x) 表示爬到第 xx 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以列出如下式子:

f(x) = f(x - 1) + f(x - 2)
f(x)=f(x−1)+f(x−2)

它意味着爬到第 xx 级台阶的方案数是爬到第 x - 1x−1 级台阶的方案数和爬到第 x - 2x−2 级台阶的方案数的和。很好理解,因为每次只能爬 11 级或 22 级,所以 f(x)f(x) 只能从 f(x - 1)f(x−1) 和 f(x - 2)f(x−2) 转移过来,而这里要统计方案总数,我们就需要对这两项的贡献求和。

以上是动态规划的转移方程,下面我们来讨论边界条件。我们是从第 00 级开始爬的,所以从第 00 级爬到第 00 级我们可以看作只有一种方案,即 f(0) = 1f(0)=1;从第 00 级到第 11 级也只有一种方案,即爬一级,f(1) = 1f(1)=1。这两个作为边界条件就可以继续向后推导出第 nn 级的正确结果。我们不妨写几项来验证一下,根据转移方程得到 f(2) = 2f(2)=2,f(3) = 3f(3)=3,f(4) = 5f(4)=5,……,我们把这些情况都枚举出来,发现计算的结果是正确的。

我们不难通过转移方程和边界条件给出一个时间复杂度和空间复杂度都是 O(n)O(n) 的实现,但是由于这里的 f(x)f(x) 只和 f(x - 1)f(x−1) 与 f(x - 2)f(x−2) 有关,所以我们可以用「滚动数组思想」把空间复杂度优化成 O(1)O(1)。

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/climbing-stairs/solution/pa-lou-ti-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
代码:

package main
func climb(n int) int {
	p, q, r := 0, 0, 1
	for i := 1; i <= n; i++ {
		p = q
		q = r
		r = p + q
	}
	return r
}
func main() {
    print(climb(5))
}

运行截图:
在这里插入图片描述

package main
func climb(n int) int {
	p, q, r := 0, 0, 1
	for i := 1; i <= n; i++ {
		p = q
		q = r
		r = p + q
	}
	return r
}
func main() {
    print(climb(2))
}

运行截图:
在这里插入图片描述

只出现一次的数字

  题目描述:
  给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/single-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
本题解题思路的核心是用异或方法来解题。异或运算有以下三种性质:
在这里插入图片描述

   其中,第三条是这题的解题关键。因为题目中的数组元素只有一个数字出现了一次,其他数字都出现了两次,所以,所有数组元素进行异或运算后得到的结果就是那个只出现一次的数字。
   代码:
package main
func Onetime(nums []int) int{
	single := 0
	for _, num := range nums {
		single ^= num
	}
	return single
}
func main() {
	var arr1 = []int{1, 2,2, 3,3, 4,4, 5,5}
    print(Onetime(arr1))
}

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

package main
func Onetime(nums []int) int{
	single := 0
	for _, num := range nums {
		single ^= num
	}
	return single
}
func main() {
	var arr1 = []int{2, 3,3, 4,4, 7,7, 9,9}
    print(Onetime(arr1))
}

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

相同的树

题目描述:
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:p = [1,2,3], q = [1,2,3]
输出:true
示例 2:

输入:p = [1,2], q = [1,null,2]
输出:false
示例 3:

输入:p = [1,2,1], q = [1,1,2]
输出:false

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/same-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
采用深度优先的搜索方式。首先判断两个二叉树是否都为空,如果是,则两个二叉树是相同的。如果只有一个二叉树是空的,则两个二叉树一定不相同。如果两个二叉树都不是空的,则从根节点开始判断它们是否相等。这里就要用到递归的思想来解题。

func isSameTree(p *TreeNode, q *TreeNode) bool {
    if p == nil && q == nil {
        return true
    }//如果两个二叉树都为空,则两个二叉树是相同的
    if p == nil || q == nil {
        return false
    }//如果只有一个二叉树是空的,则它们一定不相同
    if p.Val != q.Val {
        return false
    }//判断根节点是否相同
    return isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)//利用递归的思想来判断子节点是否相同
}

搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-insert-position
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:
本题的解题思路是采用二分法来解题。虑这个插入的位置 \textit{pos}pos,它成立的条件为:
在这里插入图片描述
其中 \textit{nums}nums 代表排序数组。由于如果存在这个目标值,我们返回的索引也是 \textit{pos}pos,因此我们可以将两个条件合并得出最后的目标:「在一个有序数组中找第一个大于等于 \textit{target}target 的下标」。

问题转化到这里,直接套用二分法即可,即不断用二分法逼近查找第一个大于等于 \textit{target}target 的下标 。下文给出的代码是笔者习惯的二分写法,\textit{ans}ans 初值设置为数组长度可以省略边界条件的判断,因为存在一种情况是 \textit{target}target 大于数组中的所有数,此时需要插入到数组长度的位置。
代码:

package main

import "fmt"

func searchInsert(nums []int, target int) int {
	n := len(nums)
	left, right := 0, n - 1
	ans := n
	for left <= right {
		mid := (right - left) >> 1 + left
		if target <= nums[mid] {
			ans = mid
			right = mid - 1
		} else {
			left = mid + 1
		}
	}
	return ans
}
func main() {
	var arr=[]int{1,2,3,4,6}
 fmt.Println(searchInsert(arr,5))

}

运行截图:
在这里插入图片描述

package main

import "fmt"

func searchInsert(nums []int, target int) int {
	n := len(nums)
	left, right := 0, n - 1
	ans := n
	for left <= right {
		mid := (right - left) >> 1 + left
		if target <= nums[mid] {
			ans = mid
			right = mid - 1
		} else {
			left = mid + 1
		}
	}
	return ans
}
func main() {
	var arr=[]int{3,4,5,7,9}
 fmt.Println(searchInsert(arr,6))

}

在这里插入图片描述

加一

题目描述:
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:

输入:digits = [0]
输出:[1]

  解题思路:

1.如果数组中元素有9的,将它置为0,并且前一位元素的数值加1.
2.如果最后一位不是9,则将其元素值加1即可。
3.如果元素值都是9,则将其全都置为0,并在首位加一个元素1。
们只需要对数组digits 进行一次逆序遍历,找出第一个不为 99 的元素,将其加一并将后续所有元素置零即可。如果 digits 中所有的元素均为 99,那么对应着思路部分的第三种情况,我们需要返回一个新的数组。
代码:

package main

import "fmt"

func plusOne(digits []int) []int {
	n := len(digits)
	for i := n - 1; i >= 0; i-- {
		if digits[i] != 9 {
			digits[i]++
			for j := i + 1; j < n; j++ {
				digits[j] = 0
			}
			return digits
		}
	}
	// digits 中所有的元素均为 9

	digits = make([]int, n+1)
	digits[0] = 1
	return digits
}
func main() {
	var arr=[]int{1,2,3,4,5}
	fmt.Println(plusOne(arr))
}

效果截图:
在这里插入图片描述

package main

import "fmt"

func plusOne(digits []int) []int {
	n := len(digits)
	for i := n - 1; i >= 0; i-- {
		if digits[i] != 9 {
			digits[i]++
			for j := i + 1; j < n; j++ {
				digits[j] = 0
			}
			return digits
		}
	}
	// digits 中所有的元素均为 9

	digits = make([]int, n+1)
	digits[0] = 1
	return digits
}
func main() {
	var arr=[]int{1,2,3,4,9}
	fmt.Println(plusOne(arr))
}

运行截图:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值