代码随想录算法训练营第二十九天 | Leetcode随机抽题检测

Leetcode随机抽题检测

  • 160 相交链表
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 206 反转链表
  • 一段用于复制的标题
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 234 回文链表
    • 未看解答自己编写的青春版
    • 重点
      • 综上,利用快慢指针找寻链表中间,就按加入虚拟头的方法写。
    • 题解的代码
    • 日后再次复习重新写
  • 141 环形链表
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 142 环形链表 II
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 21 合并两个有序链表
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 2 两数相加
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 19 删除链表的倒数第 N 个结点
    • 未看解答自己编写的青春版
    • 重点
      • 上面的代码其实有些冗余了,这道题只要能想到加入虚拟头,上面说的两种特殊情况都能迎刃而解,用一般的示例来考虑就能通过。卡哥的代码中,是先让 fast 移动 n+1 步,我的是先移动 n 步,都可以,不同的对用体现在while判断条件上。
    • 题解的代码
    • 日后再次复习重新写
  • 24 两两交换链表中的节点
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 25 K 个一组翻转链表
    • 未看解答自己编写的青春版
    • 重点
      • 上面那个评论的方法真厉害,我悟了,第一次接触这种方式来翻转链表的,但是出人意料的很适合这道题!之前做的反转链表的方法,不能直接用到本题上,想使用就要像我那样去写,去谨慎的赋值。这种翻转的思想是:一次交换两个节点,且保持 pre 不变。例如:0--1--2--3--4,pre在0处,不参与翻转,cur在1处。那么第一次操作,是:0--2--1--3--4,此时pre还在0,cur也还在1,继续:0--3--2--1--4,继续:0--4--3--2--1,它是每次都将后面的一个值,插入到pre的后面,pre不移动,cur也不移动,但是cur的相对位置,会随着链表的更改而移动。
    • 题解的代码
    • 日后再次复习重新写
  • 138 复制带随机指针的链表
    • 未看解答自己编写的青春版
    • 重点
      • 通过对上面两种方法的学习,发现了这道题的本质,就是在利用next建立新链表的时候,利用一个map,保存好原节点和新节点的对应关系就好了!本质上是考哈希!
      • 另一种非常牛的方法,随机指针复制+拆分
      • 这题太牛了,一定要着重复习后面,这种思想第一次接触。
    • 题解的代码
    • 日后再次复习重新写
  • 148 排序链表
    • 未看解答自己编写的青春版
    • 重点,这道题太重要了太重要了,一定要多次复习。
      • 归并排序,是对链表排序最好的方法
      • 一定要好好学习归并排序,这是可以作为模板代码来学习的!本质上就是两个操作,merge 和 cut 。从网上搜到的很多归并排序都是用递归写的,不过本题使用循环来写,觉得这种写法很值得学习!
      • 归并排序的递归和循环写法,都值得学习!
      • 快速排序版本:是交换节点的,并非只交换数值。(暂时没看)
    • 题解的代码
    • 日后再次复习重新写
  • 23 合并 K 个升序链表
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 146 LRU 缓存
    • 未看解答自己编写的青春版
    • 重点
      • 这道双向链表的题目,真的学习了,后面一定要多复习多重写这道题!
    • 题解的代码
    • 日后再次复习重新写
  • 94 二叉树的中序遍历
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 104 二叉树的最大深度
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 226 翻转二叉树
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 101 对称二叉树
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 543 二叉树的直径
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 102 二叉树的层序遍历
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 108 将有序数组转换为二叉搜索树
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 98 验证二叉搜索树
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 230 二叉搜索树中第K小的元素
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 199 二叉树的右视图
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 一段用于复制的标题
    • 未看解答自己编写的青春版
    • 重点
      • 额外空间复杂度为O(1)的思想!这道题我被题目骗了,题目说顺序应该是前序遍历,我就想着递归必须用前序遍历,但是前序遍历的问题是:会提前修改掉后面要进入递归的值。那么,链表,从上到下连接可以,我从下到上,反向连接,也可以啊!而这一点,利用递归的回溯特性,可以很自然地做到。假如 last 承载了下一层递归的返回值,而本层递归的root为last的上一个,只需要赋值:root.right=last,就可以了!
      • 所以后序遍历!后序遍历,也就不会有,还未进入递归,值就被修改的问题!这题的思路太妙了!
      • 思路很妙,可以多写多复习。
    • 题解的代码
    • 日后再次复习重新写
  • 105 从前序与中序遍历序列构造二叉树
    • 未看解答自己编写的青春版
      • 厉害!第一次自己尝试,写出了传入左右索引版的代码,在空间占用上,要比上面的代码低不少。
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 437 路径总和 III
    • 未看解答自己编写的青春版
    • 重点
      • 没来得及体会,日后再看,重点学习这道题。
    • 题解的代码
    • 日后再次复习重新写
  • 236 二叉树的最近公共祖先
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 124 二叉树中的最大路径和
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写

160 相交链表

未看解答自己编写的青春版

class Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:curA = headAcurB = headBcountA = 0countB = 0while curA :countA += 1curA = curA.nextwhile curB :countB += 1curB = curB.nextif countA < countB :countA,countB = countB,countAheadA,headB = headB,headAdiff = countA - countBwhile diff > 0 :headA = headA.nextdiff -= 1while headA :if headA == headB :return headBelse :headA = headA.nextheadB = headB.nextreturn None

重点

过。

题解的代码

206 反转链表

一段用于复制的标题

未看解答自己编写的青春版

class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:pre = Nonecur = headwhile cur :temp = cur.nextcur.next = prepre = curcur = tempreturn pre

重点

过。

题解的代码

日后再次复习重新写

234 回文链表

未看解答自己编写的青春版

class Solution:def isPalindrome(self, head: Optional[ListNode]) -> bool:dummy_head = ListNode(0,head)slow = fast = dummy_head# 这里要注意,快慢指针找中间,要加虚拟头才是对的,不加虚拟头不对!# 自己举例一下就发现了。while fast and fast.next :slow = slow.nextfast = fast.next.nextcur = slow.nextslow.next = Nonepre = Nonewhile cur :temp = cur.nextcur.next = prepre = curcur = tempwhile pre and head :if pre.val != head.val :return Falsepre = pre.nexthead = head.nextreturn True

重点

通过编写这道题的代码,发现了一个很重要的点,如果要用快慢指针法,寻找链表中点,最严格的方式是:要加入虚拟头!

加入虚拟头的代码:

		dummy_head = ListNode(0,head)slow = fast = dummy_headwhile fast and fast.next :slow = slow.nextfast = fast.next.nextcur = slow.nextslow.next = None # 切断操作

不加入虚拟头的代码:

		slow = fast = headwhile fast and fast.next :slow = slow.nextfast = fast.next.nextcur = slow.nextslow.next = None # 切断操作

下面用A代表慢指针,B代表快指针。

比如:1-2-3-4 ,不加入虚拟头时,一开始A,B均在 1 ,移动一步,A在2,B在3,此时还要继续移动,while不退出,继续移动,A在3,B为None,while退出,此时让A.next = None,做切断操作,这明显是错误的!应该是从 2 切断!加入虚拟头后,就是正确的结果。

加入虚拟头,可以让偶数个数的链表切割正确,奇数个数的链表切割结果不变,因为奇数个数的链表,正确切割结果,就是左边要包括中间节点,然后中间节点的next为None。

那么之前有一道题:143 重排链表,为什么卡哥的代码,也是快慢指针找中间,却没有加入虚拟头?
143 重排链表 卡哥的代码:

class Solution:def reorderList(self, head: Optional[ListNode]) -> None:"""Do not return anything, modify head in-place instead."""fast = slow = head# find mid point which including (first) mid point into the first half linked listwhile fast and fast.next:fast = fast.next.nextslow = slow.next# 下面两句代码别忘了,一个是取右半边,这样取的右半边一定是短的那一方# 这样连接也符合题目要求right = slow.next # 获取后半边的头slow.next = None # 切断!这句话很重要,不然就成环了node = Nonewhile right:temp = right.nextright.next = nodenode = rightright = temphead2 = nodehead1 = headwhile head1 and head2:temp1 = head1.nexttemp2 = head2.nexthead1.next = head2head2.next = temp1head1 = temp1head2 = temp2

在这里插入图片描述
因为这道题目的特殊性!可以看出,从2处切割,本题的节点串联逻辑是:1 – 4 – 2 – 3 。从3处切割,本题的节点串联逻辑是:1 – 4 – ( 2 3 ) ,( 2 3 ) 本来就在左子串中,无需改变位置!这是这道题的特殊性!对于偶数个数的链表,中间两个点,切不切割都一样!因为这两个点的顺序不需要颠倒。

综上,利用快慢指针找寻链表中间,就按加入虚拟头的方法写。

题解的代码

日后再次复习重新写

141 环形链表

未看解答自己编写的青春版

class Solution:def hasCycle(self, head: Optional[ListNode]) -> bool:if head == None or head.next == None :return Falseslow = fast = headwhile fast and fast.next : slow = slow.nextfast = fast.next.next# 注意判断的位置,先移动再判断,不然因为初始化都是头结点,会立刻返回Trueif slow == fast :return Truereturn False

重点

在使用快慢指针时,注意 if 逻辑判断的位置。

注意!注意!警告!警告!上面代码中的提前判断,不是必须的。可以不加。

class Solution:def hasCycle(self, head: Optional[ListNode]) -> bool:slow = fast = headwhile fast and fast.next : slow = slow.nextfast = fast.next.next# 注意判断的位置,先移动再判断,不然因为初始化都是头结点,会立刻返回Trueif slow == fast :return Truereturn False

题解的代码

日后再次复习重新写

142 环形链表 II

未看解答自己编写的青春版

class Solution:def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:slow = headfast = headwhile fast and fast.next :slow = slow.nextfast = fast.next.nextif slow == fast :cur = headwhile True :if cur == slow :return curcur = cur.nextslow = slow.nextreturn None

重点

过,这道题的理论基础,去复习,卡哥的解答。

环形链表 II

题解的代码

日后再次复习重新写

21 合并两个有序链表

未看解答自己编写的青春版

class Solution:def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:newhead = ListNode()res = newheadwhile list1 and list2 :if list1.val >= list2.val :newhead.next = list2list2 = list2.next     else :newhead.next = list1list1 = list1.nextnewhead = newhead.nextif list1 == None and list2 != None :newhead.next = list2elif list1 != None and list2 == None :newhead.next = list1return res.next

重点

过。

题解的代码

日后再次复习重新写

2 两数相加

未看解答自己编写的青春版

class Solution:def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:reshead = ListNode()cur = resheadpre = 0while l1 or l2 :if l1 == None :temp = pre+l2.valif temp > 9 :pre = 1temp = temp % 10else :pre = 0node = ListNode(temp)l2 = l2.nextelif l2 == None :temp = pre+l1.valif temp > 9 :pre = 1temp = temp % 10else :pre = 0node = ListNode(temp)l1 = l1.nextelse :temp = pre+l2.val+l1.valif temp > 9 :pre = 1temp = temp % 10else :pre = 0node = ListNode(temp)l1 = l1.nextl2 = l2.nextcur.next = nodecur = cur.nextif pre == 1 :node = ListNode(1)cur.next = nodereturn reshead.next

重点

过。

题解的代码

日后再次复习重新写

19 删除链表的倒数第 N 个结点

未看解答自己编写的青春版

class Solution:def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:dummy_head = ListNode(0,head)slow = fast = dummy_headwhile n > 0 :fast = fast.nextn -= 1while fast and fast.next :fast = fast.nextslow = slow.nextslow.next = slow.next.nextreturn dummy_head.next

重点

这道题就明确两点:

1、加入虚拟头。

2、模拟特殊情况检验代码是否正确:删除的是最后一个节点,删除的是第一个节点。

上面的代码其实有些冗余了,这道题只要能想到加入虚拟头,上面说的两种特殊情况都能迎刃而解,用一般的示例来考虑就能通过。卡哥的代码中,是先让 fast 移动 n+1 步,我的是先移动 n 步,都可以,不同的对用体现在while判断条件上。

移动 n+1 步 :

class Solution:def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:dummy_head = ListNode(0,head)slow = fast = dummy_headwhile n > -1 :fast = fast.nextn -= 1while fast :fast = fast.nextslow = slow.nextslow.next = slow.next.nextreturn dummy_head.next

移动 n 步 :

class Solution:def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:dummy_head = ListNode(0,head)slow = fast = dummy_headwhile n > 0 :fast = fast.nextn -= 1while fast.next :fast = fast.nextslow = slow.nextslow.next = slow.next.nextreturn dummy_head.next

题解的代码

日后再次复习重新写

24 两两交换链表中的节点

未看解答自己编写的青春版

class Solution:def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:if head == None or head.next == None :return headdummy_head = ListNode(0,head)pre = dummy_headcur = headwhile cur and cur.next :temp1 = cur.nexttemp2 = cur.next.nextpre.next = temp1temp1.next = curcur.next = temp2pre = curcur = cur.nextreturn dummy_head.next

重点

理清楚while循环中的交换逻辑就好,逻辑弄不清楚,就多搞一个临时变量嘛,两个temp,逻辑不就非常清晰。

这道题为了交换方便,同样也需要用虚拟头。

题解的代码

日后再次复习重新写

25 K 个一组翻转链表

未看解答自己编写的青春版

哈哈哈,独立完成 hard 题 !不过看评论,很多人也做出来了,看来这道题很简单,也就是中等的实际难度。

耗时上也还行,40% 左右。

class Solution:def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:dummy_head = ListNode(0,head)pre = dummy_headbegin = end = dummy_head.nextcount = 1while end :if count < k :end = end.nextcount += 1else :temp = end.nexta,b = self.reverse(begin,end)pre.next = bpre = aa.next = tempbegin = end = tempcount = 1return dummy_head.nextdef reverse(self,head,end):end.next = Nonepre = Nonecur = headwhile cur :temp = cur.nextcur.next = prepre = curcur = tempreturn head,end

重点

评论中的一个解答:

class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode dummy = new ListNode(0), prev = dummy, curr = head, temp;dummy.next = head;int length = 0;while(head != null) {length++;head = head.next;}head = dummy.next;for(int i = 0; i < length / k; i++) {for(int j = 0; j < k - 1; j++) {temp = curr.next;curr.next = temp.next;temp.next = prev.next;prev.next = temp;}prev = curr;curr = prev.next;}return dummy.next;}
}

上面那个评论的方法真厉害,我悟了,第一次接触这种方式来翻转链表的,但是出人意料的很适合这道题!之前做的反转链表的方法,不能直接用到本题上,想使用就要像我那样去写,去谨慎的赋值。这种翻转的思想是:一次交换两个节点,且保持 pre 不变。例如:0–1–2–3–4,pre在0处,不参与翻转,cur在1处。那么第一次操作,是:0–2–1–3–4,此时pre还在0,cur也还在1,继续:0–3–2–1–4,继续:0–4–3–2–1,它是每次都将后面的一个值,插入到pre的后面,pre不移动,cur也不移动,但是cur的相对位置,会随着链表的更改而移动。

想清楚,节点的移动逻辑,再谨慎地编写每次循环中的逻辑就可以了。

class Solution:def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:dummy_head = ListNode(0,head)pre = dummy_headcur = dummy_head.nextaa = dummy_head.nextcount = 0while aa :count += 1aa = aa.nextfor i in range(count//k):for j in range(k-1):# 这里的四个赋值,顺序非常有讲究!temp = cur.nextcur.next = temp.nexttemp.next = pre.nextpre.next = temppre = curcur = cur.nextreturn dummy_head.next

题解的代码

日后再次复习重新写

138 复制带随机指针的链表

未看解答自己编写的青春版

思想很朴素,每次都从头搜索,random节点,所以耗时也只打败了6%。去学习一下评论区。

class Solution:def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':reshead = Node(0)copycur = resheadcur = headwhile cur :node = Node(cur.val)copycur.next = nodecopycur = copycur.nextcur = cur.nextcopycur = reshead.nextcur = headwhile cur :if cur.random == None :copycur.random == None  else :temp_org = headtemp_copy = reshead.nextwhile temp_org != cur.random :temp_org = temp_org.nexttemp_copy = temp_copy.nextcopycur.random = temp_copycur = cur.nextcopycur = copycur.nextreturn reshead.next

重点

评论中的一个,递归的解法:很有意思。当然这种方法相当于记录了该链表的所有节点,空间复杂度是O(n),虽然是递归,但是这道题的时间复杂度也是O(n)。

class Solution:def copyRandomList(self, head):def copyNode(node, res):if not node: return Noneif node in res: return res[node]copy = Node(node.val, None, None)res[node] = copycopy.next = copyNode(node.next, res)copy.random = copyNode(node.random, res)return copyreturn copyNode(head, {})

另一种解法,哈希,利用字典去记录,原节点和新节点的映射关系。使用hash存储原结点和克隆结点的映射关系,通过映射关系处理克隆结点的random指针。

时间复杂度和空间复杂度,均为O(n)

class Solution {public Node copyRandomList(Node head) {if(head == null){return head;}// map方法,空间复杂度O(n)Node node = head;// 使用hash表存储旧结点和新结点的映射Map<Node,Node> map = new HashMap<>();while(node != null){Node clone = new Node(node.val,null,null);map.put(node,clone);node = node.next;}node = head;while(node != null){map.get(node).next = map.get(node.next);map.get(node).random = map.get(node.random);node = node.next;}return map.get(head);}
}

通过对上面两种方法的学习,发现了这道题的本质,就是在利用next建立新链表的时候,利用一个map,保存好原节点和新节点的对应关系就好了!本质上是考哈希!

另一种非常牛的方法,随机指针复制+拆分

原地处理,将克隆结点放在原结点后面,在原链表上处理克隆结点的random指针,最后分离两个链表,空间复杂度O(1)。

class Solution {public Node copyRandomList(Node head) {if(head == null){return head;}// 空间复杂度O(1),将克隆结点放在原结点后面Node node = head;// 1->2->3  ==>  1->1'->2->2'->3->3'while(node != null){Node clone = new Node(node.val,node.next,null);Node temp = node.next;node.next = clone;node = temp;}// 处理random指针node = head;while(node != null){// !!node.next.random = node.random == null ? null : node.random.next;node = node.next.next;}// 还原原始链表,即分离原链表和克隆链表node = head;Node cloneHead = head.next;while(node.next != null){Node temp = node.next;node.next = node.next.next;node = temp;}return cloneHead;}
}

一篇有助于理解的题解链接:

清楚的题解

这题太牛了,一定要着重复习后面,这种思想第一次接触。

题解的代码

日后再次复习重新写

哈希方法复写:

class Solution:def copyRandomList(self, head):reshead = Node(0)copycur = resheadcur = headtable = {}while cur :node = Node(cur.val)copycur.next = nodecopycur = copycur.nexttable[cur] = copycurcur = cur.nextcopycur = reshead.nextcur = headwhile cur :if cur.random == None :copycur.random == None  else :copycur.random = table[cur.random]cur = cur.nextcopycur = copycur.nextreturn reshead.next

148 排序链表

未看解答自己编写的青春版

先遍历得到数组,再排序,用一个字典存储:数组排序后的下标(key)和链表中的节点(value)的映射,然后按照key,从小到大去索引原节点,来构造新链表。这是时间复杂度为O(n logn),空间复杂度为O(n)的做法。

速度很快,打败95%,但是空间上只打败了6%

class Solution:def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:table = {}nums = []cur = headwhile cur :# 处理值相同的情况if cur.val in table :# 这里要直接 append, 不能采用赋值操作:table[cur.val] = table[cur.val].append(cur)# 因为 table[cur.val].append(cur) 的返回值是None# 下面的 pop 操作同理table[cur.val].append(cur)else :table[cur.val] = [cur]nums.append(cur.val)cur = cur.nextnums.sort()reshead = ListNode()cur = resheadfor i in nums:# 注意这里,要从最末尾开始取,因为pop就是丢掉末尾的node = table[i][-1]  table[i].pop()cur.next = nodecur = cur.nextcur.next = Nonereturn reshead.next

要使用O(1)的空间复杂度,必然是对链表直接进行排序操作,对链表的排序还从来没写过,采用哪种方式好也不清楚,冒泡肯定不行,是O(n^2),看题解说是,归并排序,归并排序是最适合链表这种数据结构的排序方式。快速排序也可以。

重点,这道题太重要了太重要了,一定要多次复习。

归并排序,是对链表排序最好的方法

在这里插入图片描述
伪代码:

current = dummy.next;
tail = dummy;
for (step = 1; step < length; step *= 2) {while (current) {// left->@->@->@->@->@->@->nullleft = current;// left->@->@->null   right->@->@->@->@->nullright = cut(current, step); // 将 current 切掉前 step 个头切下来。// left->@->@->null   right->@->@->null   current->@->@->nullcurrent = cut(right, step); // 将 right 切掉前 step 个头切下来。// dummy.next -> @->@->@->@->null,最后一个节点是 tail,始终记录//                        ^//                        tailtail.next = merge(left, right);while (tail->next) tail = tail->next; // 保持 tail 为尾部}
}

正式代码:好像是C ?

class Solution {
public:ListNode* sortList(ListNode* head) {ListNode dummyHead(0);dummyHead.next = head;auto p = head;int length = 0;while (p) {++length;p = p->next;}for (int size = 1; size < length; size <<= 1) {auto cur = dummyHead.next;auto tail = &dummyHead;while (cur) {auto left = cur;auto right = cut(left, size); // left->@->@ right->@->@->@...cur = cut(right, size); // left->@->@ right->@->@  cur->@->...tail->next = merge(left, right);while (tail->next) {tail = tail->next;}}}return dummyHead.next;}ListNode* cut(ListNode* head, int n) {auto p = head;while (--n && p) {p = p->next;}if (!p) return nullptr;auto next = p->next;p->next = nullptr;return next;}ListNode* merge(ListNode* l1, ListNode* l2) {ListNode dummyHead(0);auto p = &dummyHead;while (l1 && l2) {if (l1->val < l2->val) {p->next = l1;p = l1;l1 = l1->next;       } else {p->next = l2;p = l2;l2 = l2->next;}}p->next = l1 ? l1 : l2;return dummyHead.next;}
};

一定要好好学习归并排序,这是可以作为模板代码来学习的!本质上就是两个操作,merge 和 cut 。从网上搜到的很多归并排序都是用递归写的,不过本题使用循环来写,觉得这种写法很值得学习!

归并排序的递归和循环写法,都值得学习!

快速排序版本:是交换节点的,并非只交换数值。(暂时没看)

class Solution {
public ListNode sortList(ListNode head) {if(head==null||head.next==null) return head;// 没有条件,创造条件。自己添加头节点,最后返回时去掉即可。ListNode newHead=new ListNode(-1);newHead.next=head;return quickSort(newHead,null);}// 带头结点的链表快速排序private ListNode quickSort(ListNode head,ListNode end){if (head==end||head.next==end||head.next.next==end) return head;// 将小于划分点的值存储在临时链表中ListNode tmpHead=new ListNode(-1);// partition为划分点,p为链表指针,tp为临时链表指针ListNode partition=head.next,p=partition,tp=tmpHead;// 将小于划分点的结点放到临时链表中while (p.next!=end){if (p.next.val<partition.val){tp.next=p.next;tp=tp.next;p.next=p.next.next;}else {p=p.next;}}// 合并临时链表和原链表,将原链表接到临时链表后面即可tp.next=head.next;// 将临时链表插回原链表,注意是插回!(不做这一步在对右半部分处理时就断链了)head.next=tmpHead.next;quickSort(head,partition);quickSort(partition,end);// 题目要求不带头节点,返回结果时去除return head.next;}
}

题解的代码

日后再次复习重新写

自己复写的归并排序:好好理解!

class Solution:def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:dummy_head = ListNode(0,head)cur = headcount = 0while cur :count += 1cur = cur.nextstep = 1while step < count :# 这里cur从头结点head开始也是必须的,而不能从虚拟头开始cur = dummy_head.nextpre = dummy_headwhile cur :left = curright = self.cut(left,step)cur = self.cut(right,step)pre.next = self.merge(left,right)while pre.next :pre = pre.nextstep = step << 1return dummy_head.nextdef merge(self,head1,head2):dummy_head = ListNode()cur = dummy_headwhile head1 and head2 :if head1.val >= head2.val :cur.next = head2head2 = head2.nextcur = cur.nextelse :cur.next = head1head1 = head1.nextcur = cur.nextif head1 == None :cur.next = head2else :cur.next = head1return dummy_head.next# 注意,cut操作是,返回切断后,后半部分的链表头def cut(self,head,n):cur = head# 注意这里,一定是n>1 ,因为left是从头结点head开始的# 那么如果step是1的话,指针应该不移动,这样才能仅cut掉当前节点while n > 1 and cur != None :cur = cur.nextn -= 1if cur == None :return Noneres = cur.next# cut 操作要在最后结尾处截断,赋值为 Nonecur.next = Nonereturn res

23 合并 K 个升序链表

未看解答自己编写的青春版

有了上一题的铺垫,这道题就显得较为简单了,但是效率高不高就不清楚了。倒序归并排序,执行 n-1 次,因为是每次合并两个,然后 pop 出这两个,然后将结果 append 进去。

class Solution:def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:n = len(lists) if lists == []:return Nonetemp = lists[0]while len(lists) > 1 :head1 = lists[-1]head2 = lists[-2]temp = self.merge(head1,head2)lists.pop()lists.pop()lists.append(temp)return tempdef merge(self,head1,head2):dummy_head = ListNode()cur = dummy_headwhile head1 and head2 :if head1.val >= head2.val :cur.next = head2head2 = head2.nextcur = cur.nextelse :cur.next = head1head1 = head1.nextcur = cur.nextif head1 == None :cur.next = head2else :cur.next = head1return dummy_head.next

重点

这题的评论区里,一堆妖魔鬼怪的方法,作为初学者的我,就找一个最朴素的思路吧,分治法。

可以学习一下,直接没怎么接触过?

其实就是递归,只不过之前的递归,在获得 L1 和 L2 处,要收获结果了,都是一个类似于:加和,append的操作,本题变成了一个两个链表的merge函数。

class Solution:def mergeKLists(self, lists: List[ListNode]) -> ListNode:n = len(lists)def merge(left, right):if left > right:returnif left == right:return lists[left]mid = (left + right) // 2l1 = merge(left, mid)l2 = merge(mid + 1, right)return mergeTwoLists(l1, l2)def mergeTwoLists(l1, l2):if not l1 or not l2:return l1 or l2if l1.val < l2.val:l1.next = mergeTwoLists(l1.next, l2)return l1else:l2.next = mergeTwoLists(l1, l2.next)return l2return merge(0, n - 1)

题解的代码

日后再次复习重新写

分治法复写:将原本代码中,归并两个有序链表的 mergeTwoLists 函数的递归写法,改为了一般的循环写法,这样看上去就更好理解一些了,本质上就是递归!

class Solution:def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:n = len(lists)def merge(left, right):if left > right:returnif left == right:return lists[left]mid = (left + right) // 2l1 = merge(left, mid)l2 = merge(mid + 1, right)return self.mergeTwoLists(l1, l2)return merge(0, n - 1)def mergeTwoLists(self,head1,head2):dummy_head = ListNode()cur = dummy_headwhile head1 and head2 :if head1.val >= head2.val :cur.next = head2head2 = head2.nextcur = cur.nextelse :cur.next = head1head1 = head1.nextcur = cur.nextif head1 == None :cur.next = head2else :cur.next = head1return dummy_head.next

146 LRU 缓存

未看解答自己编写的青春版

没见过这种类型的题,也不知道应该用什么数据结构。

重点

首先要明确本题的两个要点。

1、LRU 的功能可以使用双向链表实现,访问到的节点移动到头部,超出容量的从尾部删除。

2、要实现O(1)得使用HaspMap,里面储存 key 与 链表节点即可,这样可以快速定位节点,然后删除它,将它移动到链表头部。

这道双向链表的题目,真的学习了,后面一定要多复习多重写这道题!

题解的代码

class ListNode:def __init__(self, key=None, value=None):self.key = keyself.value = valueself.prev = Noneself.next = Noneclass LRUCache:def __init__(self, capacity: int):self.capacity = capacityself.hashmap = {}# 新建两个节点 head 和 tailself.head = ListNode()self.tail = ListNode()# 初始化链表为 head <-> tailself.head.next = self.tailself.tail.prev = self.head# 因为get与put操作都可能需要将双向链表中的某个节点移到末尾,所以定义一个方法def move_node_to_tail(self, key):# 先将哈希表key指向的节点拎出来,为了简洁起名node#      hashmap[key]                               hashmap[key]#           |                                          |#           V              -->                         V# prev <-> node <-> next         pre <-> next   ...   nodenode = self.hashmap[key]node.prev.next = node.nextnode.next.prev = node.prev# 之后将node插入到尾节点前#                 hashmap[key]                 hashmap[key]#                      |                            |#                      V        -->                 V# prev <-> tail  ...  node                prev <-> node <-> tailnode.prev = self.tail.prevnode.next = self.tailself.tail.prev.next = nodeself.tail.prev = nodedef get(self, key: int) -> int:if key in self.hashmap:# 如果已经在链表中了久把它移到末尾(变成最新访问的)self.move_node_to_tail(key)res = self.hashmap.get(key, -1)if res == -1:return reselse:return res.valuedef put(self, key: int, value: int) -> None:if key in self.hashmap:# 如果key本身已经在哈希表中了就不需要在链表中加入新的节点# 但是需要更新字典该值对应节点的valueself.hashmap[key].value = value# 之后将该节点移到末尾self.move_node_to_tail(key)else:if len(self.hashmap) == self.capacity:# 去掉哈希表对应项self.hashmap.pop(self.head.next.key)# 去掉最久没有被访问过的节点,即头节点之后的节点self.head.next = self.head.next.nextself.head.next.prev = self.head# 如果不在的话就插入到尾节点前new = ListNode(key, value)self.hashmap[key] = newnew.prev = self.tail.prevnew.next = self.tailself.tail.prev.next = newself.tail.prev = new

日后再次复习重新写

94 二叉树的中序遍历

未看解答自己编写的青春版

class Solution:def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:if root == None :return []left = self.inorderTraversal(root.left)middle = root.valright = self.inorderTraversal(root.right)return left+[middle]+right

重点

过。本次刷题旨在把题做出来,不考虑,递归法怎么写,迭代法怎么写,其他方法有没有这种事情了。

题解的代码

日后再次复习重新写

104 二叉树的最大深度

未看解答自己编写的青春版

class Solution:def maxDepth(self, root: Optional[TreeNode]) -> int:if root == None :return 0left = self.maxDepth(root.left)right = self.maxDepth(root.right)return 1 + max(left,right)

重点

最大深度 = 根节点的最大高度,直接用高度的定义去递归,简单!

题解的代码

日后再次复习重新写

226 翻转二叉树

未看解答自己编写的青春版

正确可以AC的代码:

class Solution:def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:if root == None :return Noneleft = self.invertTree(root.left)right = self.invertTree(root.right)root.left , root.right = right,leftreturn root

要引以为戒的错误代码:错误原因,在未完全递归完成之前,就改变了当前正在递归节点的左右子树。

class Solution:def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:if root == None :return Noneroot.left = self.invertTree(root.right)# 上一行代码的赋值操作,导致改变了root.left,下面的递归就不对了!root.right = self.invertTree(root.left)return root

重点

题解的代码

日后再次复习重新写

101 对称二叉树

未看解答自己编写的青春版

这道题没法在原函数上进行递归判断了,必须新建一个函数,因为对称的判断需要左右两棵子树的头结点。

class Solution:   def isSymmetric(self, root: Optional[TreeNode]) -> bool:if root == None :return Truereturn self.judge_symmetric(root.left,root.right)def judge_symmetric(self,left,right):if left == None and right == None :return Trueelif left == None and right != None :return Falseelif left != None and right == None :return Falseelse :flag1 = self.judge_symmetric(left.left,right.right)flag2 = self.judge_symmetric(left.right,right.left)if left.val == right.val :flag3 = Trueelse :flag3 = Falsereturn flag1 and flag2 and flag3

怎么感觉我这次写的这个代码,有点复杂呢。

重点

之前的代码也差不多,可以通过更改顺序,稍微优化一下。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:  def isSymmetric(self, root: Optional[TreeNode]) -> bool:if root == None :return Truereturn self.digui(root.left,root.right)def digui(self,p,q):if p == None and q == None :return Trueelif p == None and q != None :return Falseelif p != None and q == None :return Falseelse :if p.val != q.val :return Falseelse :left = self.digui(p.left,q.right)if left :right = self.digui(p.right,q.left)return left and right

题解的代码

日后再次复习重新写

543 二叉树的直径

未看解答自己编写的青春版

嘿嘿,无敌。虽然这道题是简单题,但是我觉得这道题如何保存最长的路径,以及怎么处理每个节点的返回值,还蛮需要考虑清楚的。

class Solution:    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:if root == None :return 0self.res = 0self.track_road(root)return self.resdef track_road(self,root):if root == None :return 0left = self.track_road(root.left)right = self.track_road(root.right)self.res = max(self.res,left+right)return 1 + max(left,right)

重点

我自认为重点有两个:

1、最长路径不一定经过根节点,比如根节点的左子树很深,而且是完全二叉树,而根节点的右子树只有一个节点,那么最长路径一定出现在左子树中。

2、本题要处理的值,和节点的返回值不一致,不是像之前做过的题目,所求结果就是根节点的返回值!在当前节点,最长路径是左右子树加起来,但是如果要返回到上一层,只能选一个max的返回!

题解的代码

日后再次复习重新写

102 二叉树的层序遍历

未看解答自己编写的青春版

from collections import deque
class Solution:def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:if root == None :return []dq = deque()dq.append(root)res = []while dq :size = len(dq)level = []for i in range(size):node = dq.popleft()level.append(node.val)if node.left :dq.append(node.left)if node.right :dq.append(node.right)res.append(level)return res

重点

层序遍历,模板题。

题解的代码

日后再次复习重新写

108 将有序数组转换为二叉搜索树

未看解答自己编写的青春版

递归构造就行了。

class Solution:def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:if nums == [] :return Nonen = len(nums)mid = n // 2node = TreeNode(nums[mid])node.left = self.sortedArrayToBST(nums[0:mid])node.right = self.sortedArrayToBST(nums[mid+1:])return node

重点

题解的代码

日后再次复习重新写

98 验证二叉搜索树

未看解答自己编写的青春版

中序遍历:左中右,所有的中间节点处理逻辑都要放在中间。

class Solution:def __init__(self):self.pre = Nonedef isValidBST(self, root: Optional[TreeNode]) -> bool:if root == None :return Trueleft = self.isValidBST(root.left)# 中序遍历:左中右,所有的中间节点处理逻辑都要放在中间if self.pre :if self.pre.val >= root.val :return False# 这句话一定要放在中间self.pre = rootright = self.isValidBST(root.right)       return left and right

赋值语句 ( self.pre = root ) ,位置错误,导致的错误代码:

class Solution:def __init__(self):self.pre = Nonedef isValidBST(self, root: Optional[TreeNode]) -> bool:if root == None :return Trueleft = self.isValidBST(root.left)# 中序遍历:左中右,所有的中间节点处理逻辑都要放在中间if self.pre :if self.pre.val >= root.val :return Falseright = self.isValidBST(root.right)    self.pre = rootreturn left and right

中序遍历迭代法:

class Solution:def isValidBST(self, root: Optional[TreeNode]) -> bool:stack = []cur = rootpre = Nonewhile stack or cur :if cur :stack.append(cur)cur = cur.leftelse :node = stack.pop()if pre :if pre.val >= node.val :return Falsepre = nodecur = node.rightreturn True

上面从stack里 pop 出来的值,可以直接用 cur 承接,更顺眼一些。

class Solution:def isValidBST(self, root: Optional[TreeNode]) -> bool:stack = []cur = rootpre = Nonewhile stack or cur :if cur :stack.append(cur)cur = cur.leftelse :cur = stack.pop()if pre :if pre.val >= cur.val :return Falsepre = curcur = cur.rightreturn True

重点

题解的代码

日后再次复习重新写

230 二叉搜索树中第K小的元素

未看解答自己编写的青春版

中序遍历的迭代法模板,直接秒了,但是时间上只打败了7% ?

class Solution:def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:count = 0stack = []cur = rootwhile stack or cur :if cur :stack.append(cur)cur = cur.leftelse :cur = stack.pop()count += 1if count == k:return cur.valcur = cur.rightreturn 0

重点

从网上看了一些评论,也基本上和我的方法一致。

另一种思路:通过计算节点个数来找寻第K个数,查找左子树节点个数为 leftN , 如果 K<=leftN ,则所查找节点在左子树上,若 K=leftN+1 , 则所查找节点为根节点,若 K>leftN+1 , 则所查找节点在右子树上, 按照同样方法查找右子树第 K-leftN 个节点。

但是在时间上还是打败 7% , 其他解法怎么这么快的?

class Solution:def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:left = self.count(root.left)if left + 1 == k:return root.valelif left >= k :return self.kthSmallest(root.left,k)else :return self.kthSmallest(root.right,k-left-1)def count(self,root):if root == None :return 0return self.count(root.left)+self.count(root.right)+1

题解的代码

力扣的示例代码:

class Solution:def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:      res = []def dfs(node):if not node:returnif len(res)>=k:returndfs(node.left)res.append(node.val)dfs(node.right)dfs(root)return res[k-1]

原来就是递归,储存所有遍历过的节点,当结果列表长度大于等于 k 时,返回。

看起来因为使用的是递归,所以应该是比我的中序遍历迭代法要少遍历一些节点。不过这个代码,有时候90%,有时候也是7%,不纠结这道题的耗时统计了!

日后再次复习重新写

199 二叉树的右视图

未看解答自己编写的青春版

又是层序遍历模板题。

from collections import deque
class Solution:def rightSideView(self, root: Optional[TreeNode]) -> List[int]:if root == None :return []dq = deque()dq.append(root)res = []while dq :size = len(dq)for i in range(size):node = dq.popleft()if node.left :dq.append(node.left)if node.right:dq.append(node.right)res.append(node.val)return res

重点

题解的代码

日后再次复习重新写

一段用于复制的标题

未看解答自己编写的青春版

一开始总想着用递归做,但是发现,在递归中,无法时刻保存当前需要赋值的节点,因为本题要求不能返回任意值,也就是要操作原节点,所以递归函数返回值,赋值,这些操作都是无效的,所以应该用前序遍历的迭代法。

class Solution:def flatten(self, root: Optional[TreeNode]) -> None:"""Do not return anything, modify root in-place instead."""if root == None :return Nonestack = [root]cur = rootwhile stack :node = stack.pop()if node == root :passelse :cur.right = nodecur.left = Nonecur = nodeif node.right :stack.append(node.right)if node.left :stack.append(node.left)

但是上述是:额外空间复杂度为O(n),因为额外申请了一个堆栈。

重点

额外空间复杂度为O(1)的思想!这道题我被题目骗了,题目说顺序应该是前序遍历,我就想着递归必须用前序遍历,但是前序遍历的问题是:会提前修改掉后面要进入递归的值。那么,链表,从上到下连接可以,我从下到上,反向连接,也可以啊!而这一点,利用递归的回溯特性,可以很自然地做到。假如 last 承载了下一层递归的返回值,而本层递归的root为last的上一个,只需要赋值:root.right=last,就可以了!

所以后序遍历!后序遍历,也就不会有,还未进入递归,值就被修改的问题!这题的思路太妙了!

class Solution:def __init__(self):self.last = Nonedef flatten(self, root: Optional[TreeNode]) -> None:"""Do not return anything, modify root in-place instead."""if root == None :return Noneself.flatten(root.right)self.flatten(root.left)root.right = self.lastroot.left = Noneself.last = root

思路很妙,可以多写多复习。

题解的代码

日后再次复习重新写

105 从前序与中序遍历序列构造二叉树

未看解答自己编写的青春版

递归,数组切片方法:

class Solution:def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:if preorder == []:return Nonemiddle = preorder[0]index = inorder.index(middle)node = TreeNode(middle)node.left = self.buildTree(preorder[1:index+1],inorder[:index])node.right = self.buildTree(preorder[index+1:],inorder[index+1:])return node

厉害!第一次自己尝试,写出了传入左右索引版的代码,在空间占用上,要比上面的代码低不少。

class Solution:def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:def build(pleft,pright,ileft,iright):# 左闭右开,不包括right,所以才能有等于号if pleft >= pright :return Nonemiddle = preorder[pleft]index = inorder.index(middle)node = TreeNode(middle)# 举例: ileft = 3 , middle = 4 , 意味着左子树只有一个节点# 那么对于前序数组,起始位置 pleft+1 , 要想让左子树有一个点,区间应该为# [pleft+1,pleft+2) 注意循环不变量,左闭右开node.left = build(pleft+1,pleft+1+index-ileft,ileft,index)node.right = build(pleft+1+index-ileft,pright,index+1,iright)return noden = len(preorder)return build(0,n,0,n)

重点

过。可以体会下,传入左右区间的参数的版本,怎样能把每个区间都写对。

题解的代码

日后再次复习重新写

437 路径总和 III

未看解答自己编写的青春版

没做出来。

重点

主要是两种方法:双重递归 ; 前缀和。

力扣官方题解

没来得及体会,日后再看,重点学习这道题。

题解的代码

双重递归:

class Solution:def pathSum(self, root: TreeNode, targetSum: int) -> int:def rootSum(root, targetSum):if root is None:return 0ret = 0if root.val == targetSum:ret += 1ret += rootSum(root.left, targetSum - root.val)ret += rootSum(root.right, targetSum - root.val)return retif root is None:return 0ret = rootSum(root, targetSum)ret += self.pathSum(root.left, targetSum)ret += self.pathSum(root.right, targetSum)return ret

前缀和:

class Solution:def pathSum(self, root: TreeNode, targetSum: int) -> int:prefix = collections.defaultdict(int)prefix[0] = 1def dfs(root, curr):if not root:return 0ret = 0curr += root.valret += prefix[curr - targetSum]prefix[curr] += 1ret += dfs(root.left, curr)ret += dfs(root.right, curr)prefix[curr] -= 1return retreturn dfs(root, 0)

日后再次复习重新写

236 二叉树的最近公共祖先

未看解答自己编写的青春版

搞懂,左右公共祖先,怎么从后序遍历中,层层回溯到根节点的。

class Solution:def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':self.res = Noneself.digui(root,p,q)return self.resdef digui(self,root,p,q):if root == None :return Falseif root.val == p.val or root.val == q.val :self.res = rootreturn True               left = self.digui(root.left,p,q)right = self.digui(root.right,p,q)if left and right :self.res = rootreturn left or right

重点

参考卡哥的解答。

二叉树的最近公共祖先

题目拓展:如果树是一个二叉搜索树呢?前序遍历树中节点的值就可以了,当节点值第一次出现在在区间
[ p.val , q.val ] 时,这个节点就是最近公共祖先,可以用反证法证明,再走一步就不符合条件了。

题解的代码

日后再次复习重新写

124 二叉树中的最大路径和

未看解答自己编写的青春版

小小 hard 。

本题只需要考虑清楚:对于在当前节点收获结果的逻辑:左和右,分别都有两个状态,取或不取,一共四种情况就好了;递归函数在返回时,同前面做过的一道题,返回的时候,没有左右都考虑的情况。

class Solution:def maxPathSum(self, root: Optional[TreeNode]) -> int:self.res = -infself.digui(root)return self.resdef digui(self,root):if root == None :return 0if root.left == None and root.right == None :self.res = max(self.res,root.val)return root.valleft = self.digui(root.left)right = self.digui(root.right)# 只要想明白这里,左和右,分别都有两个状态,取或不取,一共四种情况就好了self.res = max(self.res,left+right+root.val,root.val,right+root.val,left+root.val)# 同前面做过的一道题,返回的时候,没有左右都考虑的情况return max(left,right,0)+root.val

重点

题解的代码

日后再次复习重新写

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/17884.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客网Verilog刷题——VL51

牛客网Verilog刷题——VL51 题目答案 题目 请编写一个十六进制计数器模块&#xff0c;计数器输出信号递增每次到达0&#xff0c;给出指示信号zero&#xff0c;当置位信号set 有效时&#xff0c;将当前输出置为输入的数值set_num。模块的接口信号图如下&#xff1a; 模块的时序图…

JVM基础篇-方法区与运行时常量池

JVM基础篇-方法区与运行时常量池 方法区 Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的方法区。方法区类似于传统语言的编译代码的存储区或者类似于操作系统进程中的“文本”段。它存储每个类的结构&#xff0c;例如运行时常量池、字段和方法数据&#xff0c;以及方法和…

Hadoop 集群如何升级?

前言 本文隶属于专栏《大数据技术体系》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见大数据技术体系 正文 升级 Hadoop 集群需要细致的规划&#xff0c;特…

使用docker部署Wordpress

文章目录 1.创建网络2.创建volume存储3.拉取镜像4.创建mysql容器mysql修改密码 5.创建wordpress容器6.访问localhost:80就可以直接使用啦 1.创建网络 docker network create --subnet172.18.0.0/24 pro-net2.创建volume存储 # mysql 存储 docker volume create volume_mysql…

vscode 前端开发插件 2023

自己记录 安装vscode后必装插件 chinesegit 必装没啥可说 随时更新 1.CSS Navigation CTRL点击类名可跳转到对应样式位置。 如果是scss less的话。css peak插件无法生效 2.GitLens — Git supercharged 可以看到每一行的git提交记录。 3.Auto Rename Tag 可以同步更新…

winform学习(3)-----Windows窗体应用和Windows窗体应用(.Net Framework)有啥区别?

1.模板选择 在学习winform的时候总是会对这两个应用不知道选择哪个&#xff1f;而且在学习的时候也没有具体的说明 首先说一下我是在添加控件的时候出现了以下问题 对于使用了Windows窗体应用这个模板的文件在工具箱中死活不见控件。 在转换使用了Windows窗体应用(.NET Fram…

Linux学习之一次性计划任务at

计划任务&#xff1a; 让计算机在指定的时间运行程序的任务 计划任务的分类&#xff1a; 1&#xff09;一次性计划任务 2&#xff09;周期性计划任务 先来讲讲一次性执行任务at。执行at 18:32报错-bash: at: command not found。 yum install -y at安装at。 at 18:32后边按下…

【C++】二叉搜索树

二叉搜索树 前言正式开始模拟实现树节点以及树框架增中序遍历查找删除 递归实现增删查查插删 析构拷贝构造赋值重载时间复杂度分析应用场景两道题 前言 本来想先把搁置了一个月的Linux讲讲的&#xff0c;但是里面有些内容需要用到一些比较高级的数据结构&#xff0c;用C写的话…

【业务功能篇61】SpringBoot项目流水线 dependencyManagement 标签整改依赖包版本漏洞问题

业务场景&#xff1a;当前我们项目引入了公司自研的一些公共框架组件&#xff0c;比如SSO单点登录jar包&#xff0c;文件上传服务jar包等公共组件&#xff0c;开发新功能&#xff0c;本地验证好之后&#xff0c;部署流水线&#xff0c;报出一些jar包版本的整改漏洞问题&#xf…

二叉树OJ(C)

文章目录 1.单值二叉树1.1法一&#xff1a;无返回值1.2法二&#xff1a;有返回值 2.相同的树3.对称二叉树4.二叉树的前序遍历5.二叉树的中序遍历6.二叉树的后序遍历7.另一棵树的子树8.二叉树遍历 1.单值二叉树 1.1法一&#xff1a;无返回值 struct TreeNode {int val;struct …

Virtualbox虚拟机中Ubuntu忘记密码

1、首先重新启动Ubuntu系统&#xff0c;鼠标快速点一下Virtualbox虚拟机窗口获取焦点&#xff0c;然后按住shift键&#xff0c;以调出grub启动菜单。 2、根据提示按下键盘E键进入编辑模式&#xff0c;向下移动光标&#xff0c;将如下"ro quiet splash $vt_handoff"部…

SpringBoot集成jasypt,加密yml配置文件

SpringBoot集成jasypt&#xff0c;加密yml配置文件 一、pom配置二、生成密文代码三、配置3.1、yml加密配置3.2、密文配置3.3、启动配置3.4、部署配置 四、遇到的一些坑 最新项目安全检测&#xff0c;发现配置文件中数据库密码&#xff0c;redis密码仍处理明文状态 一、pom配置…

一套AI+医疗模式的医院智慧导诊系统源码:springboot+redis+mybatis plus+mysql

一套AI医疗模式的医院智慧导诊系统源码 相关技术&#xff1a; 技术架构&#xff1a;springbootredismybatis plusmysqlRocketMQ 开发语言&#xff1a;java 开发工具&#xff1a;IDEA 前端框架&#xff1a;Uniapp 后端框架&#xff1a;springboot 数 据 库&#xff1a;mys…

【win11+vs 2017+OpenCV4.5.5+Qt5.12配置】解决了过程中遇到的小问题

0.版本选择 由于Qt5无法与最新的vs2022兼容&#xff0c;扩展工具中一直显示不可用&#xff0c;所以将vs降级成vs2017。 在安装Qt的过程中&#xff0c;会选择安装Qt套件&#xff0c;其中就的MCVS 2017&#xff0c;说明vs2017是与qt兼容的。 当然也可以用qt creator这一原生IDE。…

LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 论文阅读

论文信息 题目&#xff1a;LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 作者&#xff1a;Devendra Singh Chaplot, Dhiraj Gandhi 项目地址&#xff1a;https://devendrachaplot.github.io/projects/Neural-SLAM 代码地址&#xff1a;https://github.com/devendrachaplot/N…

ES6基础知识九:你是怎么理解ES6中Module的?使用场景?

一、介绍 模块&#xff0c;&#xff08;Module&#xff09;&#xff0c;是能够单独命名并独立地完成一定功能的程序语句的集合&#xff08;即程序代码和数据结构的集合体&#xff09;。 两个基本的特征&#xff1a;外部特征和内部特征 外部特征是指模块跟外部环境联系的接口…

Stable Diffusion AI绘画学习指南【插件安装设置】

插件安装的方式 可用列表方式安装&#xff0c;点开Extensions 选项卡&#xff0c;找到如下图&#xff0c;找到Available选项卡&#xff0c;点load from加载可用插件&#xff0c;在可用插件列表中找到要装的插件按install 按扭按装&#xff0c;安装完后(Apply and restart UI)应…

15、两个Runner初始化器和 springboot创建非web应用

两个Runner初始化器 两个Runner初始化器——主要作用是对component组件来执行初始化 这里的Component组件我理解为是被Component注解修饰的类 Component //用这个注解修饰的类&#xff0c;意味着这个类是spring容器中的一个组件&#xff0c;springboot应用会自动加载该组件。 …

【原创】IPTVC2实现方案(文末有demo)

前言: 名词解释: IPTVC2, 全称: 央视国际节目定价发布接口规范,标准版本当前最新为2.7.12 附赠资源链接&#xff0c;侵删:规范 规范中提供的样例&#xff0c;实现基于axis1.4(2006的时代宠物) 基于axis1版本的实现参考: Spring boot 集成Axis1.4 &#xff0c;使用wsdd文件发…

【CSDN】

欢迎使用Mark编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持&#xff0c…