算法学习笔记——整体学习路径与框架思维

数据结构的存储方式

数据结构的底层存储方式只有数组(顺序存储)和链表(链式存储)

  • 数组是紧凑连续存储,相对节约空间,可以索引快速访问,但如果连续内存空间不够则需要将数据复制到一块更大的空间中,扩容复杂度O(N),插入删除也需要移动大量元素,复杂度O(N)
  • 链表的元素不连续,不存在扩容问题,插入删除复杂度O(1),但不能随机访问(任意访问某个下标处的元素),且存储前后元素的指针要消耗额外空间

数组和链表是一切的底层基础,其他任何数据都是上层建筑

  • 图的两种表示:邻接矩阵就是二维数组,但在稀疏图中耗费空间;邻接表就是链表,节省空间但很多操作效率不如邻接矩阵
  • 树一般用链表实现,还有各种巧妙的设计(如二叉搜索树、AVL树、红黑树、区间树、B树);特别的,如果是完全二叉树,则可用数组存储树,无需保存额外的结点指针、还能简化操作,也就是“堆”

数据结构的基本操作

任何数据结构的操作,概括而言都是遍历+访问
我们选择不同种类的数据结构,目的就是在不同场景下高效地增、删、查、改

遍历+访问的两种形式:线性和非线性
线性:数组 的for/while迭代遍历、链表 的for/while迭代遍历

def traverse(arr: list):
    """遍历数组"""
    for i in range(len(arr)):
    # 访问arr[i]
class ListNode(object):
    """单链表的结点"""
    def __init__(self, data):
        self.data = data
        self.next = None

def traverse(head: ListNode):
    """遍历单链表"""
    p = head
    while True:
        # 访问p结点
        p = p.next
        if head.next is None:
            break

非线性:链表 的递归遍历

class ListNode(object):
    """单链表的结点"""
    def __init__(self, data):
        self.data = data
        self.next = None

def traverse(head: ListNode):
    """递归遍历单链表"""
    # 在此前序遍历head.data
    traverse(head.next)
    # 在此后序遍历head.data

链表的情况稍作推广,得到二叉树的递归遍历结构

class TreeNode(object):
    """二叉树的结点"""
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

def traverse(root: TreeNode):
    """二叉树的递归遍历"""
    # 在此前序遍历
    traverse(root.left)
    # 在此中序遍历
    traverse(root.right)
    # 在此后序遍历

在推广至更一般情况,得到N叉树的递归遍历结构

class TreeNode(object):
    """N叉树的结点"""
    def __init__(self, val):
        self.val = val
        self.children = []
        
def traverse(root: TreeNode):
    """N叉树的递归遍历"""
    for child in root.children:
        traverse(child)

框架思维

上面建立起了核心框架,无论任何数据结构的增删查改,都脱离不开上面的框架结构,只需根据具体问题添加代码就好了
遇到问题时,把握其共性和本质,往框架上靠,从而举一反三

算法学习路线

数据结构是工具,算法就是选用合适工具来解决问题,因此先掌握常用数据结构

先从二叉树入手,由二叉树培养框架思维,且大多算法本质上都是树的遍历问题,上文提到的框架几乎能解决所有二叉树问题

def traverse(root: TreeNode):
    """二叉树的递归遍历"""
    # 在此前序遍历
    traverse(root.left)
    # 在此中序遍历
    traverse(root.right)
    # 在此后序遍历

刷完二叉树,再去做回溯、分治、动态规划,会发现只要涉及递归的问题,基本上都是树的问题——这些问题可以用递归树来画图表示,树上的某一个节点,就是一个递归函数的返回值

动态规划, 分治算法, 回溯(DFS)算法, BFS 算法,就是在“递归树”这棵抽象的“树”中搜索目标的过程
并查集算法, 二叉堆与优先级队列,则是利用 数组 来存储 树,并实现一些特殊功能

  • 动态规划的凑零钱问题,暴力就是遍历一颗N叉树:每次从当前结点做选择,并进入下一节点
def dp(n):
    for coin in coins:
        dp(n-coin)
  • 回溯算法就是一个N叉树的前序+后序遍历问题,比如排列组合问题
def backtrack(nums: list, ans: list):
    """打印nums的所有排列组合"""
    if len(ans) == len(nums):
        print(ans)
    for num in nums:
        if num not in ans:
            ans.append(num)  # 在前序遍历位置做出选择
            backtrack(nums, ans)  # 进入下一决策树
            ans.pop()  # 在后序遍历位置撤销选择
backtrack([1,2,3],[])
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值