zoukankan      html  css  js  c++  java
  • 算法和数据结构

    算法:一个计算过程,解决问题的办法

    递归

    递归的两个必须条件

    1. 递推(调用自身)
    2. 回溯(结束条件)

    来看几个例子:

    eg1:该函数不是递归,没有结束条件

    def func1(n):
        print(n)
        func1(n - 1)

    eg2:该函数亦不是递归,虽有条件,但条件是无穷的

    def func2(n):
        if n > 0:
            print(n)
            func2(n + 1)

    eg3:该函数是递归,满足递归的两个条件

    def func3(n):
        if n > 0:
            print(n)
            func3(n - 1)

    eg4:该函数亦是递归,满足递归的两个条件

    def func4(n):
        if n > 0:
            func4(n - 1)
            print(n)

    那么,eg3 和 eg4的输出结果是一样的吗?如果不一样,为什么?

    解释:

    因为,func3执行的时候,print是在调用自身之前,所以当n是5传入函数时,会先打印5,再调用自身,这时候n是4,,依次循环递归。。。。到最后n=0,所以回溯时没有任何输出,所以func3 输出的结果是:5,4,3,2,1;而func4执行时,print是在调用自身之后,当n是5传入函数执行时,此时的n已经被减了1变成了4,再依次循环递归。。。。,再回溯的时候func4才有输出,的结果是:1,2,3,4,5

    递归的简单使用,给出一个列表:[1, [2, [3, [4, [5, [6, [7, ]]]]]]],需求:拿到列表中的元素(纯数字)

    # coding=utf-8
    li = [1, [2, [3, [4, [5, [6, [7, ]]]]]]]
    
    
    def tell(li):
        for item in li:
            if type(item) is list:
                tell(item)
            else:
                print(item)
    
    
    tell(li)

    列表查找

    顺序查找:最常用的就是for循环,挨个对比查找,效率极低!

    二分查找:把一个列表一分为二(切片),判断要找的数大于还是小于中间值,大于则在右侧查找,小于在左侧查找。每次都是将列表一切为二进行查找!

    原始的二分法(切片),时间复杂度为O(n)

    def find(find_num, ll):
        print(ll)
        if len(ll) == 0:
            print('not find')
            return
        mid_index = len(ll) // 2
        if find_num > ll[mid_index]:
            ll = ll[mid_index + 1:]
            find(find_num, ll)
        elif find_num < ll[mid_index]:
            ll = ll[:mid_index]
            find(find_num, ll)
        else:
            print('find', ll[mid_index])
    
    
    l = [1, 3, 5, 8, 12, 34, 45, 56, 67, 78, 89, 123, 234, 345, 456, 566, 789]
    find(566, l)

    改进后的二分法(不用切片),时间复杂度为O(logn)

    def num_search(num_list, num):
        start = 0
        end = len(num_list) - 1
        while start <= end:
            mid = (end + start) // 2
            if num_list[mid] == num:
                return mid
            elif num_list[mid] < num:
                start = mid + 1
            else:
                end = mid - 1
        return
    
    
    num_l = [i for i in range(1000)]
    print(num_search(num_l, 666))

    排序

    冒泡排序

    思路:比较列表相邻的两个数,如果前边的大于后边的,就交换这两个数。。。,(升序)

    代码实现,时间复杂度:O(n*n)

    import random
    
    
    def sort_list(list1):
        for i in range(len(list1) - 1):
            for j in range(len(list1) - i - 1):
                if list1[j] > list1[j + 1]:
                    list1[j], list1[j + 1] = list1[j + 1], list1[j]
        print('遍历次数: {}'.format(i))
        return list1
    
    
    data = list(range(1000))
    random.shuffle(data)
    print(sort_list(data))

    上面的代码虽然实现了冒泡排序,但是有个效率优化的问题,假设一个极端的例子,一个列表:[1,2,3,4,5,6,7,8,9],按照上述的冒泡排序方法,程序会循环8次结束,但是在这个过程中,列表中的数字位置并未发生改变,那怎么解决这个问题呢?加一个变量来判断一次循环后数字的位置是否有改变,有就继续循环,没有就结束循环

    import random
    
    
    def sort_list(list1):
        for i in range(len(list1) - 1):
            change = 0
            for j in range(len(list1) - i - 1):
                if list1[j] > list1[j + 1]:
                    list1[j], list1[j + 1] = list1[j + 1], list1[j]
                    change = 1
            if change == 0:
                break
        print('遍历次数: {}'.format(i))
        return list1
    
    
    data = list(range(1000))
    random.shuffle(data)
    print(sort_list(data))
    改进后

    选择排序

    思路:循环列表,找到最小的值放到列表第一位,在遍历一次剩余数中的最小值,继续往后放。。。。

    代码实现,时间复杂度:O(n*n)

    def select_sort(li):
        for i in range(len(li) - 1):
            min_num = i
            for j in range(i + 1, len(li)):
                if li[j] < li[min_num]:
                    min_num = j
            li[i], li[min_num] = li[min_num], li[i]
    
    
    data = list(range(1000))
    random.shuffle(data)
    select_sort(data)
    print(data)

    插入排序

    思路:列表分为无序区和有序区,最初的有序区只有一个值,每次从无序区选择一个值,插入到有序区的位置,直到无序区为空!

    代码实现,时间复杂度:O(n*n)

    def insert_sort(li):
        for i in range(1, len(li)):
            tmp = li[i]
            j = i - 1
            while j >= 0 and li[j] > tmp:
                li[j + 1] = li[j]
                j = j - 1
            li[j + 1] = tmp
    
    
    data = list(range(1000))
    random.shuffle(data)
    insert_sort(data)
    print(data)

    快排

    思路:一个列表先取一个元素x(第一个元素),然后使元素x归位,此时列表被元素x分为左右两半,左边都会比元素x小,右边的都会比元素x大,最后用递归完成排序!

    代码实现

    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)
    
    
    def partition(data, left, right):
        # 用变量保存第一个元素
        tmp = data[left]
        # 左右碰不到时循环
        while left < right:
            # 找到左边比右边小的数,大于tmp的数放在右边不动
            while left < right and data[right] >= tmp:
                right -= 1
            # 将right放在左边的left空位上
            data[left] = data[right]
            # 找到左边比右边小的数,小于tmp的数放在左边不动
            while left < right and data[left] <= tmp:
                left += 1
            # 将left放在左边的right空位上
            data[right] = data[left]
        # 左右碰到时
        data[left] = tmp
        return left
    
    
    data = list(range(10000))
    random.shuffle(data)
    quick_sort(data, 0, len(data) - 1)
    print(data)

    堆排序

    前言

    1、树与二叉树

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

    • 如果N=0,就是一颗空树
    • 如果N>0,那就存在1个节点作为数的根节点,其他节点可以分为M个集合,每个集合本身又是一棵树

    2、两种特殊的二叉树

    • 满二叉树:最后一层的节点都是满的(a)
    • 完全二叉树:满二叉树只去掉最后一层的后面几个节点(b)

    3、二叉树的存储方式

    • 链式存储
    • 顺序存储(列表)

     顺序存储就是从上往下依次按顺序存入列表,如下图:

    那么,这样存储后父节点与子节点的编号下标(i)有什么关系?

    • 父节点与左子节点的编号下标关系:2i + 1
    • 父节点与右子节点的编号下标关系:2i + 2

    堆排序

    1、堆分为大根堆和小根堆

    • 大根堆:一颗完全二叉树,满足任一节点都要比其子节点大
    • 小根堆:一颗完全二叉树,满足任一节点都要比其子节点小

    2、堆排序的过程

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

    3、代码实现

    # coding:utf-8
    import random
    
    
    # 调整
    def sift(arr, start, end):
        i = start
        j = 2 * i + 1
        tmp = arr[i]
        while j <= end:  # 孩子在堆里
            # 升序排序
            if j + 1 <= end and arr[j] < arr[j + 1]:  # 如果存在右孩子且大于左边的孩子
                j += 1  # j指向右孩子
            if arr[j] > tmp:  # 子比父大
                arr[i] = arr[j]  # 子放在父的空位上
                i = j  # 子成为新的父
                j = 2 * i + 1  # 新孩子
            else:
                break
    
        arr[i] = tmp
    
    
    # 堆排序
    def heap_sort(arr):
        length = len(arr)
        for i in range(length // 2 - 1, -1, -1):  # 循环每个小堆,做一次sift
            sift(arr, i, length - 1)
        # 堆建好之后,挨个出数
        for i in range(length - 1, -1, -1):  # i 指向堆的最后
            arr[0], arr[i] = arr[i], arr[0]  # 将调换下来的父节点数放在最后一位
            sift(arr, 0, i - 1)
        return arr
    
    
    arr = list(range(555))
    random.shuffle(arr)
    print(heap_sort(arr))

    归并排序

    假设有一个列表,分为有序的两段,怎么让它合并为一个有序的列表

    先实现一次归并,从左往右依次取左右两边的第一个元素,依次比较大小,将小的添加到新的列表中,然后继续取值比较。。。

    def merge(arr, start, mid, end):
        '''
        :param arr: 一个两段有序的列表
        :param start: 左段有序列表的最小元素
        :param mid: 左段有序列表的最后一个元素
        :param end: 右段有序列表的最小元素
        '''
        tmp = []
        i = start
        j = mid + 1
        while i <= mid and j <= end:  # 两边都有数
            if arr[i] < arr[j]:  # 左边小时将左边的元素append到tmp
                tmp.append(arr[i])
                i += 1
            else:
                tmp.append(arr[j])  # 右边小时将右边的元素append到tmp
                j += 1
        while i <= mid:
            tmp.append(arr[i])
            i += 1
        while j <= end:
            tmp.append(arr[j])
            j += 1
        arr[start:end + 1] = tmp

    归并的使用

    分解:用递归将一个列表越分越小,直到分成一个元素,一个元素是有序的

    合并:用归并将两个有序列表合并

    def merge(arr, start, mid, end):
        '''
        :param arr: 一个两段有序的列表
        :param start: 左段有序列表的最小元素
        :param mid: 左段有序列表的最后一个元素
        :param end: 右段有序列表的最小元素
        '''
        tmp = []
        i = start
        j = mid + 1
        while i <= mid and j <= end:  # 两边都有数
            if arr[i] < arr[j]:  # 左边小时将左边的元素append到tmp
                tmp.append(arr[i])
                i += 1
            else:
                tmp.append(arr[j])  # 右边小时将右边的元素append到tmp
                j += 1
        while i <= mid:
            tmp.append(arr[i])
            i += 1
        while j <= end:
            tmp.append(arr[j])
            j += 1
        arr[start:end + 1] = tmp
    
    
    def merge_sort(arr, start, end):
        if start < end:
            mid = (start + end) // 2
            # 分解
            merge_sort(arr, start, mid)
            merge_sort(arr, mid + 1, end)
            # 合并
            merge(arr, start, mid, end)
        return arr
    
    
    arr = list(range(555))
    random.shuffle(arr)
    print(merge_sort(arr, 0, len(arr) - 1))

    计数排序

    条件:假设有一个长度为10万的列表,其中的元素都在0-100之间,将该列表内的元素进行排序

    思路:先定义一个0-100的列表(下标),让每个下标对应的数全部等于0,然后循环需要排序的列表,将循环出现的数对应到定义好下标的列表中进行个数统计

    代码实现

    def count_sort(arr, max_num):
        count = [0 for i in range(max_num + 1)]
        for num in arr:
            count[num] += 1
        i = 0
        for num, m in enumerate(count):
            for j in range(m):
                arr[i] = num
                i += 1
        return arr
    
    
    data = [random.randint(0, 100) for i in range(100)]
    print('before: ', data)
    print('after: ', count_sort(data, 100))
    

      

  • 相关阅读:
    WinCE 测试网速
    YII 关联表查询
    YII CMenu配置
    php扩展开发笔记1
    使用Mysql EXPLAIN分析、优化SQL语句
    使用Mysql EXPLAIN分析、优化SQL语句 (续)
    jquery提交中文导致乱码
    *nix 命令记(持续更新)
    php 函数中使用static
    xmlhttprequest获取TransferEncoding:chunked分时编码输出
  • 原文地址:https://www.cnblogs.com/rongge95500/p/10526890.html
Copyright © 2011-2022 走看看