zoukankan      html  css  js  c++  java
  • 基本算法

    算法定义

    •   一个计算过程,解决问题的方法
    •   有输入有输出,给规定的输入得到规定的输出 

    时间复杂度

    •   用来估计算法运行时间的一个式子(单位)
    •   一般来说,时间复杂度高的算法比复杂度低的算法慢。
    •   常见复杂度效率排序

          O(1)<O(logn)<O(n) 

    简单的判断时间复杂度?(无递归)

      循环减半的过程---->O(logn)

      几层循环就是n的几次方的复杂度

    空间复杂度

      用来估计算法内存占用大小的式子 

    列表查找:从列表中查找指定元素

      输入:列表、待查找元素

      输出:元素下标或者未查到元素  

    1 li = [1, 5, 6, 55, 64]
    2 res = 6 in li
    3 li.index(6)

    顺序查找 线性查找 O(n)

      从列表第一和元素开始,顺序进行搜索,直到找到为止。  

    1 for i in li:
    2     pass

    二分查找  O(logn)

      有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中中间值的比较,可以使候选区减少一半。

    从上图:

    •  若low <= high  存在候选区
    •  若low > high 不存在候选区 说明被查找的值根本不存在
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 
     5 def bin_search(li, val):
     6     low = 0
     7     high = len(li) - 1
     8     while low <= high:
     9         # 存在候选区
    10         mid = (low + high) // 2
    11         if li[mid] == val:
    12             return mid
    13         elif li[mid] < val:
    14             # 右边
    15             low = mid + 1
    16         else:
    17             # 左边
    18             high = mid - 1
    19     # 查找不到我默认返回-1
    20     return -1
    21 
    22 
    23 # 递归
    24 def bin_search_rec(li, val, low, high):
    25     if low <= high:
    26         mid = (low + high) // 2
    27         if li[mid] == val:
    28             return mid
    29         elif li[mid] > val:
    30             return bin_search_rec(li, val, low, mid - 1)
    31         else:
    32             return bin_search_rec(li, val, mid + 1, high)
    33     else:
    34         return -1

     low B三人组(时间O(n**2)    空间O(1))

    冒泡排序  O(n**2)

    • 思路:相邻两位置比较大小
     1 # 装饰器
     2 def timer(func):
     3     def wrapper(*args, **kwargs):
     4         import time
     5         start_time = time.time()
     6         res = func(*args, **kwargs)
     7         stop_time = time.time()
     8         print('运行时间%s' % (stop_time - start_time))
     9         return res
    10     return wrapper
     1 @timer
     2 def bubble_sort(li):
     3     for i in range(len(li)-1):  # i表示第n趟 一共n或者n-1趟
     4         exchange = False
     5         for j in range(len(li)-i-1):  # 第i趟 无序区[0, n-i-1] j表示箭头0~n-i-2
     6             if li[j] > li[j+1]:
     7                 li[j], li[j+1] = li[j+1], li[j]
     8                 exchange = True
     9         if not exchange:
    10             break

    选择排序O(n**2)

    • 思路:一趟遍历记录最小的数,放到第一个位置,一直循环
     1 def get_min_pos(li):
     2     """
     3     找最小值的位置
     4     :param li:
     5     :return:
     6     """
     7     min_pos = 0
     8     for i in range(1, len(li)):
     9         if li[i] < li[min_pos]:
    10             min_pos = i
    11     return min_pos
    12 
    13 
    14 @timer
    15 def select_sort(li):
    16     for i in range(len(li) - 1):  # n趟或者n-1趟
    17         # 第i趟无序区的范围 i~最后
    18         min_pos = i   # min_pos更新无序区最小位置
    19         for j in range(i+1, len(li)):
    20             if li[j] < li[min_pos]:
    21                 min_pos = j
    22         li[i], li[min_pos] = li[min_pos], li[i]

    插入排序O(n**2)

    • 思路:摸到的牌与前边的牌进行逐个比较
     1 @timer
     2 def insert_sort(li):
     3     for i in range(1, len(li)):  # i表示摸到的牌下标
     4         tmp = li[i]   # 摸到的牌
     5         j = i - 1
     6         while j >= 0 and li[j] > tmp:  # 只要往后挪就循环 2个条件都得满足
     7             # 如果j=-1停止挪动 如果li[j]小了 停止挪动
     8             li[j+1] = li[j]
     9             j -= 1
    10         li[j+1] = tmp
    11 
    12 
    13 # li = list(range(10000))
    14 # random.shuffle(li)
    15 # insert_sort(li)

    NB排序三人组

    快排O(nlogn)

    • 思路:
    •   1.取一个元素p,使p归位
    •   2.列表被P分成两部分,左边都比P小,右边都比P大
    •   3.递归完成排序
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 import sys
     5 import random
     6 from lowB import timer
     7 # 快排 O(n*logn)
     8 
     9 
    10 @timer
    11 def quick_sort(li):
    12     _quick_sort(li, 0, len(li)-1)
    13 
    14 
    15 # 这种写法没从新开辟新的空间
    16 def _quick_sort(li, left, right):
    17     if left < right:
    18         mid = partition(li, left, right)
    19         _quick_sort(li, left, mid-1)
    20         _quick_sort(li, mid+1, right)
    21 
    22 
    23 def partition(li, left, right):
    24     tmp = li[left]
    25     while left < right:
    26         while left < right and li[right] >= tmp:
    27             right -= 1
    28         li[left] = li[right]
    29         while left < right and li[left] <= tmp:
    30             left += 1
    31         li[right] = li[left]
    32     li[left] = tmp
    33     return left
    34 
    35 
    36 @timer
    37 def sys_sort(li):
    38     li.sort()
    39 
    40 
    41 li = list(range(10000))
    42 random.shuffle(li)
    43 quick_sort(li)  # 运行时间0.19099831581115723
    44 sys_sort(li)    # 运行时间0.0009989738464355469   Python内部排序快100倍
    45 
    46 # sys.setrecursionlimit(2000)设置递归深度
    47 
    48 
    49 # 开辟了新的空间 列表生成式 生成两列表占空间 思想完全相同
    50 def quick_sort2(li):
    51     if len(li) < 2:
    52         return li
    53     tmp = li[0]
    54     left = [v for v in li[1:] if v <= tmp]
    55     right = [v for v in li[1:] if v > tmp]
    56     left = quick_sort2(left)
    57     right = quick_sort2(right)
    58     return left + [tmp] + right

    问题:1.当列表全部倒序递归的层数就非常大 2.如果p元素只有一边有数据和p元素随机取呢

    堆排序(O(nlogn)) 

     插曲了解些数据结构

    •  树是一种数据结构  可以仿照目录结构理解
    •  树是一种可以递归定义的数据结构
    •  上张图辅助理解

    • 根节点:A
    • 叶子节点:B/C等等  没孩子的节点
    • 树的深度(高度):4
    • 树的度:6
    • 孩子节点的度:比如节点F,它的度为3
    • 子树 :比如:F K L W构成一个子树    E I J P Q也是

      二叉树:度不超过2的树(节点最多有两个叉)

    • 满二叉树:节点全是满的,而且每个节点的孩子节点只能为2个
    • 完全二叉树:最多两个孩子节点,并且是最左边一个个的少

    二叉树的存储方式:链式存储、顺序存储方式

    • 父节点和左孩子节点的编号下标关系: 

         0-----1       1-----3      2-----5      3------7     4-----9

        推出关系  i------2i+1

    • 父节点和右孩子节点的编号下标关系

        0------2   1------4  2-----6   3-------8  4-----10

        推出关系 i-------2i+2

    • 孩子与父节点的编号下标关系

        1------0   2------0

        3------1   4------1

        5------2   6------2

        推出关系 左:(i-1)/2   右:(i-2)/2

        总体 :(i-1)//2

      堆

    • 大根堆:一棵完全二叉树,满足任意节点都比孩子节点大

    • 小根堆:一棵完全二叉树,满足任意节点都比孩子节点小

     

     -----------------------------------------------------------------------------------------------------------------------

    •  堆排序的三环节:
    1. 堆的向下调整环节性质
    2. 创建堆(用到性质)
    3. 挨个出数(用到性质)
     1 # 堆的向下调整性质
     2 def sift(li, low, high):
     3     # li表示树  low表示树的根 high:表示最后一个子节点的位置
     4     tmp = li[low]
     5     i = low  # 指向空的位置
     6     j = 2 * i + 1  # 指向两孩子的位置
     7     while j <= high:  # 循环退出的第二种情况:j大于high,说明空位i没有孩子了,i是叶子节点
     8         if j + 1 <= high and li[j] < li[j+1]:  # 如果右孩子存在,并且右孩子比左孩子的值大,指向右孩子
     9             j += 1
    10         if li[j] > tmp:
    11             # 省长上位
    12             li[i] = li[j]
    13             # 变换i与j的位置
    14             i = j
    15             j = 2 * i + 1
    16         else:  # 循环跳出的第一种情况:j位置的值比tmp小,说明两孩子都比tmp小
    17             break
    18         li[i] = tmp
    19 
    20 
    21 @timer
    22 def heap_sort(li):
    23     # 创建堆
    24     n = len(li)
    25     # 从最后一个子树的位置一个个进行的调整
    26     # 最后一个节点的位置是n-1
    27     # 孩子节点的父亲孩子节点是i-1 // 2
    28     for low in range(n//2-1, -1, -1):
    29         sift(li, low, n-1)
    30     print(li)  # 画一个堆出来
    31     # 挨个出数
    32     for high in range(n-1, -1, -1):
    33         li[0], li[high] = li[high], li[0]  # 退休 棋子
    34         sift(li, 0, high-1)
    35 
    36 
    37 li = list(range(10))
    38 random.shuffle(li)
    39 heap_sort(li)
    40 print(li)

     

     归并排序  

    •  归并排序之一次归并 假设现在的列表分两段有序,合成一个有序列表

     1 def merge(li, low, mid, high):
     2     i = low   # 是第一段有序列表中第一个元素
     3     j = mid + 1  # 是第二段有序列表的第一个元素
     4     ltmp = []  # 创列表比较之后元素存放
     5     while i <= mid and j <= high:
     6         if li[i] <= li[j]:
     7             ltmp.append(li[i])
     8             i += 1
     9         else:
    10             ltmp.append(li[j])
    11             j += 1
    12     while i <= mid:
    13         ltmp.append(li[i])
    14         i += 1
    15 
    16     while j <= high:
    17         ltmp.append(li[j])
    18         j += 1
    19 
    20     li[low:high + 1] = ltmp
    21     # for i in range(low, high+1):
    22     #     li[i] = ltmp[i-low]
    23 # li = [2, 5, 7, 8, 9, 1, 3, 4, 6]
    24 # merge(li, 0, 4, 8)
    25 # print(li)
    26 
    27 
    28 def merge_sort(li, low, high):
    29     if low < high:
    30         # 分解
    31         mid = (low + high) // 2
    32         # 递归
    33         merge_sort(li, low, mid)
    34         merge_sort(li, mid+1, high)
    35         # 一次归并
    36         merge(li, low, mid, high)
    37 
    38 
    39 li = [10, 4, 6, 3, 8, 2, 5, 7]
    40 merge_sort(li, 0, len(li) - 1)
    41 print(li)

     实例展示  不要直接合并起来再sort(nlogn) 归并一次合成下面实例就是(n)

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 
     5 # 如果现在两个有序列表 合并成一个
     6 # def merge2list(li1, li2):
     7 #     li = []
     8 #     i = 0
     9 #     j = 0
    10 #     while i < len(li1) and j < len(li2):
    11 #         if li1[i] <= li2[j]:
    12 #             li.append(li1[i])
    13 #             i += 1
    14 #         else:
    15 #             li.append(li2[j])
    16 #             j += 1
    17 #     while i < len(li1):
    18 #         li.append(li1[i])
    19 #         i += 1
    20 #     while j < len(li2):
    21 #         li.append(li2[j])
    22 #         j += 1
    23 #     return li
    24 #
    25 #
    26 # li1 = [2, 5, 7, 8, 9]
    27 # li2 = [1, 3, 4, 6]
    28 # li = merge2list(li1, li2)
    29 # print(li) 

    总结:

    • 快速排序 < 归并排序 < 堆排序

    三种排序算法的缺点:

    •  快速排序:极端情况下排序效率低
    •  归并排序:需要额外的内存开销
    •  堆排序:在快的排序算法中相对较慢

     补充:

      希尔排序

    • 是一种分组插入排序算法。
    • 首先取一个整数d1=n/2,将元素分为d1个组,每组相邻量元素之间距离为d1,在各组内进行直接插入排序;
    • 取第二个整数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序。
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 import random
     5 
     6 
     7 def timer(func):
     8     def wrapper(*args, **kwargs):
     9         import time
    10         start_time = time.time()
    11         res = func(*args, **kwargs)
    12         stop_time = time.time()
    13         print('运行时间%s' % (stop_time - start_time))
    14         return res
    15     return wrapper
    16 
    17 
    18 def insert_sort_gap(li, d):
    19     for i in range(1, len(li)):
    20         tmp = li[i]
    21         j = i - d
    22         while j >= 0 and li[j] > tmp:
    23             li[j+d] = li[j]
    24             j -= d
    25         li[j+d] = tmp
    26 
    27 
    28 @timer
    29 def shell_sort(li):
    30     d = len(li) // 2
    31     while d > 0:
    32         # do something
    33         insert_sort_gap(li, d)
    34         d = d // 2
    35 
    36 
    37 li = list(range(10000))
    38 random.shuffle(li)
    39 shell_sort(li)
    40 # 运行时间0.11099767684936523
    41 # 对插入排序确实提高了很多,但是跟NB三人组还是差距大

    计数排序

    • 现在有一个列表,列表中的数范围都在0到100之间,列表长度大约为100万。设计算法在O(n)时间复杂度内将列表进行排序。
     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 
     5 # 数字不能很大  这样创列表超级大
     6 def count_sort(li, max_num=50):
     7     count = [0 for _ in range(max_num + 1)]
     8     for val in li:
     9         count[val] += 1
    10     li.clear()
    11     for i, v in enumerate(count):
    12         # 表示i这个数出现了v次
    13         for _ in range(v):
    14             li.append(i)
    15 # 时间复杂度O(n)  看第二层循环n是v的嘛

    基数排序

    • 先按照低位进行桶排序,然后按照高位再桶排序,最后挨个出数-->桶排序+关键字排序思想
     1 def radix_sort(li):
     2     max_num = max(li)
     3     i = 0  # 最开始0位(最低位)
     4     while (10 ** i <= max_num):
     5         # do something
     6         # 建立一个桶
     7         buckets = [[] for _ in range(10)]
     8         # 遍历每一个值
     9         for val in li:
    10             # 最低位的数字
    11             digit = val // (10 ** i) % 10
    12             # 把val放在桶里边
    13             buckets[digit].append(val)
    14         li.clear()
    15         # 遍历出来每个桶
    16         for bucket in buckets:
    17             for val in bucket:
    18                 # 把每个桶里边的元素放在li列表中
    19                 li.append(val)
    20         i += 1

    下面是搞着玩的代码

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 
     4 
     5 # [1, 2, 3, 4, 5] 0, 1
     6 # [1, 2, 3, 4, 5, 6] 0, 1, 2
     7 # [1, 2, 3, 4, 5, 6, 7]0, 1, 2
     8 # n // 2 -1
     9 
    10 
    11 def reverse_list(li):
    12     n = len(li)
    13     for i in range(n // 2):
    14         li[i], li[n-i-1] = li[n-i-1], li[i]
    15     return li
    16 
    17 
    18 print(reverse_list([1, 2, 3, 4, 5]))
    19 
    20 
    21 def int2list(num):
    22     li = []
    23     while num > 0:
    24         li.append(num % 10)
    25         num //= 10
    26     li.reverse()
    27     return li
    28 
    29 
    30 print(int2list(12345))
    31 
    32 
    33 def reverse_int(num):
    34     """
    35     123->321 12300->321
    36     :param num:
    37     :return:
    38     """
    39     is_neg = False
    40     if num < 0:
    41         is_neg = True
    42         num = -1 * num
    43     res = 0
    44     while num > 0:
    45         res = res * 10
    46         digit = num % 10
    47         res = res + digit
    48         num = num // 10
    49     if is_neg:
    50         res = res * -1
    51     return res
    52 
    53 
    54 print(reverse_int(-100))
    55 
    56 
    57 def get_digit(num, i):
    58     """
    59 
    60     :param num: 57841
    61     :param i: i为0 个位1 十位2 百位...  3
    62     :return: 7
    63     """
    64     return num // (10 ** i) % 10

    推荐两网站不定期的练算法

      算法的动画+伪代码https://visualgo.net/en 

      时不时的洗脑壳https://leetcode-cn.com/

  • 相关阅读:
    Elasticsearch Windows下安装及配置集群
    .Net文件压缩
    DateHelper
    lambda Helper
    Log4net的使用
    python3之rabbitMQ
    python3之协程
    python3之paramiko模块
    python3之redis
    redis
  • 原文地址:https://www.cnblogs.com/Alexephor/p/11379263.html
Copyright © 2011-2022 走看看