• 常用算法总结


    常用查找方法

    1、递归

    1. 在函数内调用当前函数本身的函数就是递归函数
    2. 有结束条件
    def fibonacci(n):
        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return fibonacci(n-1) + fibonacci(n-2)
    print([fibonacci(x) for x in range(10)])5

    2、二分法查找(折半查找)

    1.二分法查找的思路
    (1)从有序数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行(2)。

    (2)如果目标元素大于中间元素,则在数组大于中间元素的那一半区域查找;如果目标元素小于中间元素,则在数组小于中间元素的那一半区域查找,然后重复步骤(1)的操作。

    (3)如果某一步数组为空,则表示找不到目标元素,结束查找。

    l = list(range(1,101))
    def bin_search(data_set,val):
       low = 0
       high = len(data_set) - 1
       while low <= high:
          mid = (low+high)//2
          if data_set[mid] == val:
             return mid
          elif data_set[mid] < val:
             low = mid + 1
          else:
             high = mid - 1
       return
    n = bin_search(l,11)
    print(n)            # 返回结果是: 10

    常用排序方法

    1、排序方法比较

                    

    一、冒泡排序(最好是O(n), 最坏O(n2))

    原理:相邻的两个元素对比,大的数后推,遍历整个列表一遍后,将最大项以冒泡的方式排列i到列表末尾

    def bubble_sort(li):
        for i in range(len(li)-1):
            for j in range(len(li)-i-1):
                if li[j] > li[j+1]:
                    li[j],li[j+1]=li[j+1],li[j]
    
    li = [1,5,2,6,3,7,4,8,9,0]
    bubble_sort(li)
    print(li)               # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    二、选择排序

    1、先假定第一个是最小的,依次与其他数比,如果其他数中有比第一个数小就假定这个更小的最小
    2、再比,第一轮就可以找到最小的那个放到0号位置,然后在假定1号位置数最小与剩下比较,再找到第二小的数放到第1号位置

    import random
    def select_sort(li):
       for i in range(len(li) - 1):
          min_loc = i                        #开始先假设0号位置的值最小
          for j in range(i+1, len(li)):      #循环无序区,依次比较,小于min_loc就暂定他的下标最小
             if li[j] < li[min_loc]:         #所以内层for循环每执行一次就选出一个小值
                min_loc = j
          li[i], li[min_loc] = li[min_loc],li[i]
           
    li = [1,5,2,6,3,7,4,8,9,0]
    select_sort(li)
    print(li)               # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    三、插入排序

    1、列表被分为有序区和无序区两个部分,最初有序区只有一个元素
    2、每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空

    import random
    
    def insert_sort(li):
       for i in range(1, len(li)):
          tmp = li[i]     #tmp是无序区取出的一个数
          j = i - 1       #li[j]是有序区最大的那个数
          while j >= 0 and li[j] > tmp:
             # li[j]是有序区最大的数,tmp是无序区取出的一个数,tmp从有序区最大的那个数开始比
             # 小就调换位置,直到找到有序区中值不大于tmp的结束
             li[j+1]=li[j]    #将有序区最右边的数向右移一个位置
             j = j - 1
          li[j + 1] = tmp       #将tmp放到以前有序区最大数的位置,再依次与前一个数比较
    data = list(range(100))
    random.shuffle(data)        #将有序列表打乱
    insert_sort(data)
    print(data)
    def insert_sort(items):
        for i in range(1,len(items)):
            item_insert = items[i]
            j = i - 1
            while j >= 0:
                if item_insert < items[j]:
                    items[j+1] = items[j]
                    j -= 1
                else:
                    break
            items[j+1] = item_insert
        return items
    
    items = [1,22,0,8,9]
    print(insert_sort(items))

    四、快速排序

    注:快排代码实现(类似于二叉树 递归调用)----右手左手一个慢动作,右手左手一个慢动作重播

    import random
    import sys
    sys.setrecursionlimit(10000000)             #设置系统最大递归深度
    
    def quick_sort(data, left, right):
        if left < right:
            mid = partition(data, left, right)    # mid返回的是上一个用来排序那个数的下标
            quick_sort(data, left, mid - 1)
            quick_sort(data, mid + 1,right)
    
    # 每执行一次partition函数都可以实现将某个数左边都比这个数小右边都比这个数大
    def partition(data, left, right):
        tmp = data[left]
        while left < right:
            while left < right and data[right] >= tmp:     # 从右向左找小于tmp的数放到左边空位置
                right -= 1
            data[left] = data[right]                       # 将右边小于tmp值得数放到左边空位置
            while left < right and data[left] <= tmp:      # 从左向右找到大于tmp的值放到右边空位置
                left += 1
            data[right] = data[left]                       # 将右边大于tmp值得数放到右边空位置
        data[left] = tmp
        return left
    
    data = list(range(100))
    random.shuffle(data)                                 #将有序列表打乱
    quick_sort(data, 0, len(data) - 1)
    print(data)
    def quick_sort(arr):
        '''''
        模拟栈操作实现非递归的快速排序
        '''
        if len(arr) < 2:
            return arr
        stack = []
        stack.append(len(arr)-1)
        stack.append(0)
        while stack:
            l = stack.pop()
            r = stack.pop()
            index = partition(arr, l, r)
            if l < index - 1:
                stack.append(index - 1)
                stack.append(l)
            if r > index + 1:
                stack.append(r)
                stack.append(index + 1)
    
    
    def partition(arr, start, end):
        # 分区操作,返回基准线下标
        pivot = arr[start]
        while start < end:
            while start < end and arr[end] >= pivot:
                end -= 1
            arr[start] = arr[end]
            while start < end and arr[start] <= pivot:
                start += 1
            arr[end] = arr[start]
        # 此时start = end
        arr[start] = pivot
        return start
    
    lst = [1,3,5,7,9,2,4,6,8,10]
    quick_sort(lst)
    print lst   # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    def quick(list):
        if len(list) < 2:
            return list
    
        tmp = list[0]  # 临时变量 可以取随机值
        left = [x for x in list[1:] if x <= tmp]  # 左列表
        right = [x for x in list[1:] if x > tmp]  # 右列表
        return quick(left) + [tmp] + quick(right)
    
    li = [4,3,7,5,8,2]
    print quick(li)  # [2, 3, 4, 5, 7, 8]
    
    #### 对[4,3,7,5,8,2]排序
    '''
    [3, 2] + [4] + [7, 5, 8]                 # tmp = [4]
    [2] + [3] + [4] + [7, 5, 8]              # tmp = [3] 此时对[3, 2]这个列表进行排序
    [2] + [3] + [4] + [5] + [7] + [8]        # tmp = [7] 此时对[7, 5, 8]这个列表进行排序
    '''

    2、快排原理

                  

    # 从排序前--------> 到P归位 经历过程(前面都比5小后面都比5大)
    # 1、    首先从右向左比较,取出列表第一个元素5(第一个位置就空出来)与列表最后一个元素8比较,8>5不换位置
    # 2、    用5与-2位置的9比,5<9不换位置
    # 3、    5与-3位置的2比较,2<5,将-3位置的5放到1号位置,那么-3号位置空出来了,然后从左往右比较
    # 4、    5与2号位置的7比,5<7,将7放到-3号位置,2号位置空出来了,在从右往左比
    # 5、    -4号位置的1小于5将1放到空出的2号位置,-4位置空出来了,再从右向左比
    # 6、    这样第一次循环就实现了5放到列表中间,前面的都比5大,后面的都比5小

    3、快排与冒泡时间复杂度对比

    最好情况

    一般情况

    最坏情况

    快排

    O(nlogn)

    O(nlogn)

    O(n^2)

    冒泡

    O(n)

    O(n^2)

    O(n^2)

    4、快排最坏时间复杂度为何为O(n2)

     1. 每次划分只能将序列分为一个元素与其他元素两部分,这时的快速排序退化为冒泡排序

     2. 如果用数画出来,得到的将会是一棵单斜树,也就是说所有所有的节点只有左(右)节点的树;平均时间复杂度O(n*logn)

    五、堆排序

     1、堆的定义

      1、堆中某个节点的值总是不大于或不小于其父节点的值;

      2、堆总是一棵完全二叉树

      3、完全二叉树定义:

      1)若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数
      2)第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

      4、完全二叉树特性

      1)一个高度为h的完全二叉树最多有  2-1 个节点

      2)根为 i 号节点,左孩子 为 2i、 右孩子为 2i+1,父亲节点 (i – 1) / 2

      3)一个满二叉树 第 m层节点个数 等于 2m-1 个

      4)推倒一个h层的满二叉树为何 有 2h -1 个节点

              s = 20 + 21 + 22  +  2+ ...... + 2h-1

                   s = 21 + 21 + 22 + 23 ...... + 2h-1 - 1

              s = 2*21 + 22 + 23 ...... + 2h-1 - 1

              s = 22 + 22 + 23 ...... + 2h-1 - 1

              s = 2*22 + 23 ...... + 2h-1 - 1

              s = 2h -1

     2、调长定义(节点的左右子树都是堆但自己不是堆)

        1. 调长图解

                          

        2. 调长原理

            1、首先将2拿出来与9和7比,这里面9最大,就用9作为根
            2、2放到9以前的位置,与8和5比,8最大放到开始9的位置
            3、2放到起始8的位置与6和4比,6最大,就出现了右边那张图了

      3、构造堆:从最后一个有孩子的父亲开始 

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    def sift(data, low, high):
       '''  构造堆  堆定义:堆中某节点的值总是不大于或不小于父节点的值
       :param data: 传入的待排序的列表
       :param low:  需要进行排序的那个小堆的根对应的号
       :param high: 需要进行排序那个小堆最大的那个号
       :return:
       '''
       i = low            #i最开始创建堆时是最后一个有孩子的父亲对应根的号
       j = 2 * i+ 1       #j子堆左孩子对应的号
       tmp = data[i]      #tmp是子堆中原本根的值(拿出最高领导)
       while j <= high:  #只要没到子堆的最后(每次向下找一层)  #孩子在堆里
          # if j < high and data[j] < data[j + 1]:
          if j + 1 <= high and data[j] < data[j + 1]: #如果有右孩纸,且比左孩子大
             j += 1
          if tmp < data[j]:        #如果孩子还比子堆原有根的值tmp大,就将孩子放到子堆的根
             data[i] = data[j]     #孩子成为子堆的根
             i = j                 #孩子成为新父亲(向下再找一层)
             j = 2 * i + 1         #新孩子  (此时如果j<=high证明还有孩,继续找)
          else:
             break                 #如果能干就跳出循环就会流出一个空位
       data[i] = tmp                #最高领导放到父亲位置
    
    def heap_sort(data):
       '''调整堆'''
       n = len(data)
       # n//2-1 就是最后一个有孩子的父亲那个子堆根的位置
       for i in range(n // 2 - 1, -1, -1):  #开始位置,结束位置, 步长       这个for循环构建堆
          # for循环输出的是: (n // 2 - 1 ) ~ 0 之间的数
          sift(data, i , n-1)     # i是子堆的根,n-1是堆中最后一个元素
    
    
    data = [20,50,20,60,70,10,80,30,40]
    heap_sort(data)
    print data  # [80, 70, 20, 60, 50, 10, 20, 30, 40]

      1、在构造有序堆时,我们开始只需要扫描一半的元素(n/2-1 ~ 0)即可,为什么?
      2、因为(n/2-1)~0的节点才有子节点,如图1,n=8,(n/2-1) = 3 即3 2 1 0这个四个节点才有子节点
      3、所以代码4~6行for循环的作用就是将3 2 1 0这四个节点从下到上,从右到左的与它自己的子节点比较并调整最终形成大顶堆,过程如下:
      4、第一次for循环将节点3和它的子节点7 8的元素进行比较,最大者作为父节点(即元素60作为父节点)

                               

      5、第二次for循环将节点2和它的子节点5 6的元素进行比较,最大者为父节点(元素80作为父节点)

                              

      6、第三次for循环将节点1和它的子节点3 4的元素进行比较,最大者为父节点(元素70作为父节点)

                                

      7、第四次for循环将节点0和它的子节点1 2的元素进行比较,最大者为父节点(元素80作为父节点)

                            

         注:元素20和元素80交换后,20所在的节点还有子节点,所以还要再和它的子节点5 6的元素进行比较,这就是28行代码 i = j 的原因

      8、至此有序堆已经构造好了!如上面右图

                            

     4、调整堆

      1. 调整堆过程

       1、建立堆
       2、通过调长,得到堆顶元素,为最大元素
       3、去掉堆顶,将最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
       4、堆顶元素为第二大元素
       5、重复步骤3,直到堆变空

      2. 调整堆具体步骤

       1、 堆顶元素80和尾40交换后-->调整堆

                                      

       2、堆顶元素70和尾30交换后-->调整堆

                                  

       3、堆顶元素60尾元素20交换后-->调整堆

                               

       4、其他依次类推,最终已排好序的元素如下

                              

      5、堆排序代码实现

    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    import random
    
    def sift(data, low, high):
        '''  构造堆  堆定义:堆中某节点的值总是不大于或不小于父节点的值
        :param data: 传入的待排序的列表
        :param low:  需要进行排序的那个小堆的根对应的号
        :param high: 需要进行排序那个小堆最大的那个号
        :return:
        '''
        root = low  # root最开始创建堆时是最后一个有孩子的父亲对应根的号
        child = 2 * root + 1  # child子堆左孩子对应的号
        tmp = data[root]  # tmp是子堆中原本根的值(拿出最高领导)
        while child <= high:  # 只要没到子堆的最后(每次向下找一层)  #孩子在堆里
            if child + 1 <= high and data[child] < data[child + 1]:  # 如果有右孩纸,且比左孩子大
                child += 1
            if tmp < data[child]:  # 如果孩子还比子堆原有根的值tmp大,就将孩子放到子堆的根
                data[root] = data[child]  # 孩子成为子堆的根
                root = child  # 孩子成为新父亲(向下再找一层)
                child = 2 * root + 1  # 新孩子  (此时如果child<=high证明还有孩,继续找)
            else:
                break  # 如果能干就跳出循环就会流出一个空位
        data[root] = tmp  # 最高领导放到父亲位置
    
    def heap_sort(data):
        '''调整堆'''
        n = len(data)
        ''' n//2-1 就是最后一个有孩子的父亲那个子堆根的位置 '''
        for i in range(n // 2 - 1, -1, -1):  # 开始位置,结束位置, 步长       这个for循环构建堆
            # for循环输出的是: (n // 2 - 1 ) ~ 0 之间的数
            sift(data, i, n - 1)  # i是子堆的根,n-1是堆中最后一个元素
        # 堆建好了,后下面就是挨个出数
        for i in range(n - 1, -1, -1):  # i指向堆的最后        这个for循环出数然后,调长调整堆
            # for循环输出的是 : n-1 ~ 0之间所有的数,n-1就是这个堆最后那个数的位置
            data[0], data[i] = data[i], data[0]  # 将堆的第一个和最后一个值调换位置(将最大数放到最后)
            sift(data, 0, i - 1)  # 将出数后的部分重新构建堆(调长)
    
    
    data = list(range(100))
    random.shuffle(data)  # 将有序列表打乱
    heap_sort(data)
    print(data)

      6、初始化建堆过程时间:O(n) 公式推倒

        参考博客:https://www.cnblogs.com/GHzz/p/9635161.html

        说明:建堆时间复杂度指初始化堆需要调整父节点和子节点顺序次数

    ''' 假设高度为:k '''
    #### 1、推倒第i层的总时间:s = 2^( i - 1 )  *  ( k - i )
    # 说明:如果在最差的条件下,就是比较次数后还要交换;因为这个是常数,所以提出来后可以忽略;
    '''
    1. 2^( i - 1):表示该层上有多少个元素
    2. ( k - i):表示子树上要下调比较的次数:第一层节点需要调整(h-1)次,最下层非叶子节点需要调整1次。
    3. 推倒
        倒数第1层下调次数:s = 2^( i - 1 )  *  0 
        倒数第2层下调次数:s = 2^( i - 1 )  *  1
        倒数第3层下调次数:s = 2^( i - 1 )  *  2
        倒数第i层下调次数:s = 2^( i - 1 )  *  ( k - i )
    '''
    
    #### 2、一次新建堆总时间:S = n - longn -1  # 根据1中公式带人推倒
    # S = 2^(k-2) * 1 + 2^(k-3)*2.....+2*(k-2)+2^(0)*(k-1)  ===> 因为叶子层不用交换,所以i从 k-1 开始到 1;
    '''
    S = 2^(k-2) * 1 + 2^(k-3)*2.....+2*(k-2)+2^(0)*(k-1)     # 等式左右乘上2,然后和原来的等式相减,就变成了:
    S = 2^(k - 1) + 2^(k - 2) + 2^(k - 3) ..... + 2 - (k-1)
    S = 2^k -k -1                                            # 又因为k为完全二叉树的深度,所以 
    (2^(k-1)) <=  n < (2^k - 1 )                             # 两边同时对2取对数,简单可得
    k = logn                                                 # 实际计算得到应该是 log(n+1) < k <= logn 
    
    综上所述得到:S = n - longn -1,所以时间复杂度为:O(n)
    '''

      7、堆排序时间:O(nlogn) 公式推倒

        1)推导方法1:

          循环  n -1 次,每次都是从根节点往下循环查找,所以每一次时间是 logn,总时间:logn(n-1) = nlogn  - logn ;

        2)推导方法2:

          1. 在一个堆中一次调长(调整堆)时间复杂度: log(n)

          2. 排序时一次出栈顶元素需要循环 n次,每次时间复杂度为:log(n)

          3. 所以总时间复杂度:nlog(n)

    1.6 归并排序(递归调用) 

      1、归并原理图

                     

      2、归并排序代码(时间复杂度:O(nlogn))

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    def merge(li, low, mid, high):
       '''
       :param li:      带排序列表
       :param low:     列表中第一个元素下标,一般是:0
       :param mid:     列表中间位置下标
       :param high:    列表最后位置下标
       :return:
       '''
       i = low
       j = mid + 1
       ltmp = []
       while i <= mid and j <= high:
          if li[i] < li[j]:
             ltmp.append(li[i])
             i += 1
          else:
             ltmp.append(li[j])
             j += 1
       while i <= mid:
          ltmp.append(li[i])
          i += 1
       while j <= high:
          ltmp.append(li[j])
          j += 1
       li[low:high+1] = ltmp
    
    def mergesort(li, low, high):
       if low < high:
          mid = (low + high) // 2          #获取列表中间的索引下标
          mergesort(li, low, mid)          #先分解
          mergesort(li, mid+1, high)
          merge(li, low, mid, high)        #然后合并
    
    data = [10,4,6,3,8,2,5,7]
    mergesort(data, 0 , len(data) -1)
    print(data)                            # [2, 4, 6, 8, 10, 12, 14, 16, 18]
  • 相关阅读:
    As3支持的Html标签一览
    SharpPcap 3.4使用范例
    十六章:构建自定义集合(Part 2)
    十七章:反射、特性和动态编程(Part 1)
    十六章:构建自定义集合(Part 1)
    Implementing Finalize and Dispose to Clean Up Unmanaged Resources
    HDU1711Number Sequence(KMP)
    HDU2087剪花布条(KMP)
    HDU1671Phone List (trie树)
    HDU 1075 What are you talking about(trie树)
  • 原文地址:https://www.cnblogs.com/8lala/p/12567186.html
Copyright © 2020-2023  润新知