首页 > 编程语言 >程序员算法入门1

程序员算法入门1

时间:2024-11-26 11:30:44浏览次数:10  
标签:arr 入门 递归 复杂度 问题 程序员 算法 dp

算法是程序员的必修课,它帮助我们编写高效的代码并解决复杂的问题。然而,对于初学者,算法看似高深,实际却可以从基础入手逐步掌握。本专栏旨在为零基础程序员提供一个系统、简单、易懂的算法入门指导。

目录

目录

1. 什么是算法?

特性

2. 算法与复杂度:从效率谈起

时间复杂度

举例:比较数组中的最大值

时间复杂度的图解

空间复杂度

3. 排序算法详解与可视化

3.1 冒泡排序

3.2 插入排序

3.3 快速排序

4. 搜索算法:如何快速找到答案?

4.1 线性搜索

4.2 二分搜索

5. 递归与分治思想

5.1 什么是递归?

5.2 递归代码示例:阶乘

5.3 分治思想

6. 动态规划的基础理解

6.1 动态规划的特点

6.2 动态规划的经典问题:斐波那契数列

6.3 动态规划案例:背包问题

7. 图论算法的简单应用

7.1 深度优先搜索(DFS)

7.2 广度优先搜索(BFS)

8. 总结与进阶学习建议


目录


1. 什么是算法?

算法是一组有序的指令,用于解决特定问题。可以把它想象成解决问题的“步骤清单”。例如,我们生活中经常使用的“做饭”就是一个算法:备料、加热、调味、出锅,这些步骤是按照一定顺序完成的。

特性

  • 输入和输出:算法需要明确的输入和输出。
  • 有限性:算法必须在有限步内结束。
  • 明确性:每一步都清晰可执行。
  • 高效性:好的算法在解决问题时速度快,占用资源少。

2. 算法与复杂度:从效率谈起

一个算法的效率通常通过 时间复杂度 和 空间复杂度 来评估。

时间复杂度

时间复杂度描述了算法运行时间与输入规模的关系。常见的时间复杂度有:

  • O(1):常数时间,例如数组访问。
  • O(n):线性时间,例如遍历列表。
  • O(n²):平方时间,例如嵌套循环。
举例:比较数组中的最大值

python

def find_max(arr):
    max_val = arr[0]
    for num in arr:
        if num > max_val:
            max_val = num
    return max_val
  • 复杂度:O(n),因为需要遍历整个数组。
时间复杂度的图解

以下图显示了不同时间复杂度的增长趋势:

  时间 |
       |
       |            O(n²)
       |         /
       |        /
       |   O(n)
       |   /
       |  /
       | O(1)
       ------------------
          输入规模

空间复杂度

描述算法占用的内存。比如,递归算法可能会占用较多内存,因为它需要保存每次递归调用的状态。


3. 排序算法详解与可视化

排序是程序员学习算法的必经之路。我们通过排序算法来学习基本的算法设计思路。

3.1 冒泡排序

原理:两两比较相邻元素,将较大的元素“冒泡”到最后。

代码实现

python

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr

图解: 假设我们对数组 [5, 3, 8, 6] 进行冒泡排序:

第一轮:

[5, 3, 8, 6]  -> [3, 5, 8, 6] -> [3, 5, 6, 8]

第二轮:

[3, 5, 6, 8]  -> 无需交换

最终结果:

[3, 5, 6, 8]

时间复杂度:O(n²),适合小规模数据。


3.2 插入排序

原理:将元素插入到已经排好序的子序列中。

代码实现

python

def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        while j >= 0 and key < arr[j]:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
    return arr

图解: 对 [5, 3, 8, 6] 使用插入排序:

  1. 初始:[5 | 3, 8, 6]
  2. 插入3:[3, 5 | 8, 6]
  3. 插入8:[3, 5, 8 | 6]
  4. 插入6:[3, 5, 6, 8]

3.3 快速排序

原理:选择一个“基准”元素,将小于基准的放左边,大于基准的放右边,递归处理子序列。

代码实现

python

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[0]
    less = [x for x in arr[1:] if x <= pivot]
    greater = [x for x in arr[1:] if x > pivot]
    return quick_sort(less) + [pivot] + quick_sort(greater)

图解: 对 [5, 3, 8, 6] 使用快速排序:

  1. 选基准5,分割为 [3] 和 [8, 6]
  2. 递归排序:
    • [3] 不变。
    • [8, 6] 基准为8,分割为 [6] 和 []
  3. 合并结果:[3, 5, 6, 8]

4. 搜索算法:如何快速找到答案?

搜索算法用于从数据集中查找目标元素。

4.1 线性搜索

原理:逐一检查每个元素,直到找到目标。

代码实现

python                  

def linear_search(arr, target):
    for i in range(len(arr)):
        if arr[i] == target:
            return i
    return -1

时间复杂度:O(n)


4.2 二分搜索

原理:数据必须是有序的,每次将搜索范围缩小一半。

代码实现

python    

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

图解: 假设数组为 [1, 3, 5, 7, 9],目标是 7

  1. 初始范围:[1, 3, 5, 7, 9],中间值为 5
  2. 缩小范围:[7, 9],中间值为 7
  3. 找到目标。

时间复杂度:O(log n)


5. 递归与分治思想

递归是一种解决问题的方法,核心思想是将问题拆解为更小的子问题,并利用相同的方法来解决这些子问题,直到问题足够小以至于可以直接解决。

递归和分治常常结合使用。分治(Divide and Conquer)是一种将问题分成若干子问题,分别解决后再合并结果的算法设计策略。

5.1 什么是递归?

递归的定义:一个函数直接或间接地调用自身

递归的三个要素:

  1. 递归终止条件:必须要有一个明确的终止条件,避免无限递归。
  2. 递归的逻辑:在每一次递归中,解决一个更小的子问题。
  3. 递归调用自身:通过改变输入参数使问题规模逐渐缩小。

5.2 递归代码示例:阶乘

计算一个数的阶乘 n!n!,公式为:
n!=n×(n−1)!n!=n×(n−1)! 0!=10!=1

递归实现:

python

def factorial(n):
    if n == 0:  # 递归终止条件
        return 1
    return n * factorial(n - 1)  # 递归调用

调用过程:
当计算 factorial(4) 时,发生以下递归调用:

  1. factorial(4) 调用 4 * factorial(3)
  2. factorial(3) 调用 3 * factorial(2)
  3. factorial(2) 调用 2 * factorial(1)
  4. factorial(1) 调用 1 * factorial(0)
  5. factorial(0) 返回 1,递归停止。

最终结果为:
4×3×2×1=244×3×2×1=24

5.3 分治思想

分治算法的核心思想是将一个大问题分成若干小问题,每个子问题独立解决,最后将结果合并。

分治的步骤:

  1. 分解:将问题分成若干子问题。
  2. 解决:递归地解决这些子问题。
  3. 合并:将子问题的解合并成原问题的解。

5.4 快速排序:分治的典型应用

快速排序是分治思想的经典例子。算法核心是选择一个“基准值”,将数组划分为小于基准值的部分和大于基准值的部分,然后递归地对两部分排序。

快速排序代码:

python

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[0]
    less = [x for x in arr[1:] if x <= pivot]
    greater = [x for x in arr[1:] if x > pivot]
    return quick_sort(less) + [pivot] + quick_sort(greater)

过程可视化: 对数组 [5, 3, 8, 6, 2] 执行快速排序:

  1. 选基准值 5,划分为 [3, 2] 和 [8, 6]
  2. 对 [3, 2]
    • 选基准值 3,划分为 [2] 和 []
  3. 对 [8, 6]
    • 选基准值 8,划分为 [6] 和 []
  4. 合并结果:[2, 3, 5, 6, 8]

6. 动态规划的基础理解

动态规划(Dynamic Programming, DP)是一种将复杂问题分解为多个子问题,并利用子问题的解构建原问题解的算法思想。它常用于优化具有重叠子问题的问题。

6.1 动态规划的特点

  1. 最优子结构:问题的最优解可以由子问题的最优解构造。
  2. 重叠子问题:问题的不同部分会重复计算同一子问题。
  3. 状态转移方程:描述如何从子问题的解推导原问题的解。

6.2 动态规划的经典问题:斐波那契数列

斐波那契数列定义为: F(n)=F(n−1)+F(n−2)F(n)=F(n−1)+F(n−2) 其中 F(0)=0,F(1)=1F(0)=0,F(1)=1。

递归实现:


python

def fibonacci_recursive(n):
    if n <= 1:
        return n
    return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)

然而,这种递归效率低下,因为它重复计算了大量子问题。


优化:动态规划实现

通过记忆化存储已解决的子问题,避免重复计算。

python

def fibonacci_dp(n):
    dp = [0] * (n + 1)
    dp[1] = 1
    for i in range(2, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]

对比:

  • 递归时间复杂度:O(2ⁿ)
  • 动态规划时间复杂度:O(n)

6.3 动态规划案例:背包问题

问题描述: 给定一个固定容量的背包和若干物品,每件物品有重量和价值,求在不超过背包容量的情况下,如何选择物品使总价值最大。

动态规划解法:

定义 dp[i][w] 为前 i 个物品在容量为 w 的背包中可获得的最大价值。

状态转移方程: dp[i][w]=max⁡(dp[i−1][w],dp[i−1][w−wt[i]]+val[i])dp[i][w]=max(dp[i−1][w],dp[i−1][w−wt[i]]+val[i])

  • 如果不选第 i 个物品:价值为 dp[i-1][w]
  • 如果选第 i 个物品:价值为 dp[i-1][w-wt[i]] + val[i]

代码实现:

python

def knapsack(values, weights, capacity):
    n = len(values)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(1, capacity + 1):
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])
            else:
                dp[i][w] = dp[i-1][w]
    return dp[n][capacity]

7. 图论算法的简单应用

图论是算法学习的重要组成部分,用于解决节点和边的关系问题。常见的图算法包括最短路径、最小生成树等。


7.1 深度优先搜索(DFS)

原理:从一个节点开始,沿着一条路径探索到尽头,再回溯继续探索其他路径。

代码实现:

python

def dfs(graph, start, visited=None):
    if visited is None:
        visited = set()
    visited.add(start)
    for neighbor in graph[start]:
        if neighbor not in visited:
            dfs(graph, neighbor, visited)
    return visited

7.2 广度优先搜索(BFS)

原理:从一个节点开始,按层次依次遍历邻居。

代码实现:

python

from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    visited.add(start)
    while queue:
        node = queue.popleft()
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)
    return visited

8. 总结与进阶学习建议

  • 学习顺序:从基础排序和搜索开始,逐步深入动态规划和图论算法。
  • 练习平台:尝试在 LeetCodeCodeforces牛客网 上刷题。
  • 进阶方向:研究高级算法如最短路径(Dijkstra)、最大流问题(Ford-Fulkerson)等。

通过本文的系统学习,你已经踏入算法的大门,接下来的关键是持续练习和思考!

标签:arr,入门,递归,复杂度,问题,程序员,算法,dp
From: https://blog.csdn.net/hxyh888/article/details/144051812

相关文章

  • 4G模组LuatOS:超低功耗模式的快速入门指南
    关于超低功耗模式的快速入门指南,我将教大家使用Air201的超低功耗模式下,定时三分钟上传以及G-senser拓展示例。接下来,我们讲解相关示例的具体使用。1.搭建环境新同学建议先看前期的基础知识相关教程,更有助于理解和操作。可以在LuaTools项目管理中新建一个项目,重新选择底层CORE......
  • 什么是算法?算法设计有哪些基本方法?
    算法是解决问题的一种方法或过程,它是一组有限的、明确的步骤,用于解决特定问题或执行特定任务。算法通常具有五个基本特征:输入、输出、确定性、有限性和可行性。算法设计的基本方法包括以下几种:列举法:通过列出所有可能的情况并检验条件满足性来解决问题。这种方法简单但当情......
  • 贪心算法
    https://www.luogu.com.cn/problem/P1803includeincludeincludeusingnamespacestd;vector<pair<int,int>>vct;intmain(){intn;scanf("%d",&n);for(inti=1;i<=n;i++){inta,b;scanf("%d%d",&a,&b)......
  • Linux从入门到精通
    一、源码描述Linux从入门到精通教程二、介绍包含以下章节:1、Linux快速入门2、Linux发展及系统安装3、CentOS系统管理4、Linux必备命令集5、Linux用户及权限管理6、Linux软件包企业实战7、Linux磁盘管理8、NTP服务器企业实战9、DHCP服务器企业实战10、Samba服务......
  • 微信小程序页面配置详解:从入门到精通
    微信小程序页面配置详解:从入门到精通引言随着移动互联网的飞速发展,微信小程序作为一种新兴的应用形式,因其便捷性和丰富的功能而受到广泛欢迎。在小程序的开发过程中,页面配置是至关重要的一环。本文将深入探讨微信小程序的页面配置,帮助开发者从基础到高级逐步掌握这一重要......
  • 程序员修炼之道,从小工到专家 阅读笔记
    时间管理的重要性:时间是程序员最宝贵的资源,要学会如何高效利用。提出了时间管理的基本原则,例如优先任务、避免拖延等。设定优先级:使用“重要-紧急”矩阵来评估和排序任务。先完成重要且紧急的工作,然后是重要但不紧急的任务。专注(DeepWork):强调在编程时应减少干扰,保持专......
  • 朴素贝叶斯分类器算法Python代码实现
    1.朴素贝叶斯分类器简介朴素贝叶斯分类器是机器学习中的一种概率分类方法。它的核心思想是根据贝叶斯定理计算后验概率P(Y∣......
  • 算法与数据结构 1 - 模拟
    模拟介绍正如名称所说,模拟是信息学学生最早接触,也是难度跨度最大的知识点。简单如《A+B问题》《校门外的树》开门见山,没有任何铺垫和掩饰;困难如《猪国杀》《乱西星上的空战》同样开门见山,但谁做谁头疼。因此,本文选择了模拟作为《算法与数据结构》的第一章。引入正如名字所表......
  • 【分块】LibreOJ 6278 数列分块入门2
    题目https://loj.ac/p/6278题解将\(n\)个元素的数组\(a\)按块长\(\sqrt{n}\)进行分块处理。为每个块设置一个懒添加标记\(add[i]\),代表这个区间每个元素共同添加的数值大小。对于任意一个无序数组,想要维护出该数组内小于某个值的元素个数,时间复杂度都将来到\(O(n)\);对......
  • 《程序员修炼之道》第四章读后感
    程序员修炼之道》第四章主要探讨了程序员在编程过程中如何不断提升自己的技术水平,如何养成良好的编码习惯,并通过实践和不断的反思达到“高手”之境。读完这一章,我对程序员的修炼之道有了更深入的理解,以下是我对第四章的读后感。第四章强调了写出“高质量”代码的重要性。在这章中......