结论在前
1、我做算法题的目的是加深一下数据结构与算法的知识
2、不是做题就有效果,里面数据结构的基础好重要,没有这些数据的认识,也就是缺少了这部分的认知,你想10年都不一定有效果
3、题目需要看懂,不要拿起题目就分析用什么动态规划,递归虽然是必要的,但读懂题目更清楚用什么数据结构与算法去解决而不是做到差不多又重来
4、一定一定要拿出纸笔来描述算法的过程,画图这个真的好重要,分析能力的体验,我当时做的时候以为真的好简单,之后做了下,发现有边界情况,然后打补丁,不断提交,不断补丁导致这个程序变成,以针对特殊情况去解决问题,这个真的好像我们写业务代码一样,没有模式可言
5、做不出来,就不要死扣了等,直接看答案吧,这种不是怀疑自己智力,当你看完答案会有醍醐灌顶的效果,不懂看多几次
6、形成套路,这种抽象总结不是要你背诵题目,而是这些本来是需要了解,总结,抽象,公式化的东西,大脑是懒的我们需要更聪明地节省时间
题思考
1、Two Sum
https://leetcode-cn.com/problems/two-sum/
给你一个数组,里面是整数,还有给你一个目标数,寻址两数相加等于目标数
我的思路:我突然就想找出有多少个组合,并没有看懂题意,事实里面可能有很多组合如,[-2,3,4,2,1] , 目标数位 5,这个时候,组合有 [3,2], [4,1] 我的思考就去了这里,题目说只是说找出,没有说找全部 因此总结,题没有总结
解法:
1、两个循环,nxn 地循环 找出等于目标数
2、哈希表,这个思想是用空间换时间,在循环里面找到由目(标数 – 循环当前的数) 得到 0 的key在哈希表里面存在就说明找到这两个数
func twoSum(nums []int, target int) []int {
hashTable := map[int]int{}
for i, x := range nums {
if p, ok := hashTable[target-x]; ok {
return []int{p, i}
}
hashTable[x] = i
}
return nil
}
2、二分查找法
https://leetcode-cn.com/problems/binary-search/
我的思考:这本来熟悉到不熟悉,但实在没有多用忘记的忘记,其实这个在真的在业务开发用的很少?其实真是,你说排行榜,基本上你用上redis,或者几个循环就搞定了,就不需要太多,但当没有这些可以满足你的时候,你可能会寻找解决方案,你会查看他们源码,基本是用上了这些标准数据结构库进行处理,无论怎样变基本的东西都在这里
这个题意没有,但是鉴于忘记了这个算法,需要补充基本知识
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
pivot := left + (right - left)
if nums[pivot] == target {
return pivot
}
if target < nums[pivot] {
right = pivot - 1
} else {
left = pivot + 1
}
}
return -1
}
3、278. 第一个错误的版本 https://leetcode-cn.com/problems/first-bad-version/
我的分析:题意没有理解,实例没有看明白,其实本地说的是调用内置函数,找出那几个版本是有问题的,这个就是考二分查找法
func firstBadVersion(n int) int {
left, right := 1, n;
for left < right { // 循环直至区间左右端点相同
mid := left + (right - left) / 2; // 防止计算时溢出
if (isBadVersion(mid)) {
right = mid; // 答案在区间 [left, mid] 中
} else {
left = mid + 1; // 答案在区间 [mid+1, right] 中
}
}
// 此时有 left == right,区间缩为一个点,即为答案
return left;
}
4、35. 搜索插入位置 https://leetcode-cn.com/problems/search-insert-position/
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。 输入: nums = [1,3,5,6], target = 5 输出: 2
我的分析:好像好简单,一个循环搞定,但是人家是给了一个循序数组给你,这个也是二分查找法最快,在工作上凡事需要查找某个值,建议还是先排好序,出了利用好算法外,还利用好数组里面的局部性,也就是加载到高速缓存上,更快给到CPU运算
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)-1
mid := left
for left <= right {
mid = left + (right-left)/2
fmt.Println(nums[mid], left, mid, right)
if nums[mid] == target {
return mid
} else if nums[mid] > target {
right = mid - 1
} else if nums[mid] < target {
left = mid + 1
}
}
if nums[mid] < target {
return mid + 1
} else {
return mid
}
}
4、977. 有序数组的平方 https://leetcode-cn.com/problems/squares-of-a-sorted-array/
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]
有多种解法
https://leetcode-cn.com/problems/squares-of-a-sorted-array/solution/you-xu-shu-zu-de-ping-fang-by-leetcode-solution/https://leetcode-cn.com/problems/squares-of-a-sorted-array/solution/dai-ma-sui-xiang-lu-shu-zu-ti-mu-zong-ji-1rtz/
方法一:直接排序
方法二:双指针
我的分析:我是直接循环求平方,在排序,但是效果不好, 时间复杂度:O(n) 空间复杂度:O(logn)
看了答案后,发现人家是通过借助变量在同一个循环里面,找最大最小值,题目里面已经说明 非递减顺序 排序的整数数组,通过左右指针移动,指向的平方后比对大小看看,那个大在放到某个数组里面
func sortedSquares(nums []int) []int {
n := len(nums)
ans := make([]int, n)
i, j := 0, n-1
for pos := n - 1; pos >= 0; pos-- {
if v, w := nums[i]*nums[i], nums[j]*nums[j]; v > w {
ans[pos] = v
i++
} else {
ans[pos] = w
j--
}
}
return ans
}
复杂度分析
时间复杂度:O(n),其中 nn 是数组 nums 的长度。
空间复杂度:O(1)。除了存储答案的数组以外,我们只需要维护常量空间。
5、189. 旋转数组 https://leetcode-cn.com/problems/rotate-array/
给定一个数组,将数组中的元素向右移动k个位置,其中k是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 你可以使用空间复杂度为O(1) 的原地算法解决这个问题吗?
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右旋转 1 步: [7,1,2,3,4,5,6] 向右旋转 2 步: [6,7,1,2,3,4,5] 向右旋转 3 步: [5,6,7,1,2,3,4]
我的分析:第一次看不明白题目,看多次,原来是循环移动,所以这里就会出现mod的计算,如上面的例子中,7移动由6到0,1由0到1,除了mod之后还需要临时变量保存做交换
func rotate(nums []int, k int) []int {
n := len(nums)
k %= n
for start, count := 0, gcd(k, n); start < count; start++ {
pre, cur := nums[start], start
// fmt.Println("1=1", pre, cur, start)
for ok := true; ok; ok = cur != start {
next := (cur + k) % n
nums[next], pre, cur = pre, nums[next], next
// fmt.Println("2=2", nums, next, pre, cur)
}
}
return nums
}
func gcd(a, b int) int {
for a != 0 {
a, b = b%a, a
}
return b
}
6、283. 移动零
https://leetcode-cn.com/problems/move-zeroes/
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明:
必须在原数组上操作,不能拷贝额外的数组。 尽量减少操作次数。
我的分析:无思路,直接看答案,得知用双指针能解决,思路为
7、344. 反转字符串 https://leetcode-cn.com/problems/reverse-string/
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
输入:[“h”,”e”,”l”,”l”,”o”] 输出:[“o”,”l”,”l”,”e”,”h”]
我的分析:双指针交换
func reverseString(s []byte) {
j := len(s) - 1
i := 0
for i < j {
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
}
}
8、557. 反转字符串中的单词 III
https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例:
输入:”Let’s take LeetCode contest” 输出:”s’teL ekat edoCteeL tsetnoc”
我的分析,循环每个字符直到遇到空格为止,然后根据这个位置倒序地把字符赋值变量,然后这个变量添加这个空格
func reverseWords(s string) string {
length := len(s)
ret := []byte{}
for i := 0; i < length; {
start := i
for i < length && s[i] != ' ' {
i++
}
for p := start; p < i; p++ {
ret = append(ret, s[start+i-1-p])
}
for i < length && s[i] == ' ' {
i++
ret = append(ret, ' ')
}
}
return string(ret)
}
9、876. 链表的中间结点
https://leetcode-cn.com/problems/middle-of-the-linked-list/
给定一个头结点为 head的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5] 输出:此列表中的结点 3 (序列化形式:[3,4,5]) 返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。 注意,我们返回了一个 ListNode 类型的对象 ans,这样: ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
我的分析,这个看似是知道数量取中值,但是这个是链表,需要把所有元素都循环一次,效率为O(N),空间为O(N)(单指针法可以节省)
我查看了答案后,发现原来有快慢指针,这个真的让我刮目双看,slow 一次走一步,fast 一次走两步,当 fast 到达链表的末尾时,slow 必然位于中间
func middleNode(head *ListNode) *ListNode {
slow := head;
fast := head;
for fast != nil && fast.Next != nil {
slow = slow.Next;
fast = fast.Next.Next;
}
return slow;
}
10、19. 删除链表的倒数第 N 个结点
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 进阶:你能尝试使用一趟扫描实现吗?
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
我的分析,链表一个特性是找一个元素需要遍历,关于顺序我们可以通过快慢指针实现,通过一个哑变量来定位那个元素
func removeNthFromEnd(head *ListNode, n int) *ListNode {
lenN := getNodesLen(head)
// fmt.Println(lenN)
dummy := &ListNode{0, head}
cur := dummy
for i:=0; i < lenN - n; i+=1 {
cur = cur.Next
}
// if cur.Next != nil {
cur.Next = cur.Next.Next
// } else {
// head = nil
// }
return dummy.Next
}
func getNodesLen(head *ListNode) int {
n:=0
for ; head !=nil; head = head.Next {
n+=1
}
return n
}
11、3. 无重复字符的最长子串
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
我的分析,我没有做出来,看完答案后,了解滑动窗口,由第一个字符开始,不断扩大自己的位数直到重复字符出现
func lengthOfLongestSubstring(s string) int {
// 哈希集合,记录每个字符是否出现过
m := map[byte]int{}
n := len(s)
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans := -1, 0
for i := 0; i < n; i++ {
if i != 0 {
// 左指针向右移动一格,移除一个字符
delete(m, s[i-1])
}
for rk + 1 < n && m[s[rk+1]] == 0 {
// 不断地移动右指针
m[s[rk+1]]++
rk++
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1)
}
return ans
}
func max(x, y int) int {
if x < y {
return y
}
return x
}
12、733. 图像渲染
https://leetcode-cn.com/problems/flood-fill/
有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。
给你一个坐标(sr, sc)表示图像渲染开始的像素值(行 ,列)和一个新的颜色值newColor,让你重新上色这幅图像。
为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。
最后返回经过上色渲染后的图像。
示例 1:
输入: image = [[1,1,1],[1,1,0],[1,0,1]] sr = 1, sc = 1, newColor = 2 输出: [[2,2,2],[2,2,0],[2,0,1]] 解析: 在图像的正中间,(坐标(sr,sc)=(1,1)), 在路径上所有符合条件的像素点的颜色都被更改成2。 注意,右下角的像素没有更改为2, 因为它不是在上下左右四个方向上与初始点相连的像素点。
func floodFill(image [][]int, sr int, sc int, newColor int) [][]int {
lenc := len(image[0])
lenr := len(image)
if sr > lenr-1 || sc > lenc-1 {
return image
}
centerColor := image[sr][sc]
if centerColor == newColor {
return image
}
// 上左下右顺序深度遍历
image[sr][sc] = newColor
// 记录遍历的点
queue := [][]int{{sr, sc}}
// fmt.Println(queue, image)
// 从rc rs 出发
for i := 0; i < len(queue); i++ {
pos := queue[i]
// 上
if pos[0]-1 >= 0 && image[pos[0]-1][pos[1]] == centerColor {
// fmt.Println("上")
queue = append(queue, []int{pos[0] - 1, pos[1]})
image[pos[0]-1][pos[1]] = newColor
}
// 右
if pos[1]+1 < lenc && image[pos[0]][pos[1]+1] == centerColor {
// fmt.Println("右")
queue = append(queue, []int{pos[0], pos[1] + 1})
image[pos[0]][pos[1]+1] = newColor
}
// 下
if pos[0]+1 < lenr && image[pos[0]+1][pos[1]] == centerColor {
// fmt.Println("下")
queue = append(queue, []int{pos[0] + 1, pos[1]})
image[pos[0]+1][pos[1]] = newColor
}
// 左
if pos[1]-1 >= 0 && image[pos[0]][pos[1]-1] == centerColor {
// fmt.Println("左")
queue = append(queue, []int{pos[0], pos[1] - 1})
image[pos[0]][pos[1]-1] = newColor
}
// fmt.Println(queue)
}
return image
}
13、617. 合并二叉树
https://leetcode-cn.com/problems/merge-two-binary-trees/
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
注意: 合并必须从两个树的根节点开始
我的分析,使用递归
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
if root1 == nil {
return root2
}
if root2 == nil {
return root1
}
root1.Val += root2.Val
root1.Left = mergeTrees(root1.Left, root2.Left)
root1.Right = mergeTrees(root1.Right, root2.Right)
return root1
}
14、116. 填充每个节点的下一个右侧节点指针
https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
进阶:
你只能使用常量级额外空间。 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
我的分析,广度遍历,进入下一个节点,把左右节点相连,然后在循环下一层在相连
func connect(root *Node) *Node {
if root == nil {
return root
}
leftNode := root
for leftNode.Left != nil {
node := leftNode
// 遍历层
for node != nil {
// 左右节点连接
node.Left.Next = node.Right
if node.Next != nil {
node.Right.Next = node.Next.Left
}
node = node.Next
}
leftNode = leftNode.Left
}
return root
}
15、21. 合并两个有序链表
https://leetcode-cn.com/problems/merge-two-sorted-lists/
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]
分析:递归方式,比对大小,停止条件
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
if l1 == nil {
return l2
}
if l2 == nil {
return l1
}
if l1.Val < l2.Val {
l1.Next = mergeTwoLists(l1.Next, l2)
return l1
} else {
l2.Next = mergeTwoLists(l1, l2.Next)
return l2
}
}
16、206. 反转链表
https://leetcode-cn.com/problems/reverse-linked-list/
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]输出:[5,4,3,2,1]
分析:递归,停止条件为这个节点的是否空或下一点为空
func reverseList(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
cur := reverseList(head.Next)
head.Next.Next = head
head.Next = nil
return cur
}
17、77. 组合
https://leetcode-cn.com/problems/combinations/
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2输出:[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4],]
分析,这题我做不到,这个需要用回溯算法
这个题解能够更好了解回溯算法,期重点是,找递归条件,还有一个出栈做回溯
func combine(n int, k int) [][]int {
res := [][]int{}
res = backtrack(n, k, 1, []int{}, res)
return res
}
func backtrack(n, k, start int, track []int, res [][]int) [][]int {
if len(track) == k {
tmp := make([]int, k)
copy(tmp, track)
res = append(res, tmp)
}
if len(track)+n-start+1 < k {
return res
}
for i := start; i <= n; i++ {
track = append(track, i)
res = backtrack(n, k, i+1, track, res)
track = track[:len(track)-1]
}
return res
}
18、784. 字母大小写全排列
https://leetcode-cn.com/problems/letter-case-permutation/
给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
示例: 输入:S = “a1b2” 输出:[“a1b2”, “a1B2”, “A1b2”, “A1B2”]
输入:S = “3z4” 输出:[“3z4”, “3Z4”]
输入:S = “12345” 输出:[“12345”]
分析:这个我完全做出来,但是不够简洁,看答案,二分掩码,递归
func letterCasePermutation(s string) []string {
slen := len(s)
letters := [][]byte{}
i := 0
for i < slen {
if check_is_letter(s[i]) {
lower := lower_letter(s[i])
upper := upper_letter(s[i])
// fmt.Println(string(lower), string(upper))
if len(letters) == 0 {
letters = [][]byte{{lower}, {upper}}
} else {
tmp := make([][]byte, len(letters))
copy(tmp, letters)
letters = [][]byte{}
for _, letter := range tmp {
letter = append(letter, lower)
tmpl := make([]byte, len(letter))
copy(tmpl, letter)
letters = append(letters, tmpl)
// fmt.Println("lower", string(tmpl))
}
for _, letter := range tmp {
letter = append(letter, upper)
tmpl := make([]byte, len(letter))
copy(tmpl, letter)
letters = append(letters, tmpl)
// fmt.Println("upper", string(letter))
}
// fmt.Println("letter:", letters)
}
} else {
if len(letters) == 0 {
letters = [][]byte{{s[i]}}
} else {
for j, letter := range letters {
letter = append(letter, s[i])
letters[j] = letter
// fmt.Println("not letter", string(letter))
}
}
}
i++
}
snew := []string{}
for i := 0; i < len(letters); i++ {
snew = append(snew, string(letters[i]))
}
return snew
}
func check_is_letter(s byte) bool {
if s >= 65 && s <= 90 || s >= 97 && s <= 122 {
return true
} else {
return false
}
}
func lower_letter(s byte) byte {
if s >= 65 && s <= 90 {
return byte(s + 32)
}
return s
}
func upper_letter(s byte) byte {
if s >= 97 && s <= 122 {
return byte(s - 32)
}
return s
}
19、70. 爬楼梯
https://leetcode-cn.com/problems/climbing-stairs/
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶 示例 2:
输入: 3 输出: 3 解释: 有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
分析:动态规划,矩阵快速幂。。这个对我来说比较新鲜,化了很多时间了解
https://leetcode-cn.com/problems/climbing-stairs/solution/pa-lou-ti-by-leetcode-solution/
func climbStairs(n int) int {
p, q, r := 0, 0, 1
for i := 1; i <= n; i++ {
p = q
q = r
r = p + q
}
return r
}
20、198. 打家劫舍
https://leetcode-cn.com/problems/house-robber/
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。 示例 2:
输入:[2,7,9,3,1] 输出:12 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。
分析:这条题目,好特别低介绍了如何对比两个前后指针变量总和来判断那个金额最高
func rob(nums []int) int {
if len(nums) == 0 {
return 0
}
if len(nums) == 1 {
return nums[0]
}
p := nums[0]
q := nums[1]
if p > q {
q = p
}
for i :=2; i < len(nums); i++ {
tmp := q
if p + nums[i] > q {
q = p + nums[i]
}
p = tmp
}
return q
}
后面的是位移的题目
231. 2 的幂
https://leetcode-cn.com/problems/power-of-two/
191. 位1的个数
https://leetcode-cn.com/problems/number-of-1-bits/
190. 颠倒二进制位
https://leetcode-cn.com/problems/reverse-bits/
136. 只出现一次的数字
https://leetcode-cn.com/problems/single-number/
都是强调位移的特性,并,与,异或,运算的逻辑,190. 颠倒二进制位 比较有难度
基本上做不出的
46. 全排列 https://leetcode-cn.com/problems/permutations/
567. 字符串的排列 https://leetcode-cn.com/problems/permutation-in-string/
后续迭代
后续需要加强点有4个
1、重温数据结构与常规算法,把基础打号
2、每天找相关的题目来看看是否掌握
3、看动态规划,还有回溯算法,这类虽然对于日常工作用的不多主要是场景问题,如果要你做一个排班的问题,这个回溯算法就很有别要了
4、多搜索看看这些算法数据结构的实际用途
资料
1、极客时间,王峥算法,下面是我推荐连接
2、代码随想录
https://space.bilibili.com/525438321?from=search&seid=10425904522036290503
3、有条件的看youtu