python 数据结构与算法2

1 查找和排序

1.1 查找

1.1.1 顺序查找算法及算法分析

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

在这里插入图片描述

1.1.2 二分查找算法及算法分析

在这里插入图片描述

def search(num, num_list):
    # 最小规模
    if len(num_list) == 1:
        return num_list[0] == num

    middle = len(num_list)//2
    if num_list[middle] == num:
        return True
    # 减小规模
    elif num_list[middle] > num:
        return search(num, num_list[:middle])
    else:
        return search(num, num_list[middle+1:])


if __name__ == '__main__':
    print(search(44, [17, 20, 26, 31, 35, 44, 54, 55, 65]))

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

1.2 排序

1.2.1 冒泡排序

在这里插入图片描述

# 时间复杂度: O(n^2)
# 空间复杂度: O(n)

def bubble_sort(num_list):

    length = len(num_list)
    for i in range(length):
        # 经过一次遍历没有发生交换说明已经排序完成,不需要继续遍历
        sort_flag = False
        for j in range(i+1, length):
            if num_list[i] > num_list[j]:
                sort_flag = True
                num_list[i], num_list[j] = num_list[j], num_list[i]
        if sort_flag is False:
            break

    return num_list


if __name__ == '__main__':
    print(bubble_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))

1.2.2 选择排序

选择排序相对于冒泡排序只是减少了交换的次数。
在这里插入图片描述


def select_sort(num_list):
    """
    时间复杂度:O(n^2)
    空间复杂度:O(n)

    算法流程:
    ① 外层循环一次,内存循环遍历[0, n]找到最小的元素与当前元素交换位置,即将最小值排在了开头;
    ② 第二次循环只需要遍历[1, n]找到剩余元素中最小的元素与当前元素交换位置;
    ③ 依次类推...
    :param num_list: 要排序的列表
    :return: 返回排序好的列表
    """
    length = len(num_list)
    for i in range(length):
        # 找到 num_list[i+1:] 中最小的下标,与当前num_list[i]交换
        min_index = i
        for j in range(i+1, length):
            if num_list[min_index] > num_list[j]:
                min_index = j

        num_list[i], num_list[min_index] = num_list[min_index], num_list[i]

    return num_list


if __name__ == '__main__':
    print(select_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))

1.2.3 插入排序

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

在这里插入图片描述

def insert_sort(num_list):
    """
    时间复杂度:O(n^2)
    空间复杂度:O(n)

    算法流程:
    ① 遍历 1-n个元素;
    ② 遍历到k时,记录current_value = num_list[k],并向前比较如果
    num_list[k-1] > num_list[k],则将num_list[k] = num_list[k-1];
    ③ 循环退出时,即找到了某个位置满足:num[index-1] < num[index] < num[index+1],
    将记录current_value插入到num[index]
    :param num_list: 要排序的列表
    :return: 返回排序好的列表
    """

    length = len(num_list)
    for i in range(1, length):
        
        # 1. 记录当前值
        current_value = num_list[i]
        index = i
        
        # 2. 从num_list[index]开始倒序遍历,如果当前遍历的值大于current_value,
        # 则向后移动一位
        while index > 0 and num_list[index-1] > current_value:
            num_list[index] = num_list[index-1]
            index -= 1
        
        # 3. 循环退出时,即找到了num[index-1] < num[index] < num[index+1]的位置,
        # 将current_value插入
        num_list[index] = current_value

    return num_list


if __name__ == '__main__':
    print(insert_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))

1.2.4 谢尔排序

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



def shell_sort(num_list):
    """
    时间复杂度: O(n^1.5)
    空间复杂度: O(n)
    ① 设置间隔从len(num_list) // 2开始;每次缩小间隔 sublist_gap //= 2
    ② 核心部分使用插入排序,对每个子列表进行插入排序
    :param num_list:
    :return:
    """
    sublist_gap = len(num_list) // 2
    while sublist_gap > 0:
        for start in range(sublist_gap):
            # 对子列表进行插入排序
            gap_insert_sort(num_list, start, sublist_gap)

        sublist_gap //= 2

    return num_list


def gap_insert_sort(num_list, start, gap):
    length = len(num_list)
    for i in range(start+gap, length, gap):

        current_value = num_list[i]
        index = i

        while index > 0 and num_list[index-1] > current_value:
            num_list[index] = num_list[index-1]
            index -= 1

        num_list[index] = current_value

    return num_list


if __name__ == '__main__':
    print(shell_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))

在这里插入图片描述

1.2.5 归并排序

在这里插入图片描述

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



def merge_sort(num_list):
    """
    1. 将num_list持续分解为left和right两部分,直到分解为1个元素的列表
    2. 对比 left和right 两个子列表的0元素,将较小的加入到merged列表中,并从原列表弹出。
    3. 最终left或right有剩余元素的直接extend到merged列表。

    :param num_list:
    :return:
    """
    # 递归结束条件
    if len(num_list) <= 1:
        return num_list

    # 分解
    middle = len(num_list) // 2
    left = merge_sort(num_list[:middle])
    right = merge_sort(num_list[middle:])

    # 合并
    merged = []
    while left and right:
        if left[0] <= right[0]:
            merged.append(left.pop(0))
        else:
            merged.append(right.pop(0))

    merged.extend(right if right else left)

    return merged


if __name__ == '__main__':
    print(merge_sort([54, 68, 18]))

1.2.6 快速排序

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


def quick_sort(num_list, first, last):
    # 递归结束条件
    if first < last:
        split_point = partition(num_list, first, last)
        # 分裂点左右递归调用分裂
        quick_sort(num_list, first, split_point-1)
        quick_sort(num_list, split_point+1, last)

    return num_list


def partition(num_list, first, last):
    middle_value = num_list[first]
    left_mark = first + 1
    right_mark = last

    while True:
        # 左标向右移动
        while left_mark <= right_mark and num_list[left_mark] <= middle_value:
            left_mark += 1
        # 右标向左移动
        while right_mark >= left_mark and num_list[right_mark] >= middle_value:
            right_mark -= 1
        # 两标相交时停止
        if right_mark < left_mark:
            break
        # 左右标指定的下标值兑换,即把小的值移到左边,大的值移到右边
        else:
            num_list[left_mark], num_list[right_mark] = num_list[right_mark], num_list[left_mark]

    # 将中值与right_mark下标的值进行交换
    num_list[first], num_list[right_mark] = num_list[right_mark], num_list[first]

    # 中值点就是分裂点
    return right_mark


if __name__ == '__main__':
    sort_list = [54, 68, 18, 2, 5, 36, 99, 41, 25]
    print(quick_sort(sort_list, 0, len(sort_list)-1))

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

2 散列Hashing

散列表又称为哈希表,是一种数据集,其中数据项的存储方式有利于将来快速的查找定位。
散列表中的每一个存储位置,称为槽(slot),可以用来保存数据项,每个槽有唯一的名称。

在这里插入图片描述
在这里插入图片描述
负载因子:
在这里插入图片描述
散列冲突:
在这里插入图片描述

2.1 完美散列函数

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

2.1.1 完美散列函数的用途

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

2.1.2 散列函数MD5/SHA

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

2.2 散列函数设计

2.2.1 折叠法

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

2.2.2 平方取中法

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

2.2.3 非数项

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

2.3 散列冲突的解决方案

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

2.3.1 线性探测 Linear Probing


在这里插入图片描述
线性探测的缺点:聚集。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3.2 数据项链Chaining

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

3 树

树是一种非线性的数据结构,在操作系统、数据库系统、计算机网络等领域中都有广泛应用。
在这里插入图片描述

3.1 树结构相关术语

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

3.1.1 树的定义1

在这里插入图片描述

3.1.2 树的定义2(递归定义)

在这里插入图片描述

3.2 树的嵌套列表实现

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


def binary_tree(root_node):
    return [root_node, [], []]


def insert_left(root, new_branch):
    left = root.pop(1)
    if len(left) > 1:
        root.insert(1, [new_branch, left, []])
    else:
        root.insert(1, [new_branch, [], []])


def insert_right(root, new_branch):
    right = root.pop(2)
    if len(right) > 1:
        root.insert(2, [new_branch, [], right])
    else:
        root.insert(2, [new_branch, [], []])


def get_root_value(root):
    return root[0]


def set_root_value(root, new_value):
    root[0] = new_value


def get_left_child(root):
    return root[1]


def get_right_child(root):
    return root[2]


if __name__ == '__main__':
    tree = binary_tree(3)
    insert_left(tree, 4)
    insert_left(tree, 5)
    insert_right(tree, 6)
    insert_right(tree, 7)

    left_child = get_left_child(tree)
    print(left_child)

    set_root_value(tree, 9)
    print(tree)

    insert_left(left_child, 11)
    print(tree)

    print(get_right_child(get_right_child(tree)))

3.3 树的链表实现

在这里插入图片描述

class BinaryTree(object):
    def __init__(self, root):
        self.root = root
        self.left_child = None
        self.right_child = None

    def insert_left(self, new_node):
        if self.left_child is None:
            self.left_child = BinaryTree(new_node)
        else:
            tree = BinaryTree(new_node)
            tree.left_child = self.left_child
            self.left_child = tree

    def insert_right(self, new_node):
        if self.right_child is None:
            self.right_child = BinaryTree(new_node)
        else:
            tree = BinaryTree(new_node)
            tree.right_child = self.right_child
            self.right_child = tree

    def get_root_value(self):
        return self.root

    def set_root_value(self, new_value):
        self.root = new_value

    def get_left_child(self):
        return self.left_child

    def get_right_child(self):
        return self.right_child


if __name__ == '__main__':
    tree = BinaryTree("a")
    tree.insert_left("b")
    tree.insert_right("c")
    tree.get_right_child().set_root_value("hello")
    tree.get_left_child().insert_right("d")

在这里插入图片描述

3.4 树的应用:表达式解析树

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

表达式解析树建立规则:
在这里插入图片描述

from data_structure.Stack.stack import Stack
from data_structure.Tree.binary_tree_link import BinaryTree


def build_parse_tree(exp):
    pstack = Stack()
    tree = BinaryTree("")
    pstack.push(tree)
    current_tree = tree

    for i in exp:
        if i == "(":
            # 左括号,创建左子节点,当前节点入栈并下降为左节点
            current_tree.insert_left("")
            pstack.push(current_tree)
            current_tree = current_tree.get_left_child()
        elif i not in ["+", "-", "*", "/", ")"]:
            # 操作数,设置当前节点数据数,从栈中拿到父节点并上升为父节点
            current_tree.set_root_value(int(i))
            parent = pstack.pop()
            current_tree = parent
        elif i in ["+", "-", "*", "/"]:
            # 操作符,设置当前节点数据符,当前节点入栈并下降为右节点
            current_tree.set_root_value(i)
            current_tree.insert_right("")
            pstack.push(current_tree)
            current_tree = current_tree.get_right_child()
            # 右括号,从栈中拿到父节点并上升为父节点
        elif i == ")":
            current_tree = pstack.pop()
        else:
            raise ValueError
    return tree


if __name__ == '__main__':

    build_parse_tree("(3+(4*5))")

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

import operator

from data_structure.Stack.stack import Stack
from data_structure.Tree.binary_tree_link import BinaryTree


def build_parse_tree(exp):
    pstack = Stack()
    tree = BinaryTree("")
    pstack.push(tree)
    current_tree = tree

    for i in exp:
        if i == "(":
            # 左括号,创建左子节点,当前节点入栈并下降为左节点
            current_tree.insert_left("")
            pstack.push(current_tree)
            current_tree = current_tree.get_left_child()
        elif i not in ["+", "-", "*", "/", ")"]:
            # 操作数,设置当前节点数据数,从栈中拿到父节点并上升为父节点
            current_tree.set_root_value(int(i))
            parent = pstack.pop()
            current_tree = parent
        elif i in ["+", "-", "*", "/"]:
            # 操作符,设置当前节点数据符,当前节点入栈并下降为右节点
            current_tree.set_root_value(i)
            current_tree.insert_right("")
            pstack.push(current_tree)
            current_tree = current_tree.get_right_child()
            # 右括号,从栈中拿到父节点并上升为父节点
        elif i == ")":
            current_tree = pstack.pop()
        else:
            raise ValueError
    return tree


def evaluate(parse_tree):
    opers = {
        "+": operator.add,
        "-": operator.sub,
        "*": operator.mul,
        "/": operator.truediv,
    }
    # 缩小规模
    left = parse_tree.get_left_child()
    right = parse_tree.get_right_child()

    if left and right:
        fn = opers[parse_tree.get_root_value()]
        # 递归调用自身
        return fn(evaluate(left), evaluate(right))
    else:
        # 递归结束条件:没有左右子节点,说明是叶子节点直接返回值
        return parse_tree.get_root_value()


if __name__ == '__main__':

    parse_tree = build_parse_tree("(3+(4*5))")
    print(evaluate(parse_tree))

在这里插入图片描述

3.5 树的遍历

在这里插入图片描述
前序遍历例子:
在这里插入图片描述

class BinaryTree(object):
    def __init__(self, root):
        self.root = root
        self.left_child = None
        self.right_child = None

    def insert_left(self, new_node):
        if self.left_child is None:
            self.left_child = BinaryTree(new_node)
        else:
            t = BinaryTree(new_node)
            t.left_child = self.left_child
            self.left_child = t

    def insert_right(self, new_node):
        if self.right_child is None:
            self.right_child = BinaryTree(new_node)
        else:
            t = BinaryTree(new_node)
            t.right_child = self.right_child
            self.right_child = t

    def get_root_value(self):
        return self.root

    def set_root_value(self, new_value):
        self.root = new_value

    def get_left_child(self):
        return self.left_child

    def get_right_child(self):
        return self.right_child

    # 前序
    def preorder(self):
        print(self.root, end="-->")
        if self.left_child:
            self.left_child.preorder()
        if self.right_child:
            self.right_child.preorder()

    # 后序
    def postorder(self):
        if self.left_child:
            self.left_child.postorder()
        if self.right_child:
            self.right_child.postorder()
        print(self.root, end="-->")

    # 中序
    def inorder(self):
        if self.left_child:
            self.left_child.inorder()
        print(self.root, end="-->")
        if self.right_child:
            self.right_child.inorder()


if __name__ == '__main__':
    tree = BinaryTree("a")
    tree.insert_left("b")
    tree.insert_right("c")
    tree.get_right_child().set_root_value("hello")
    tree.get_left_child().insert_right("d")

    tree.preorder()   # a-->b-->d-->hello-->
    print()
    tree.postorder()  # d-->b-->hello-->a-->
    print()
    tree.inorder()    # b-->d-->a-->hello-->
 

在这里插入图片描述

3.6 优先队列Priority Queue

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二叉堆python实现:
在这里插入图片描述
添加节点:
在这里插入图片描述
在这里插入图片描述
删除节点:
在这里插入图片描述
在这里插入图片描述
无序表生成堆:
在这里插入图片描述

class BinaryHeap(object):
    def __init__(self):
        self.heap_list = [0]
        self.current_size = 0

    # 将新的key加入到堆中
    def insert(self, k):
        self.heap_list.append(k)
        self.current_size += 1
        # 新的key上浮
        self.perc_up(self.current_size)

    # 返回堆中的最小项
    def find_min(self):
        pass

    # 删除堆中的最小项,并返回---即删除堆顶
    def del_min(self):
        ret_val = self.heap_list[1]
        # 将最后一个节点移到堆顶
        self.heap_list[1] = self.heap_list[self.current_size]
        self.current_size -= 1
        self.heap_list.pop()
        self.perc_down(1)
        return ret_val

    # 返回堆是否为空
    def is_empty(self):
        return self.current_size == 0

    # 返回堆中key的个数
    def size(self):
        return self.current_size

    # 从一个key列表创建新堆
    def build_heap(self, lst):
        i = len(lst) // 2
        self.current_size = len(lst)
        self.heap_list = [0] + lst[:]
        print(len(self.heap_list), i)
        while i > 0:
            print(self.heap_list, i)
            self.perc_down(i)
            i -= 1
        print(self.heap_list, i)

    def perc_up(self, i):
        while i // 2 > 0:
            # 如果父节点大于当前节点,则与父节点交换位置
            if self.heap_list[i] < self.heap_list[i//2]:
                self.heap_list[i // 2], self.heap_list[i] = \
                    self.heap_list[i], self.heap_list[i//2]
                i //= 2
            else:
                break

    def perc_down(self, i):
        while i*2 <= self.current_size:
            # 找到左右子节点中较小的子节点进行交换
            mc = self.min_child(i)
            if self.heap_list[i] > self.heap_list[mc]:
                # 交换下沉
                self.heap_list[i], self.heap_list[mc] = \
                    self.heap_list[mc], self.heap_list[i]
                i = mc
            else:
                break

    def min_child(self, i):
        # 只有一个子节点
        if i*2 + 1 > self.current_size:
            return i*2
        else:
            # 选择较小的子节点
            if self.heap_list[i*2] < self.heap_list[i*2+1]:
                return i*2
            else:
                return i*2 + 1

3.7 二叉查找树

在这里插入图片描述
在这里插入图片描述
二叉查找树实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值