学习常用算法——python

news2025/1/11 1:42:47

常用算法

时间复杂度

在日常生活中, 我们描述物体的重量使用的是kg, 描述物体的长度使用的是m, 那么相对的, 在计算机科学中也需要一种度量来定性地描述算法的运行时间, 这种度量方法称为大O表示法.

  • 声明f(n)作为我们的函数, n表示的参数. 不同的参数会导致算法运行的时间不同. 那么最坏的情况就是T(n)

  • T(n) = O(f(n)), f(n)表示表达式执行的次数之和.

    def foo(n):
    	for i in range(n):
    		表达式
    f(n) = n
    T(n) = O(n)
    该算法的时间复杂度为O(n)
    
    • 键对值取值

      O(1)
      
    • 嵌套的for循环

      for i in range(n):
      	for j in range(m):
      		表达式
      
      O(n*m)
      
    • 执行多次的for循环

      for i in range(n):
      	表达式
      for j in range(n):
      	表达式
      
      O(2n)
      

      上述代码时间复杂度为O(2n), 但是在大O表示法当中, 可以忽略系数和常数, 所以当前的时间复杂度也可以写为O(n)

    • 二分法

      2**3 = 8
      3 = log8
      f(n) = logn
      
      O(logn)
      

常见的排序

https://www.runoob.com/w3cnote/selection-sort.html
  • 选择排序
  • 冒泡排序
  • 插入排序
  • 归并排序
  • 快速排序
  • 桶排序

选择排序O(n²)

  • 从当前元素后的无序元素数组中找到最小值

  • 如果找到了, 将该元素与当前元素交换

  • 如果没找到, 说明当前元素是后续无序数组中的最小值

  • 执行上述过程n-1

    def selection_sort(array):
        # 最后一个元素无序比较, 所以执行n-1次
        for i in range(len(array)-1):
            min_index = i
            # 从当前index+1的位置开始遍历
            for j in range(i+1, len(array)):
                if array[j] < array[min_index]:
                    min_index = j
    
            # 如果找到最小值
            if i != min_index:
                array[i], array[min_index] = array[min_index], array[i]
    

冒泡排序O(n²)

  • 本质就是将最大值移动到数组末端

  • 比较cur和next, 如果cur > next, 则交换两者

  • 执行上述过程n

    def bubble_sort(array):
        for i in range(len(array)):
            # 每完成一次遍历, 结尾就有一个最大元素, 那么我们接下来的遍历过程可以-1
            for j in range(len(array)-1-i):
                # array[j]表示cur
                if array[j] > array[j+1]:
                    array[j], array[j+1] = array[j+1], array[j]
    

归并排序

归并排序适用于两个有序数组进行重新排序

6cZ72.png

  • 合并两个有序数组

    • 声明一个新数组, 该数组的长度 = 左数组 + 右数组

    • 比较两个左数组和右数组的头元素, 小的值将添加到新数组的头部

    • 重复步骤2, 直到某个数组没有元素为止

    • 将剩余元素的数组直接添加到新数组末端

      def merge(left, right):
          """
          :param left:  左数组
          :param right: 右数组
          :return:
          """
          # 因为我们是python的list, 所以不用考虑声明长度
          result = []
          while left and right:
              # 比较两个数组的头元素
              if left[0] <= right[0]:
                  # 通过list.pop删除并返回指定位置的元素
                  result.append(left.pop(0))
              else:
                  result.append(right.pop(0))
      
          while left:
              result.append(left.pop(0))
      
          while right:
              result.append(right.pop(0))
      
          return result
      
  • 对无序的数组进行重新排序

    无序数组[a, b, c, d, e] 可以拆分成[a, b], [c, d, e]

    [a, b] 排序后得到[b, a]

    [c, d, e]可以拆分为[c, d], [e]

    [c, d] 排序后得到[d, c]

    得到了三组有序数组

    [d, c], [e] 归并后得到[d, c, e]

    [d, c, e], [b, a]归并后得到[d, b, c, a, e]

    def merge_sort(array):
        """
        使用递归实现分治思想
        :param array:
        :return:
        """
        # 一旦使用递归, 首先要考虑的是退出逻辑
        if len(array) < 2:
            return array
    
        middle = len(array) // 2
        left_array = array[:middle]
        right_array = array[middle:]
        return merge(merge_sort(left_array), merge_sort(right_array))
    

快速排序

  • 从数组中选定一个基准(pivot)

  • 遍历基础之后的元素, 将比它小的元素置于左边, 比它大的元素置于右边

    • 声明i, j两个指针
    • 比较pivot和j的值, 如果pivot > j, 交换ij的值, i+1
    • 不管比较的结果, j + 1
    • j到达末端, pivot和i-1交换值
    def partition(array, left, right):
        """
        :param array:
        :param left: 左端索引
        :param right: 右端索引
        :return:
        """
        # 为了方便我们理解, 这里选left
        pivot = left
    
        i = j = pivot + 1
        while j <= right:
            if array[pivot] > array[j]:
                array[i], array[j] = array[j], array[i]
                i += 1
            j += 1
    
        array[i-1], array[pivot] = array[pivot], array[i-1]
        # 返回原pivot值的新索引
        return i-1
    
  • 实现分治思想

    def quick_sort(array, left, right):
        if left < right:
            pivot = partition(array, left, right)
            # 子任务的排序不需要考虑pivot
            quick_sort(array, left, pivot-1)
            quick_sort(array, pivot+1, right)
    

动态规划

https://baike.baidu.com/item/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/529408?fr=aladdin

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程. 在多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化的过程为动态规划方法.

简单地说, 动态规划的本质就是穷举所有的可能性, 寻找最优解.

  • 动态规划的应用

    一切求最值得情况, 都可以通过动态规划来解决.

    • 资金管理问题
    • 资源分配问题
    • 最短路径问题
    • 最优决策问题
  • 动态规划的思想

    • 通过分治法来实现动态规划决策的过程

      • 将待解决的问题分解成若干个子问题, 先求解子问题.
    • 动态规划会产生大量的重复计算.

    • 动态规划中当前状态取决于上一阶段状态本阶段的状态

      • 基于2, 3两点. 所以动态规划使用的是数组来做为缓存数据结构, 而不是hashmap, 该数组被称位dp_table(dynamic programming table)
  • 动态规划的解题步骤

    • 列出状态转移方程

      # 暂时简单地理解为可以用来表达任意子问题的方程式
      dp_table[n] = f(dp_table[n-1])
      
    • 初始化长度为n的dp_table

      • 分治思想最终会达到拆无可拆的地步, 该情形下就是dp_table的初始值

        在斐波那契函数当中, 索引值小于2就是拆无可拆的地步

    • 穷举所有决策和记录产生的结果/状态

      • dp_table最终会被填满, 即所有的可能性已经穷举完毕
      • 一般来说, dp_table[-1]就是我们要求的最值

题集

  • 斐波那契函数

    斐波那契函数严格来说不属于动态规划, 因为它不必求最值. 这里我们只用它体现将问题变成求解子问题的过程

    def fib(n):
        """
        斐波那契函数当前值等于前两个值之和
        0, 1, 1, 2, 3, 5
    
        1. 写出状态转移方程 fib(n) = fib(n-1) + fib(n-2)
        2. 考虑最终子问题的初始值
        :param n:
        :return:
        """
        if n < 2:
            return n
    
        return fib(n-1) + fib(n-2)
    
  • 爬楼梯

    def upstairs(n):
        """
        台阶1: [1]
        台阶2: [1 + 1], [0 + 2]
        台阶3: ([1 + 1 + 1], [0 + 2 + 1]), ([1 + 2])
        台阶4: ([1 + 1 + 1 + 1], [0 + 2 + 1 + 1], [1 + 2 + 1]), ([1 + 1 + 2], [0 + 2 + 2])
    
        dp_table[n] = dp_table[n-1] + dp_table[n-2]
        :param n:
        :return:
        """
        dp_table = [0] * n
    
        # 初始化dp_table
        dp_table[0], dp_table[1] = 1, 2
    
        # 填满dp_table, 遍历n即可
        for i in range(2, n):
            dp_table[i] = dp_table[i-1] + dp_table[i-2]
    
        return dp_table[-1]
    
    
  • 买股票的最佳时机1

    def max_profit(prices):
        """
        - 只有两次操作, 一次为买, 一次为卖
        - 最大利润 = 最高位 - 最低位
    
        股票问题的难点在于要考虑所谓的状态不仅仅表示上一个状态的值(也就是利润的最大值)
        还要考虑股票的状态, 因为股票买了之后持有是有价值的, 但是暂时不能算作利润.
        最终利润 = 持有的股票 - 当前股价. 也点出了股票的本质就是未来的期望
                        卖           买
        dp_table = [[最大利润,  最低股价的股票]...]
        :param prices:
        :return:
        """
        dp_table = [[0, 0] for _ in range(len(prices))]
    
        dp_table[0] = [0, 7]
        for i in range(1, len(prices)):
            # 最大利润 = max(prev最大利润, cur股价 - prv持有股价)
            dp_table[i][0] = max(dp_table[i-1][0], prices[i] - dp_table[i-1][1])
            # 持有股票 = min(prev持有股价, cur股价)
            dp_table[i][1] = min(dp_table[i-1][1], prices[i])
        return dp_table
    
    
  • 买股票的最佳时机2

    def max_profit2(prices):
        """
        - 在不重复购买的情况下, 可以任意操作
        - 最大利润 = 交易操作的累计和
    
        这道题和上一道题的区别在于, 因为是多次操作, 那我们要寻找的不是最低价的股票
        而是要得到每次买卖操作的最大利润.
        同样的, 持有的股票不能算作当前利润, 因为股票终究是要卖出才能得到最大收益.
        所以要分两种情况:
                       持有股票: 最大利润 = 交易所得利润(同时持有股票)
                       不持有股票: 最大利润 = 交易所得利润 + 上一阶段如果持有股票 - 当前股价
    
                       卖     买
        dp_table = [[不持有, 持有]...]
        :param prices:
        :return:
        """
        dp_table = [[0, 0] for _ in range(len(prices))]
    
        dp_table[0] = [0, -prices[0]]
    
        for i in range(1, len(prices)):
            # 不持有的情况: 1. 上一次没有持有, 不存在卖的操作; 2. 上一次持有, 这次卖了
            dp_table[i][0] = max(dp_table[i-1][0], dp_table[i-1][1] + prices[i])
            # 持有的情况: 1. 上一次持有, 不存在买的操作; 2. 上一次不持有, 这次买了
            dp_table[i][1] = max(dp_table[i-1][1], dp_table[i-1][0] - prices[i])
        return dp_table[-1][0]
    
    

双指针

  • 左右指针

    强调两个指针相对的位置

    • 二分查找
    • 两数之和
    • 链表翻转
    • 滑动窗口
  • 快慢指针

    强调两个指针移动速度, 或者叫步长

    • 判断链表中是否有环
    • 链表中点
    • 数组去重
    • 快排

题集

  • 快慢指针

    • 删除有序数组中的重复项

      def drop_duplicates(nums):
          """
          移除数组中的重复元素
      
          - 直接对nums进行修改
          :param nums:
          :return:
          """
          # 指示当前需要判断重复的元素的索引
          slow = 1
      
          # 用来遍历数组, 查找与slow值不同的元素
          # 如果找到了, slow的值将被赋值为fast代表的值
          fast = 1
      
          while fast < len(nums):
              if nums[fast-1] != nums[fast]:
                  nums[slow] = nums[fast]
                  slow += 1
              fast += 1
      
          return nums, slow
      
      
      
    • 环形链表

      def has_cycle(head):
          slow = fast = head
      
          # 如果最终fast为null, 说明当前到达链表的末端
          # 说明当前链表没有环
          while fast:
              fast = fast.next.next
              slow = slow.next
              if slow == fast:
                  return True
          return False
      
    • 环形链表2

      def detect_cycle(head):
          """
          slow = a + b
          fast = a + b + c + b
      
          fast = 2slow
      
             a + b + c + b = 2a + 2b
          => c = a
          :param head:
          :return:
          """
          slow = fast = head
          while fast:
              fast = fast.next.next
              slow = slow.next
              if slow == fast:
                  # 停止的位置就是快慢指针相遇点
                  break
      
          # 当前无环
          if not fast:
              return None
      
          slow = head
          while slow != fast:
              # 因为a=c, slow和fast以同样的速度前进, 相交点就是入环点
              slow = slow.next
              fast = fast.next
          return slow
      
      
  • 左右指针

    • 验证回文串

      def is_palindrome(string):
          left = 0
          right = len(string) - 1
          while right >= left:
              while not string[left].isalpha():
                  left += 1
      
              while not string[right].isalpha():
                  right -= 1
      
              if string[left].lower() == string[right].lower():
                  left += 1
                  right -= 1
              else:
                  print("break", left, right)
                  break
      
          return left-1 == right+1 or left-1 == right
      
    • 合并两个有序数组

    • 二分查找

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

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

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

相关文章

超低延时4K级可定制化专业视觉计算平台

> 内置超低延时4K30 ISP IP&#xff0c;ISP延时 0.7 ms > 内置GigE vision IP支持 GigE Vision2.0、GenICam V2.4.0标准&#xff0c;支持用户自定义XML描述文件 > 内置工业机器视觉行业标准的U3 vison IP > 基于FPGA,支持Bayer、YCbCr、RGB等格式,满足高帧率/高…

ElementUI实现在下拉列表里面进行搜索

分析: 首先我们需要实现上图的效果,然后Element-UI的el-select是没有的,所以需要自己写我们需要用到el-popover组件,然后使用它的v-model"visible"来实现控制显示我们在el-popover的slot"reference" 放一个el-select 使用popper-append-to-body"false…

C++ 之 移动构造函数

1、左值和右值 C( 包括 C) 中所有的表达式和变量要么是左值&#xff0c;要么是右值。 通俗的左值的定义就是非临时对象&#xff0c;那些可以在多条语句中使用的对象&#xff0c;表达式结束后依然存在的持久化对象&#xff0c;所有的具名变量或者对象都是左值。右值是指临时的…

<Android开发> Android vold - 第一篇 vold前言简介

本次主要讲解存储模块如U盘等设备在android设备中的管理和使用的模块。本次主要基于android 8.1版本进行解析。不同android版本 vold的内容可能会有所差异。读者可对比阅读解析。 1 Vold介绍 Android中Vold是volume Daemon,即Volume守护进程;Android没有使用Linux平台下的ud…

泛型的介绍和使用方法

目录 一、泛型概述 二、泛型类 三、泛型接口 1.直接在实现类中确定好类型 2.实现类也写成泛型类 四、泛型方法 五、泛型好处 六、泛型集合 1.概念 2.特点 一、泛型概述 1. 本质是参数化类型&#xff0c;把类型作为参数传递。 2. 常见的形式有泛型类、泛型接口、泛型…

【虚幻引擎】UE4/UE5 后期处理盒子(PostProcessVolume)

一、简介 PostProcessVolume&#xff08;后期处理盒子&#xff09;&#xff1a;UE4非常强大的一个后期处理&#xff0c;可以调节画面的色彩&#xff0c;相机的景深&#xff0c;视频的输出效果&#xff0c;环境的光线构造&#xff0c;电影级的氛围感。 二、参数介绍 一、场景中…

驱动开发 Linux按键中断点灯

华清远见上海中心22071班 三个按键实现按键中断&#xff0c; key1->led1 key2->led2 key3->led3 按键按一下灯亮&#xff0c;再按一下灯灭 #include <linux/module.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/f…

外贸供应链ERP怎么选?全流程综合管理解析

随着外贸体制深入改革、进出口权放开等&#xff0c;以往处于垄断地位&#xff0c;享有种种优惠政策的外贸企业&#xff0c;将面临越来越严峻的国内外市场的竞争及各种挑战。长期以来形成的相对落后的管理体制和经营模式&#xff0c;严重地影响外贸企业在新形势下的生存和发展。…

m基于GA遗传优化+SA模拟退火的混合改进算法的多产品多机器生产优化matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 这里&#xff0c;我们首先介绍一下改进算法的基本原理&#xff0c;按照前面说的&#xff0c;这里我们主要将GA和SA进行合并。 这里&#xff0c;我研究了下&#xff0c;将两种算法做如下方法的结合…

Microcorruption 第一关 Tutorial

Microcorruptioin 第一关 Tutorial 首先进入Tutorial这一关&#xff0c;这是闯关页面。 在主函数设置断点&#xff0c;控制台输入"break main"或"b main"或手动点击反汇编栏中main函数的第一行设置断点。 该闯关游戏主要是破解密码&#xff0c;查看主函数…

前端开发免费资源分享

Fancy-Border-Radius 地址&#xff1a;https://9elements.github.io/fancy-border-radius/ 简介&#xff1a;在线编辑border-radius的可视化工具&#xff0c;通过调节可以很方便地帮我们生成想要的形状&#xff0c;然后直接复制下面的css代码&#xff0c;即可使用 Make some …

sCrypt 合约中的椭圆曲线算法:第二部分

我们在脚本中实现了椭圆曲线 (EC) 算法。在之前的实现中&#xff0c;我们进行链下计算并在脚本中验证结果。我们这里直接用脚本计算。 基于EC的应用非常多&#xff0c;特别是在密码学领域&#xff0c;如数字签名、加密、承诺方案等。作为具体示例&#xff0c;我们重新实现了 E…

11.28作业

实现对点灯所涉及函数的封装 1.头文件 #ifndef __GPIO_H__ #define __GPIO_H__ //结构体封装 typedef struct{volatile unsigned int MODER;volatile unsigned int OTYPER;volatile unsigned int OSPEEDR;volatile unsigned int PUPDR;volatile unsigned int IDR;volatile un…

Kotlin进阶指南 - 单元测试

为了减少一些功能繁琐的测试流程&#xff0c;单元测试是提升开发效率的有效方式之一 在早些年的时候我有记录过一篇 Android 使用单元测试&#xff0c;只不过当时更多的针对 Java 方面的单元测试&#xff1b;在使用 Kotlin 后&#xff0c;我发现单元测试有点不同&#xff0c;好…

Nacos注册中心和服务消费方式

目录 一&#xff0c;服务治理介绍 什么是服务治理&#xff1f; 常见的注册中心 二&#xff0c;nacos简介 三&#xff0c;搭建nacos环境 四&#xff0c;代码演示 五&#xff0c;基于Feign实现服务调用 什么是Feign Feign的使用 Feign参数传递 一&#xff0c;服务治理介…

全国心力衰竭日:重症心衰的黑科技——永久型人工心脏

今天是第8个“全国心力衰竭日”。近几年&#xff0c;中国逐渐老龄化&#xff0c;心衰则是老龄化面临的严峻问题。我国心衰患病率估计已达1.3%&#xff0c;至少有1000万心力衰竭患者。中国已成为世界上拥有最大心衰患者群的国家之一。心力衰竭作为大多数心血管疾病的终末阶段&am…

如何在 docker 容器使用 nginx 实现反向代理统一站点入口

在微服务架构下&#xff0c;我们会部署很多微服务来实现我们的系统。每个微服务会有不同的端口。而用户在访问我们的站点时希望通过统一的端口来访问所有的服务&#xff0c;因为在很多情况下用户只能通过 80 或者 443 端口访问外界服务。 这个时候我们就可以使用反向代理来实现…

云上“两地三中心”,中小企业都用得起的多保险灾备方案

在云时代&#xff0c;大部分中小型企业都奔跑在云上或是服务器托管公司。任何规模的数据中心服务中断都会让你的企业踩雷。据统计&#xff0c;80%的数据中心服务中断都是由服务器硬件造成的。 据万博智云不完全统计&#xff1a; 2021年3月&#xff0c;一场大火完全摧毁了OVH在…

[附源码]计算机毕业设计SpringBoot蛋糕购物商城

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

如何将pdf转word?这几个软件可以做到文档格式转换

我们在日常办公中使用较为频繁的就是pdf和word两种文件格式&#xff0c;pdf的兼容性较好&#xff0c;就算跨设备传输也不会出现乱码的情况&#xff1b;word文档可以直接进行编辑修改&#xff0c;各有各的好处。如果我们想对pdf的内容进行修改的话&#xff0c;就需要把pdf文件转…