代码随想录算法训练营第一天|数组理论基础,704. 二分查找,27. 移除元素
代码随想录算法训练营第二天|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
代码随想录算法训练营第三天|链表理论基础,203.移除链表元素,707.设计链表,206.反转链表
代码随想录算法训练营第四天|24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,总结
代码随想录算法训练营第五天|哈希表理论基础,242.有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和
代码随想录算法训练营第六天|454.四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,总结
代码随想录算法训练营第七天|344.反转字符串,541. 反转字符串II,卡码网:54.替换数字,151.翻转字符串里的单词,卡码网:55.右旋转字符串
代码随想录算法训练营第八天|28. 实现 strStr(),459.重复的子字符串,字符串总结,双指针回顾
代码随想录算法训练营第九天|理论基础,232.用栈实现队列,225. 用队列实现栈
代码随想录算法训练营第十天|20. 有效的括号,1047. 删除字符串中的所有相邻重复项,150. 逆波兰表达式求值
代码随想录算法训练营第十一天|239. 滑动窗口最大值,347.前 K 个高频元素,总结
满二叉树
如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。下图即为深度为k,有2^k-1个节点的满二叉树:
完全二叉树
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
二叉搜索树
二叉搜索树与满二叉树、完全二叉树的区分在于二叉搜索书是有数值的,是一个有序树。
平衡二叉搜索树
平衡二叉搜索树又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉树可以链式存储,也可以顺序存储。链式存储方式使用指针, 顺序存储的方式使用数组;顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。
链式存储:
顺序存储: 顺序存储就是用数组来存储二叉树,如果父节点的数组下标是
i
i
i,那么它的左孩子就是
i
?
2
+
1
i * 2 + 1
i?2+1,右孩子是
i
?
2
+
2
i * 2 + 2
i?2+2:
二叉树主要有两种遍历方式:
栈其实就是递归的一种实现结构,前中后序遍历的逻辑其实都是可以借助栈使用递归的方式来实现的;而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
链式存储的二叉树节点的定义方式:和链表差不多,相对于链表 ,二叉树的节点里多了一个指针, 有两个指针,指向左右孩子。
class TreeNode:
def __init__(self, val, left = None, right = None):
self.val = val
self.left = left
self.right = right
视频讲解:每次写递归都要靠直觉? 这次带你学透二叉树的递归遍历!| LeetCode:144.前序遍历,145.后序遍历,94.中序遍历
递归算法的三要素:
前序遍历:
题目链接:144.前序遍历
# 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 preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
left=self.preorderTraversal(root.left)
right=self.preorderTraversal(root.right)
return [root.val]+left+right
后序遍历:
题目链接:145.后序遍历
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
left=self.postorderTraversal(root.left)
right=self.postorderTraversal(root.right)
return left+right+[root.val]
中序遍历:
题目链接:94.中序遍历
# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
left=self.inorderTraversal(root.left)
right=self.inorderTraversal(root.right)
return left+[root.val]+right
视频讲解:
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
前序遍历:
题目链接:144.前序遍历
# 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 preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
stack=[root]
result=[]
while stack:
node=stack.pop()
#处理中节点
result.append(node.val)
#右节点入栈
if node.right:
stack.append(node.right)
#左节点入栈
if node.left:
stack.append(node.left)
return result
后序遍历:
题目链接:145.后序遍历
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
stack=[root]
result=[]
while stack:
#处理中节点
node=stack.pop()
result.append(node.val)
#处理左节点
if node.left:
stack.append(node.left)
#处理右节点
if node.right:
stack.append(node.right)
return result[::-1]
中序遍历:
题目链接:94.中序遍历
# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
stack=[]
result=[]
cur=root
while cur or stack:
if cur:
stack.append(cur)
cur=cur.left
else:
cur=stack.pop()
result.append(cur.val)
cur=cur.right
return result
核心思想:将访问的节点放入栈中,把要处理的节点也放入栈中,紧接着放入一个空指针作为标记。这样就使得前序、后序、中序的代码只是顺序上稍有不同。
前序遍历:
题目链接:144.前序遍历
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st= []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #右
st.append(node.right)
if node.left: #左
st.append(node.left)
st.append(node) #中节点是需要处理的节点,其后要加一个空节点
st.append(None)
else:##遇到空节点将下一个节点放进结果集
node = st.pop()
result.append(node.val)
return result
后序遍历:
题目链接:145.后序遍历
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
st.append(node) #中
st.append(None)
if node.right: #右
st.append(node.right)
if node.left: #左
st.append(node.left)
else:#遇到空节点将下一个节点放进结果集
node = st.pop()
result.append(node.val)
return result
中序遍历:
题目链接:94.中序遍历
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #添加右节点
st.append(node.right)
st.append(node) #添加中节点
st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记
if node.left: #添加左节点
st.append(node.left)
else: #遇到空节点将下一个节点放进结果集
node = st.pop() #重新取出栈中元素
result.append(node.val) #加入到结果集
return result