学习常用算法——python
创始人
2024-02-23 16:54:54

常用算法

时间复杂度

在日常生活中, 我们描述物体的重量使用的是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) = lognO(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)):# 每完成一次遍历, 结尾就有一个最大元素, 那么我们接下来的遍历过程可以-1for j in range(len(array)-1-i):# array[j]表示curif 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 arraymiddle = len(array) // 2left_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:"""# 为了方便我们理解, 这里选leftpivot = lefti = j = pivot + 1while j <= right:if array[pivot] > array[j]:array[i], array[j] = array[j], array[i]i += 1j += 1array[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)# 子任务的排序不需要考虑pivotquick_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, 51. 写出状态转移方程 fib(n) = fib(n-1) + fib(n-2)2. 考虑最终子问题的初始值:param n::return:"""if n < 2:return nreturn 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_tabledp_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 = 1while fast < len(nums):if nums[fast-1] != nums[fast]:nums[slow] = nums[fast]slow += 1fast += 1return nums, slow
    • 环形链表

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

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

    • 验证回文串

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

    • 二分查找

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

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...