数据结构与算法(python)

时间复杂度

时间复杂度是用来估计算法运行时间的一个单位。时间复杂度越高,算法越慢

常见的时间复杂度

O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)

快速判断算法复杂度:

  1. 确定问题规模n
  2. 是否有循环减半过程 – logn
  3. 是否有k层关于n的循环 – n的k次方

空间复杂度

空间复杂度是用来评估算法内存占用大小的式子
快速判断算法复杂度:

  1. 算法使用了几个变量:O(1)
  2. 算法使用了长度为n的一维列表:O(n)
  3. 算法使用了m行n列的二维列表:O(mn)

递归

递归的两个特点:
调用自身,结束条件

汉诺塔

在这里插入图片描述在这里插入图片描述

def hanoi(n, a, b, c):
    if n > 0:
        hanoi(n-1, a, c, b)
        print('moving from %s to %s' % (a, c))
        hanoi(n-1, b, a, c)

汉诺塔移动次数的递推式:
h(x)=2h(x)+1
h大概是2的64次方

斐波那契数列

def fib_list(num):
    """ 求出长度为num的fib """
    fibList = []
    for i in range(num):
        if len(fibList) < 2:
            fibList.append(1)
            continue
        fibList.append(fibList[-1] + fibList[-2])
    return fibList

1.原始递归

def fib_recur(n):
    # 递归,时间复杂度为O(2^n)
    if n <= 1:
        return n
    else:
        return fib_recur(n - 1) + fib_recur(n - 2)

2.把之前算过的斐波那契数存在字典中,这样的话递归要用的话就直接存取,而不是去重新计算。

def fib_recur_dict(n, rabbits=None):
    if rabbits is None:
        rabbits = {1: 1, 2: 1}
    if n in rabbits:
        return rabbits[n]
    res = fib_recur_dict(n - 1) + fib_recur_dict(n - 2)
    rabbits[n] = res
    return res

3.递推

def fib_loop(n):
    # 递推,时间复杂度为O(n)
    a, b = 0, 1
    for i in range(n):
        a, b = b, a + b
    return a

查找

输入:列表,待查找元素
输出:元素下标(未找到元素时返回None或-1)

线性查找/顺序查找

Linear Search
从列表第一个元素开始,顺序进行搜索,直到找到元素或搜索到列表最后一个元素为止。

def linear_search(ls, target):
    """ 线性查找,时间复杂度为O(n) """
    for index, value in enumerate(ls):
        if value == target:
            return index
    return None

二分查找/折半查找

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

def binary_search(ls, target):
    """ 二分查找,时间复杂度为O(logn),要求列表是有序的 """
    left = 0
    right = len(ls) - 1
    while right >= left:  # 确保候选区有值
        mid = (right + left) // 2
        if ls[mid] == target:
            return mid
        elif ls[mid] > target:
            right = mid - 1
        else:
            # ls[mid] < target
            left = mid + 1
    return None

排序

冒泡排序

Bubble Sort
原地排序
判断列表每两个相邻的数,如果前面比后面大,则交换这两个数(需要升序排列)。
一趟排序完成后,则无序区减少一个数,有序区增加一个数。

def bubble_sort(ls):
    """ 冒泡,时间复杂度为O(n^2) """
    for i in range(len(ls) - 1):  # 第i趟,从0开始,只剩一个数时不需要最后一趟
        exchange = False
        for j in range(len(ls) - i - 1):  # j:无序区的索引指针,指针顾头不顾尾所以-1
            if ls[j] > ls[j + 1]:
                ls[j], ls[j + 1] = ls[j + 1], ls[j]
                exchange = True
        if not exchange:
            return 

优化如果冒泡排序中的一趟排序没有发生交换,则说明列表已经有序,可以直接结束算法。

选择排序

Select Sort
原地排序
一趟排序记录无序区最小的数的位置,放到无序区的第一个位置…

def select_sort(ls):
    """ 选择排序,时间复杂度O(n^2) """
    for i in range(len(ls) - 1):  # 第i趟
        # 无序区最小值的索引位置,默认i
        min_loc = i
        # 遍历无序区
        for j in range(i, len(ls)):
            if ls[j] < ls[min_loc]:
                min_loc = j
        # 找到无序区最小数,与无序区第一个数交换
        ls[i], ls[min_loc] = ls[min_loc], ls[i]

插入排序

Insert Sort
原地排序
初始时手里(有序区)只有一张牌,每次(从无序区)摸一张牌,插入到手里的正确位置。
无序区有n-1张牌

@execute_time
def insert_sort(ls):
    """ 插入排序,时间复杂度O(n^2) """
    for i in range(1, len(ls)):  # i表示摸到的牌(无序区)的下标
        j = i - 1  # j表示手里的牌(有序区)的下标
        # 1.j>i,将j右挪,将i插到j前面 || 2.j<i,直接插到j后面
        tmp = ls[i]
        while ls[j] > tmp and j >= 0:
            # 右挪
            ls[j + 1] = ls[j]
            j -= 1
        ls[j + 1] = tmp

快速排序

Quick Sort
思路:取第一个元素p,使元素p归位,列表被p分成两部分
左边都比p小,右边都比p大递归完成排序(不断递归,当左右的元素数为0或1时完成递归)

框架:

def quick_sort(data,left,right):
    if left < right:
        mid = partition(data,left,right)
        quick_sort(data,left,mid-1)
        quick_sort(data,mid+1,right)
        
重点实现partition函数:
def partition(ls, left, right):
    """ 快速排序,时间复杂度O(nlogn) """
    tmp = ls[left]
    while left < right:
        while left < right and ls[right] >= tmp:
            right -= 1
        ls[left] = ls[right]
        
        while left < right and ls[left] <= tmp:
            left += 1
        ls[right] = ls[left]
    ls[left] = tmp
    return left

缺点:

  1. python递归最大深度问题(999),可以修改
  2. 最坏情况,列表是有序的时候…

堆排序

树是一种数据结构,树是一种可以递归定义的数据结构。
树是由n个节点组成的集合:

  1. 如果n=0,那这是一颗空树
  2. 如果n>0,那存在一个节点作为树的根节点,其他节点可分为m个集合,每个集合本身又是一棵树。
    在这里插入图片描述
  3. 根节点:A
  4. 叶子节点:B,C,H,I,P,Q,K,L,M,N(下面没有子节点的节点)
  5. 树的深度(高度):4层
  6. 节点的度:F节点的度是3(分了3个叉)
  7. 树的度:整个树里最大节点的度-> 6
  8. 孩子节点/父节点:E是I的父节点,I是E的孩子节点
  9. 子树

二叉树

二叉树:度不超过2的树,每个节点最多有两个孩子节点。
两个孩子节点被区分为左孩子节点和右孩子节点。
在这里插入图片描述

二叉树的存储方式:

  1. 链式存储方式
  2. 顺序存储方式

满二叉树

一个二叉树,如果每一层的节点都达到最大值,则为满二叉树。
16,color_FFFFFF,t_70,g_se,x_16)

完全二叉树

叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边的若干位置的二叉树。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值