二叉树、动态规划与链表学习

文章目录

二叉树前序、中序、后序遍历相互求法

前序遍历:

复制代码
1.访问根节点 
2.前序遍历左子树 
3.前序遍历右子树 

前序遍历实现(Python)

前序遍历是二叉树遍历的一种方式,顺序为:根节点 -> 左子树 -> 右子树。以下是递归和迭代两种实现方法。

递归实现

递归方法简洁直观,适合理解前序遍历的逻辑。

python 复制代码
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def preorder_traversal(root):
    result = []
    def traverse(node):
        if not node:
            return
        result.append(node.val)  # 访问根节点
        traverse(node.left)      # 遍历左子树
        traverse(node.right)     # 遍历右子树
    traverse(root)
    return result
迭代实现

迭代方法使用栈模拟递归调用,避免递归的深度限制。

python 复制代码
def preorder_traversal_iterative(root):
    if not root:
        return []
    stack, result = [root], []
    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
使用示例
python 复制代码
# 构建二叉树
#     1
#      \
#       2
#      /
#     3
root = TreeNode(1, None, TreeNode(2, TreeNode(3), None))
print("递归结果:", preorder_traversal(root))          # 输出: [1, 2, 3]
print("迭代结果:", preorder_traversal_iterative(root)) # 输出: [1, 2, 3]

中序遍历:

复制代码
1.中序遍历左子树 
2.访问根节点 
3.中序遍历右子树 

中序遍历实现方法

中序遍历是二叉树遍历的一种方式,顺序为:左子树、根节点、右子树。以下是Python实现方法:

递归实现

python 复制代码
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def inorderTraversal(root):
    res = []
    def inorder(node):
        if not node:
            return
        inorder(node.left)
        res.append(node.val)
        inorder(node.right)
    inorder(root)
    return res

迭代实现(使用栈)

python 复制代码
def inorderTraversal(root):
    res = []
    stack = []
    curr = root
    while curr or stack:
        while curr:
            stack.append(curr)
            curr = curr.left
        curr = stack.pop()
        res.append(curr.val)
        curr = curr.right
    return res

二叉树构造示例

python 复制代码
# 构建示例二叉树
#     1
#      \
#       2
#      /
#     3
root = TreeNode(1)
root.right = TreeNode(2)
root.right.left = TreeNode(3)

print(inorderTraversal(root))  # 输出: [1, 3, 2]
后序遍历:
复制代码
1.后序遍历左子树 
2.后序遍历右子树 
3.访问根节点

后序遍历实现

后序遍历是二叉树遍历的一种方式,遍历顺序为左子树、右子树、根节点。以下是递归和非递归两种实现方法。

递归实现

递归方法是最直观的实现方式,直接按照左、右、根的顺序递归调用即可。

python 复制代码
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def postorder_traversal(root):
    result = []
    def traverse(node):
        if node is None:
            return
        traverse(node.left)
        traverse(node.right)
        result.append(node.val)
    traverse(root)
    return result
非递归实现

非递归方法使用栈来模拟递归过程,需要注意访问节点的顺序和标记已处理节点的状态。

python 复制代码
def postorder_traversal_iterative(root):
    if not root:
        return []
    
    stack = []
    result = []
    last_visited = None
    node = root
    
    while stack or node:
        if node:
            stack.append(node)
            node = node.left
        else:
            peek_node = stack[-1]
            if peek_node.right and last_visited != peek_node.right:
                node = peek_node.right
            else:
                result.append(peek_node.val)
                last_visited = stack.pop()
    return result
示例用法

以下是如何使用上述代码遍历一个简单的二叉树。

python 复制代码
# 构建二叉树
#       1
#      / \
#     2   3
#    / \
#   4   5
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print("递归后序遍历:", postorder_traversal(root))
print("非递归后序遍历:", postorder_traversal_iterative(root))

输出结果:

复制代码
递归后序遍历: [4, 5, 2, 3, 1]
非递归后序遍历: [4, 5, 2, 3, 1]

python 输出数组中最长递增子序列

最长递增子序列问题

最长递增子序列(Longest Increasing Subsequence, LIS)是一个经典的动态规划问题。以下是一个Python实现,能够找到并输出数组中最长递增子序列的长度和具体序列。

动态规划实现

动态规划方法的时间复杂度为O(n²),适用于中等规模输入。

python 复制代码
def longest_increasing_subsequence(nums):
    if not nums:
        return 0, []
    
    n = len(nums)
    dp = [1] * n
    prev = [-1] * n
    
    for i in range(1, n):
        for j in range(i):
            if nums[i] > nums[j] and dp[i] < dp[j] + 1:
                dp[i] = dp[j] + 1
                prev[i] = j
    
    max_length = max(dp)
    last_index = dp.index(max_length)
    
    # 回溯构建最长子序列
    lis = []
    while last_index != -1:
        lis.append(nums[last_index])
        last_index = prev[last_index]
    lis.reverse()
    
    return max_length, lis

# 示例
nums = [10, 9, 2, 5, 3, 7, 101, 18]
length, sequence = longest_increasing_subsequence(nums)
print("Length of LIS:", length)
print("LIS sequence:", sequence)
更高效的二分查找实现

对于大规模数据,可以使用二分查找优化,时间复杂度降低到O(n log n)。

python 复制代码
import bisect

def longest_increasing_subsequence_optimized(nums):
    tails = []
    prev_indices = [-1] * len(nums)
    indices = []
    
    for i, num in enumerate(nums):
        idx = bisect.bisect_left(tails, num)
        if idx == len(tails):
            tails.append(num)
            indices.append(i)
        else:
            tails[idx] = num
            indices[idx] = i
        
        if idx > 0:
            prev_indices[i] = indices[idx - 1]
    
    # 回溯重建序列
    max_length = len(tails)
    if max_length == 0:
        return 0, []
    
    lis = []
    current = indices[-1]
    while current != -1:
        lis.append(nums[current])
        current = prev_indices[current]
    lis.reverse()
    
    return max_length, lis

# 示例
nums = [10, 9, 2, 5, 3, 7, 101, 18]
length, sequence = longest_increasing_subsequence_optimized(nums)
print("Length of LIS:", length)
print("LIS sequence:", sequence)
代码说明
  1. 动态规划方法使用两个数组:dp存储以每个元素结尾的LIS长度,prev记录前驱索引以便重建序列。
  2. 优化方法维护一个tails数组,其中tails[i]表示长度为i+1的递增子序列的最小末尾元素。
  3. 两种方法都通过回溯前驱索引来重建实际的LIS序列。
复杂度对比
  • 动态规划:O(n²)时间,O(n)空间
  • 二分优化:O(n log n)时间,O(n)空间

对于大多数实际应用,二分查找优化版本是更好的选择,特别是当输入规模较大时。


python 反转单链表

单链表的实现

用一组地址任意的存储单元存放线性表中的数据元素。以元素(数据元素的映象) + 指针(指示后继元素存储位置) = 结点。

以"结点的序列"表示线性表,称作线性链表(单链表)。单链表是一种顺序存取的结构,为找第 i 个数据元素,必须先找到第 i-1 个数据元素。

链表的结点结构:

┌──┬──┐

│data│next│

└──┴──┘

data域:存放结点值的数据域

next域:存放结点的直接后继的地址(位置)的指针域(链域)。

注意:①链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。

②每个结点只有一个链域的链表称为单链表(Single Linked List)。

所谓的链表就好像火车车厢一样,从火车头开始,每一节车厢之后都连着后一节车厢。

反转单链表的方法

反转单链表是数据结构中的常见操作,可以通过迭代或递归的方式实现。以下是两种方法的详细说明和示例代码。

迭代法

迭代法通过遍历链表,逐个反转节点的指针方向。需要维护三个指针:prevcurrentnext_node

python 复制代码
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverse_list(head):
    prev = None
    current = head
    while current:
        next_node = current.next
        current.next = prev
        prev = current
        current = next_node
    return prev

步骤解析:

  • 初始化 prevNonecurrent 为链表头节点 head
  • 遍历链表,保存 current 的下一个节点到 next_node
  • 反转 current 的指针方向,使其指向 prev
  • 更新 prevcurrent 为下一个节点。
  • 最终 prev 成为新链表的头节点。
递归法

递归法通过递归调用反转子链表,再将当前节点连接到反转后的子链表末尾。

python 复制代码
def reverse_list_recursive(head):
    if not head or not head.next:
        return head
    new_head = reverse_list_recursive(head.next)
    head.next.next = head
    head.next = None
    return new_head

步骤解析:

  • 递归终止条件是当前节点为空或下一个节点为空。
  • 递归调用反转 head.next 的子链表,返回的新头节点为 new_head
  • 将当前节点 head 的下一个节点的指针指向 head,完成局部反转。
  • head.next 设置为 None,避免链表成环。
  • 最终返回 new_head 作为反转后的链表头节点。
复杂度分析
  • 时间复杂度:两种方法均为 O(n),需要遍历链表一次。
  • 空间复杂度:迭代法为 O(1),递归法为 O(n)(递归调用栈空间)。

部分题目来源网络资源!

相关推荐
Bathwind-w3 小时前
空间矢量脉宽调制(Space Vector Pulse Width Modulation)SVPWM基础
学习
小苏兮3 小时前
【把Linux“聊”明白】自动化构建-make/Makefile详解
linux·服务器·学习·自动化·1024程序员节
XH1.3 小时前
学习RT-thread(项目一:基于RT-thread的multi_button控制灯闪烁)
stm32·单片机·学习
Wu Liuqi4 小时前
【大模型学习4】大语言模型(LLM)详解
人工智能·学习·语言模型·大模型
CarmenHu4 小时前
RAFT微调学习笔记
笔记·学习
QiZhang | UESTC4 小时前
JAVA算法练习题day67
java·python·学习·算法·leetcode
d111111111d5 小时前
STM32外设学习--ADC模数转换器--笔记
笔记·stm32·单片机·嵌入式硬件·学习
AC是你的谎言5 小时前
网络层和数据链路层
linux·网络·学习·智能路由器
Miki Makimura5 小时前
KVStore 多行文章型键值扩展解析:切片存储与客户端多行交互
运维·服务器·网络·学习