数据结构与算法(完结!)

目录

一、算法的引入

1.1 感受算法(demo)

1.2 常见的时间复杂度

1.3 列表时间复杂度和字典时间复杂度的比较

1.4 认识数据结构和算法的关系

二、顺序表

1.1 认识顺序表和内存地址的关系

1.2 顺序表

1.3 顺序表的扩充机制

1.4 列表底层逻辑的实现

三、链表

3.1 链表概念

3.2 单向链表:

3.2.1 单向链表的实现:

3.2.2 添加(add)头部节点图解

3.2.3 插入insert实现思路

3.2.4 append尾部加入数据

3.2.5 remove 删除节点

3.3单向循环链表

3.3.1单向循环链表的实现

3.4双向链表

3.4.1 双向链表的实现

四、栈和队列

1、栈

1.1 线性表

1.2 栈的特点

1.3 栈实现操作

1.3.1 栈-----用顺序表实现

1.3.2 栈-----用链表实现

1.4 栈的应用

2、队列

2.1 队列的特点

2.2 队列实现操作

2.2.1 队列 -- 顺序表实现

2.2.2 队列 -- 链表实现

2.3 思考题

3、 双端队列

3.1 双端队列特点

3.2 双端队列实现操作

3.2.1 双端队列 - 顺序表实现

3.2.2 双端队列 - 链表实现

五、排序

1、排序算法的稳定性

2、冒泡排序

3、选择排序

4、插入排序

5、希尔排序

6、快速排序(重点)

7、归并排序

8、桶排序

9、哈希排序

9.1 哈希冲突

9.1.1  开链法

9.1.2 多哈希法

9.1.3 开放寻址法

9.2 扩容问题

10、二分查找(重点)

10.1 查找

10.2 二分查找

六、树

1、树&二叉树

1.1 树

1.2 树的语术

1.3 二叉树

1.4 二叉树存储结构

2、树的广度优先遍历

3、树的深度优先遍历

4、根据前序遍历与中序遍历确定树


一、算法的引入

1.1 感受算法(demo)

写一个demo,如果 a+b+c=1000,且 a^2+b^2=c^2(a,b,c 为自然数),如何求出所有a、b、c可能的组合?(最初的想法是枚举法)

# 如果 a+b+c=1000,且 a^2+b^2=c^2(a,b,c 为自然数),如何求出所有a、b、c可能的组合?

# 第一步  分析需求

# 找到所有满足以上两个条件的a,b,c的组合

# 第二步  设计算法  # 枚举法

# 尝试a,b,c的所有组合,判断当前的组合是否满足以上两个条件,如果满足,就输出,否则就尝试下一个组合

# 第三步 代码实现
import time

start_time = time.time()  # 开始时间

for a in range(0, 1001):
    for b in range(0, 1001):
        for c in range(0, 1001):
        '''
         c = 1000 - a - b  # (算法的优化,c就不需要循坏,优化法)
         if  a**2 + b**2 == c**2
        '''
            if a + b + c == 1000 and a**2 + b**2 == c**2:
                print(f'组合{a},{b},{c}满足条件')

end_time = time.time()  # 结束时间
print('程序执行的时间为:', end_time-start_time, '秒')

算法的特点:

  • 输入项: 算法具有0个或多个输入
  • 输出项: 算法至少有1个输出
  • 有穷性: 算法必须能在有限个步骤之后终止,并且需要在可接受的时间内
  • 确切性: 算法的每一步骤必须有确切的定义
  • 可行性: 算法的每一步都是可行的

判断一个算法的好坏主要是通过看时间复杂度

区别:时间频度和时间复杂度的概念

时间频度:一个算法中的语句执行次数称为语句频度或时间频度, 记为T(n)

时间复杂度的概念:全称渐进时间复杂度,描述随着问题的数据规模的增长,算法的时间频度的增长趋势.记作O(F(n)),F(n)是T(n)的渐进函数.

根据上面的小测试,计算时间频度 T   时间复杂度
# 程序运行的时间不一样,但是运行的步骤是一样的
# 程序运行的步骤 时间频度 T
# T = 1000 * 1000 * 8
# T = 1000 * 1000 * 3
# t = 2000 * 2000 * 3
# 如果说把问题的数据规模设为n,T=n*n*3
# T = 3*n^2
# 当n为无穷大时,时间频度的式子中,谁的值最大,那么时间复杂度就是谁
# O(n^2)
# T = 3*n^3 + 2*n^2 + 10000  n^3 + n^2
# O(n^3)

时间复杂度的计算方法:

  • 计算时,往往只关注时间频度中的最高次项,其他次要项和常数项忽略
  • 顺序结构,时间复杂度按加法来计算
(例如:让用户输入两个列表,一个列表的长度是m,另一个长度是n,对这两个列表分别求和,比较它们的和的大小,循环遍历,分别求和,比较大小,m步,n步 O(m+n))
  • 循环结构,时间复杂度按乘法来计算
  • 分支结构,时间复杂度取最大值
  • 没有特殊说明时,算法的时间复杂度都是指最坏的时间复杂度

1.2 常见的时间复杂度

1.3 列表时间复杂度和字典时间复杂度的比较

1.4 认识数据结构和算法的关系

什么是数据结构?

数据结构 是计算机存储、组织数据的方式. 数据结构 将数据以某种关系组织在一起。

算法是基于数据结构去设计的 也可以说算法的第一步就是选择数据结构

程序 = 数据结构 + 算法

二、顺序表

1.1 认识顺序表和内存地址的关系

问题:1, 2, 3, 4, 5这样一组数据如何以某种关系存储在计算机中 使得只要知道其中一个元素的地址 就可以得到其他元素的地址?

位(bit) 最小的存储单位,每一位存储一个1位的二进制码

字节(byte) 由8个bit组成的存储单元

常见数据类型的内存大小

1、整数类型:

int (整数类型)= 4个字节 = 32bit

float(浮点数)=4个字节(单精度浮点数)/8个字节(双精度浮点数)

2、布尔类型:

bool = 1字节 (但实际上,一个字节是最小的可寻址内存单元,因此通常只能以字节为单位)

3、字符串

char(字符)=1字节

wchar_t(宽字符)=2字节或4字节(取决于平台)

4、数组和列表

  • list(列表):在Python中,列表的内存大小是动态变化的,取决于列表中包含的元素数量和元素类型。
  • array(数组):在其他语言中,数组的内存大小也是根据元素类型和数量动态计算的。

5、字符串

str(字符串):在Python中,字符串的内存大小也是动态变化的,取决于字符串的长度和编码方式

6、字典和集合

dict(字典):在Python中,字典的内存大小也是动态变化的,取决于键值对的数量和键值的类型。

set(集合):在Python中,集合的内存大小也是动态变化的,取决于集合中元素的数量和元素的类型

7、自定义对象

内存大小取决于对象的成员变量和其类型。在Python中,可以使用sys.getsizeof()函数来获取对象占用的内存大小

1.2 顺序表

1.3 顺序表的扩充机制

一体式顺序表: 表头和数据区存储在一起,扩容时表头也要跟着变化

分离式顺序表: 表头和数据区分开存储,扩容时表头不需要变化(列表)

1.4 列表底层逻辑的实现

lst = []
"""
列表对象
表头 容量 元素个数
数据区
lst.allocated 容量
lst.size 元素个数
lst.items = 数据区
"""
lst.append(1)


PY_SSIZE_T_MAX = float('inf') #列表最大的容量
obj_size = 1

class List:
    allocated = 0
    size = 0
    items = []

    def list_resize(self, new_size):
        """
        self: 列表对象本身
        :param new_size: 修改之后的元素个数
        :return:
        """
        allocated = self.allocated  # 获取列表对象当前的容量
        # allocated >> 1 ==> allocated // 2
        if allocated >= new_size >= (allocated >> 1):
            self.size = new_size
            return 0
        # 计算需要的内存容量是多少
        new_allocated = new_size + (new_size >> 3) + (3 if new_size < 9 else 6)
        if new_allocated > PY_SSIZE_T_MAX:
            return -1

        if new_size == 0:
            new_allocated = 0

        # 计算我们容量需要的字节数
        num_allocated_bytes = new_allocated * obj_size
        # 获取到新的内存空间的地址
        items = addr(self.items, num_allocated_bytes)
        if items == None:
            return -1
        # 让列表对象的数据区地址指向新的内存空间的地址
        self.items = items
        self.size = new_size
        self.allocated = new_allocated
        return 0

三、链表

3.1 链表概念

链表是一种物理存储单元上非连续、非顺序的存储结构 数据元素的逻辑顺序通过链表中的指针链接次序实现.

链表由一系列结点组成,结点可以在运行时动态生成, 每个结点包括两个部分:存储数据元素的数据域、存储下一个节点地址的指针域

3.2 单向链表:

3.2.1 单向链表的实现:

3.2.2 添加(add)头部节点图解

3.2.3 插入insert实现思路

3.2.4 append尾部加入数据

3.2.5 remove 删除节点

class Node:
    # 节点类
    def __init__(self, data, _next=None):
        self.data = data  # 数据域
        self.next = _next  # 指针域


class SingleLinkList:
    def __init__(self):
        self.head = None  # 链表的头结点
        self._length = 0  # 链表的长度,链表的元素个数

    def is_empty(self):
        # 判断链表是否为空
        return self._length == 0

    def length(self):
        # 返回链表的长度
        return self._length

    def nodes_list(self):
        # 返回链表中的所有节点的值组成的列表
        res = []
        cur = self.head
        while cur:
            res.append(cur.data)
            cur = cur.next
        return res

    def add(self, data):
        # 往链表的头部添加一个节点,值为data
        # 新建一个节点node
        node = Node(data)
        # 先让node指向当前链表中的头结点
        node.next = self.head
        # 再让链表的head指向当前node节点
        self.head = node
        # 添加节点之后,链表的长度加1
        self._length += 1

    def append(self, data):
        # 往链表的尾部添加一个节点,值为data
        # 新建一个节点node,值为data
        node = Node(data)
        # 找到链表的尾节点
        # 从头结点开始,遍历链表中的所有节点
        # 每次判断当前节点的next是否为空
        # 为空说明当前节点就是尾节点
        # 不为空 通过当前节点的next去访问下一个节点,
        if self.head:
            cur = self.head
            while cur.next:
                cur = cur.next
            # 让当前的尾节点的指针域指向node
            cur.next = node
        else:
            self.head = node
        # 链表的长度+1
        self._length += 1

    def insert(self, pos, data):
        # 往链表的指定位置插入一个节点,值为data
        if pos <= 0:
            self.add(data)
        elif pos > self._length:
            self.append(data)
        else:
            # 正常的输入
            # 第一步 新建一个节点 node
            node = Node(data)
            # 第二步
            cur = self.head
            while pos - 1:
                cur = cur.next
                pos -= 1
            # 到这里之后,cur指向的是索引为pos-1的节点
            # 让node的next指向索引为pos的节点
            node.next = cur.next
            # 让索引为pos-1的节点的next指向cur
            cur.next = node
            self._length += 1

    def remove(self, data):
        # 删除链表中第一个值为data的结点
        cur = self.head
        prev = None  # 要删除的节点的前驱结点
        while cur:
            if cur.data == data:
                # 如果前驱结点为空,说明我们要删除的节点是第一个节点
                if not prev:
                    self.head = cur.next
                else:
                    prev.next = cur.next
                self._length -= 1
                return 0
            prev = cur
            cur = cur.next
        return -1

    def modify(self, pos, data):
        # 修改链表中指定位置节点的值
        if 0 <= pos < self._length:
            cur = self.head
            while pos:
                cur = cur.next
                pos -= 1
            cur.data = data
        else:
            print('你输入的范围不符合要求')

    def search(self, data):
        # 查找链表中是否有节点的值为data
        cur = self.head
        while cur:
            if cur.data == data:
                return True
            cur = cur.next
        return False


if __name__ == '__main__':
    l1 = SingleLinkList()  # 新建一个链表类
    print(l1.head, l1.length())
    l1.add(1)
    print(l1.head.data, l1.length())
    l1.add(3)
    print(l1.head.data, l1.length())
    print(l1.nodes_list())
    l1.append(4)
    print(l1.head.data, l1.length())
    print(l1.nodes_list())
    l1.insert(7, 7)
    print(l1.nodes_list())
    print(l1.search(11))

3.3单向循环链表

3.3.1单向循环链表的实现

class Node:
    # 节点类
    def __init__(self, data, _next=None):
        self.data = data  # 数据域
        self.next = _next  # 指针域


class SingleCycleLinkList:
    def __init__(self):
        self.head = None  # 链表的头结点
        self._length = 0  # 链表的长度,链表的元素个数
        self.tail = None  # 链表的尾节点

    def is_empty(self):
        # 判断链表是否为空
        return self._length == 0

    def length(self):
        # 返回链表的长度
        return self._length

    def nodes_list(self):
        # 返回链表中的所有节点的值组成的列表
        res = []
        if self.is_empty():
            return res
        res.append(self.head.data)
        cur = self.head.next
        while cur != self.head:
            res.append(cur.data)
            cur = cur.next
        return res

    def add(self, data):
        # 往链表的头部添加一个节点,值为data
        # 新建一个节点node
        node = Node(data)
        if self.is_empty():
            self.head = node
            node.next = self.head
        else:
            # 先让node指向当前链表中的头结点
            node.next = self.head
            # 让链表的尾节点的next指向Node
            cur = self.head
            while cur.next != self.head:
                cur = cur.next
            cur.next = node
            # 再让链表的head指向当前node节点
            self.head = node
        # 添加节点之后,链表的长度加1
        self._length += 1

    def append(self, data):
        # 往链表的尾部添加一个节点,值为data
        # 新建一个节点node,值为data
        node = Node(data)
        # 找到链表的尾节点
        # 从头结点开始,遍历链表中的所有节点
        # 每次判断当前节点的next是否为空
        # 为空说明当前节点就是尾节点
        # 不为空 通过当前节点的next去访问下一个节点,
        if self.head:
            cur = self.head
            while cur.next != self.head:
                cur = cur.next
            cur.next = node  # 原本的尾节点指向新建的节点
        else:
            self.head = node
        node.next = self.head  # 新的尾节点指向当前的头结点
        # 链表的长度+1
        self._length += 1

    def insert(self, pos, data):
        # 往链表的指定位置插入一个节点,值为data
        if pos <= 0:
            self.add(data)
        elif pos > self._length:
            self.append(data)
        else:
            # 正常的输入
            # 第一步 新建一个节点 node
            node = Node(data)
            # 第二步
            cur = self.head
            while pos - 1:
                cur = cur.next
                pos -= 1
            # 到这里之后,cur指向的是索引为pos-1的节点
            # 让node的next指向索引为pos的节点
            node.next = cur.next
            # 让索引为pos-1的节点的next指向cur
            cur.next = node
            self._length += 1

    def remove(self, data):
        # 判断一下链表是否为空,为空那必然没有值为data的节点
        if self.is_empty():
            return -1
        # 删除链表中第一个值为data的结点
        cur = self.head
        flag = True  # 标志位的作用是,让第一次循环能进入
        prev = None  # 要删除的节点的前驱结点
        while cur != self.head or flag:
            flag = False  # 让循环继续的条件就必须是cur != self.head
            if cur.data == data:
                # 如果前驱结点为空,说明我们要删除的节点是第一个节点
                if not prev:
                    # 找到尾节点
                    last_node = self.head
                    while last_node.next != self.head:
                        last_node = last_node.next
                    # 让尾节点的next指向新的head
                    last_node.next = self.head.next
                    self.head = cur.next  # self.head.next
                else:
                    prev.next = cur.next
                self._length -= 1
                return 0
            prev = cur
            cur = cur.next
        return -1

    def modify(self, pos, data):
        # 修改链表中指定位置节点的值
        if 0 <= pos < self._length:
            cur = self.head
            while pos:
                cur = cur.next
                pos -= 1
            cur.data = data
        else:
            print('你输入的范围不符合要求')

    def search(self, data):
        # 查找链表中是否有节点的值为data
        if self.is_empty():
            return False
        cur = self.head
        flag = True
        while cur != self.head or flag:
            flag = False
            if cur.data == data:
                return True
            cur = cur.next
        return False


if __name__ == '__main__':
    l1 = SingleCycleLinkList()  # 新建一个链表类
    l1.add(1)
    l1.add(2)
    l1.add(3)
    l1.add(4)
    l1.add(2)
    print(l1.nodes_list())
    print(l1.search(5))
    # l1.insert(7, 7)
    # print(l1.nodes_list())
    # print(l1.search(11))

3.4双向链表

3.4.1 双向链表的实现

class Node:
    # 节点类
    def __init__(self, data, _prev=None, _next=None):
        self.prev = _prev  # 指针域 指向的是当前节点的前一个节点
        self.data = data  # 数据域
        self.next = _next  # 指针域 指向的是当前节点的下一个节点


class DoubleLinkList:
    def __init__(self):
        self.head = None  # 链表的头结点
        self._length = 0  # 链表的长度,链表的元素个数

    def is_empty(self):
        # 判断链表是否为空
        return self._length == 0

    def length(self):
        # 返回链表的长度
        return self._length

    def nodes_list(self):
        # 返回链表中的所有节点的值组成的列表
        res = []
        cur = self.head
        while cur:
            res.append(cur.data)
            cur = cur.next
        return res

    def add(self, data):
        # 往链表的头部添加一个节点,值为data
        # 新建一个节点node
        node = Node(data)
        # None.prev
        if self.is_empty():
            self.head = node
        else:
            self.head.prev = node  # 让链表中原本的头结点的prev指向新建的节点
            # 先让node指向当前链表中的头结点
            node.next = self.head
            # 再让链表的head指向当前node节点
            self.head = node
        # 添加节点之后,链表的长度加1
        self._length += 1

    def append(self, data):
        # 往链表的尾部添加一个节点,值为data
        # 新建一个节点node,值为data
        node = Node(data)
        # 找到链表的尾节点
        # 从头结点开始,遍历链表中的所有节点
        # 每次判断当前节点的next是否为空
        # 为空说明当前节点就是尾节点
        # 不为空 通过当前节点的next去访问下一个节点,
        if self.head:
            cur = self.head
            while cur.next:
                cur = cur.next
            # 让当前的尾节点的指针域指向node
            node.prev = cur  # 让node的prev指针域去指向原本的尾节点
            cur.next = node  # 让原本的尾节点的next去指向新建的节点
        else:
            self.head = node
        # 链表的长度+1
        self._length += 1

    def insert(self, pos, data):
        # 往链表的指定位置插入一个节点,值为data
        if pos <= 0:
            self.add(data)
        elif pos > self._length:
            self.append(data)
        else:
            # 正常的输入
            # 第一步 新建一个节点 node
            node = Node(data)
            # 第二步
            cur = self.head
            while pos - 1:
                cur = cur.next
                pos -= 1
            # 到这里之后,cur指向的是索引为pos-1的节点
            # 让node的prev指向索引为pos-1的节点
            node.prev = cur
            # 让索引为pos的节点指向新建的节点
            cur.next.prev = node
            # 让node的next指向索引为pos的节点
            node.next = cur.next
            # 让索引为pos-1的节点的next指向cur
            cur.next = node
            self._length += 1

    def remove(self, data):
        # 删除链表中第一个值为data的结点
        cur = self.head
        while cur:
            if cur.data == data:
                # 如果前驱结点为空,说明我们要删除的节点是第一个节点
                if cur == self.head:
                    self.head = cur.next
                else:
                    cur.prev.next = cur.next
                if cur.next:
                    cur.next.prev = cur.prev
                self._length -= 1
                return 0
            cur = cur.next
        return -1

    def modify(self, pos, data):
        # 修改链表中指定位置节点的值
        if 0 <= pos < self._length:
            cur = self.head
            while pos:
                cur = cur.next
                pos -= 1
            cur.data = data
        else:
            print('你输入的范围不符合要求')

    def search(self, data):
        # 查找链表中是否有节点的值为data
        cur = self.head
        while cur:
            if cur.data == data:
                return True
            cur = cur.next
        return False


if __name__ == '__main__':
    l1 = DoubleLinkList()
    print(l1.nodes_list())
    l1.add(1)
    print(l1.nodes_list())
    l1.add(2)
    print(l1.nodes_list())
    l1.append(3)
    print(l1.nodes_list())
    l1.insert(7, 4)
    print(l1.nodes_list())
    l1.modify(1, 11)
    print(l1.nodes_list())
    print(l1.search(1))

四、栈和队列

1、栈

1.1 线性表

线性表: 由零个或多个数据元素组成的有限序列 除了第一个节点外,均有唯一的前驱结点 除了最后一个节点外,均有唯一的后继结点

线性表主要由顺序存储结构或者链式存储结构

一般线性表: 可以自由的操作节点,例如顺序表,链表

受限线性表: 对节点的操作受到限制,例如栈和队列

1.2 栈的特点

1.3 栈实现操作

1.3.1 栈-----用顺序表实现
# 栈 -- 用顺序表实现


class Stack:
    def __init__(self):
        # 把列表的最后一个元素作为栈顶
        self.__data = []

    def push(self, item):
        # 添加一个元素item到栈顶
        self.__data.append(item)

    def pop(self):
        # 要判断栈是否为空
        if self.is_empty():
            raise ValueError('栈为空')
        return self.__data.pop()

    def top(self):
        # 要判断栈是否为空
        if self.is_empty():
            raise ValueError('栈为空')
        return self.__data[-1]

    def is_empty(self):
        return self.__data == []

    def size(self):
        return len(self.__data)


if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(2)
    stack.push(3)
    stack.push(4)
    print(stack.pop())
    print(stack.pop())
    print(stack.pop())
    print(stack.pop())
    print(stack.pop())

1.3.2 栈-----用链表实现
# 栈 -- 用链表实现


class Node:
    def __init__(self, data, _next=None):
        self.data = data  # 数据域
        self.next = _next  # 指针域


class Stack:
    def __init__(self):
        # 以链表的第一个节点作为栈顶
        self.__top = None  # 栈顶元素
        self._size = 0  # 栈的元素个数

    def push(self, item):
        # 添加一个元素item到栈顶
        # 让self.top指向新的节点
        # 让新的节点的next指向原本的栈顶
        self.__top = Node(item, self.__top)
        self._size += 1

    def pop(self):
        # 要判断栈是否为空
        if self.is_empty():
            raise ValueError('栈为空')
        value = self.__top.data
        self.__top = self.__top.next
        self._size -= 1
        return value

    def top(self):
        # 要判断栈是否为空
        if self.is_empty():
            raise ValueError('栈为空')
        return self.__top.data

    def is_empty(self):
        return self._size == 0

    def size(self):
        return self._size


if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(2)
    stack.push(3)
    stack.push(4)
    print(stack.pop())
    print(stack.pop())
    print(stack.pop())
    print(stack.pop())
    print(stack.pop())

1.4 栈的应用

力扣第20题: 有效的括号

给定一个只包括‘(’, ‘)’, ‘[’, ‘]’, ‘{’, ‘}’的字符串

判断该字符串是否有效 有效字符串需满足:   

 1. 左括号必须用相同类型的右括号闭合     

2. 左括号必须以正确的顺序闭合

空字符串可被认为是有效字符串

例如: “{}”结果为True, “({[]})”结果为True, “(]”结果为False, “([)]”结果为False

from stack_02 import Stack
# 遍历字符串
# 遇到左边括号,就入栈
# 遇到右边括号,栈是否为空
# 为空-> False
# 不为空, 弹出栈顶元素
# 弹出的栈顶元素和遇到的右边括号匹配一下,看是否是相同类型
# 是不同类型,返回False
# 是相同类型,继续往下遍历
# 如果字符串全部匹配完了
# 栈为空 -- > True
# 栈不为空 --> False


def func(string):
    if len(string) % 2:
        return False
    stack = Stack()
    dic = {
        '(': ')',
        '[': ']',
        '{': '}'
    }
    for char in string:
        # 判断遇到的是左括号还是右括号
        if char in '([{':
            stack.push(dic[char])
        else:
            if stack.is_empty() or stack.pop() != char:
                return False

    return stack.is_empty()


if __name__ == '__main__':
    print(func('('*20+')'*19))

2、队列

2.1 队列的特点

2.2 队列实现操作

2.2.1 队列 -- 顺序表实现
# 队列 -- 顺序表实现


class Queue:
    def __init__(self, size):
        # 以列表的最后一个元素作为队尾
        self.items = [None] * size  # 先声明长度为size的数据区
        self.head = 0  # 对首的索引
        self._length = 0  # 队列的长度
        self.size = size  # 队列的最大长度

    def is_empty(self):
        return self._length == 0

    def length(self):
        return self._length

    def push(self, item):
        if self.length() == self.size:
            raise ValueError('队列已满')
        # 先算出要添加的元素的索引
        idx = (self.head + self.length()) % self.size
        self.items[idx] = item
        self._length += 1

    def pop(self):
        # 抛出队首元素
        if self.is_empty():
            raise ValueError('队列是空的')
        value = self.items[self.head]
        self.head = (self.head + 1) % self.size
        self._length -= 1
        return value

    def peek(self):
        if self.is_empty():
            raise ValueError('队列是空的')
        return self.items[self.head]


if __name__ == '__main__':
    queue = Queue(3)
    queue.push(1)
    queue.push(2)
    queue.push(3)
    print(queue.pop())  # 1
    queue.push(4)
    print(queue.length())  # 3
    print(queue.peek())  # 2
    print(queue.pop())  # 2
    print(queue.pop())  # 3
    print(queue.pop())  # 4
    print(queue.items)
    # print(queue.pop())
2.2.2 队列 -- 链表实现
# 队列 -- 链表实现


class Node:
    def __init__(self, data, _next=None):
        self.data = data  # 数据域
        self.next = _next  # 指针域


class Queue:
    def __init__(self, size):
        self.head = None  # 队列的队头
        self.rear = None  # 队列的队尾
        self._length = 0  # 队列的长度
        self.size = size  # 队列的最大长度

    def is_empty(self):
        return self._length == 0

    def length(self):
        return self._length

    def push(self, item):
        # 判断队列是否已满
        if self.length() == self.size:
            raise ValueError('队列已满')
        # 添加一个元素item到队尾
        # 最后一个元素作为队尾
        node = Node(item)
        # 如果队列是空
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            self.rear.next = node
            self.rear = node
        self._length += 1

    def pop(self):
        # 抛出队首元素
        if self.is_empty():
            raise ValueError('队列是空的')
        value = self.head.data
        self.head = self.head.next
        self._length -= 1
        return value

    def peek(self):
        if self.is_empty():
            raise ValueError('队列是空的')
        return self.head.data


if __name__ == '__main__':
    queue = Queue(3)
    queue.push(1)
    queue.push(2)
    queue.push(3)
    print(queue.pop())  # 1
    queue.push(4)
    print(queue.length())  # 3
    print(queue.peek())  # 2
    print(queue.pop())  # 2
    print(queue.pop())  # 3
    print(queue.pop())  # 4
    # print(queue.pop())

2.3 思考题

在现实生活中,我们的队列常常会有长度限制,比如队列中已经有n个元素了,那么就提示队列已满

你能否实现一个限制长度的队列呢?

如果实现一个限制长度的队列, 用顺序结构该如何去实现才能做到所有操作的时间复杂度都是O(1)呢?

3、 双端队列

3.1 双端队列特点

3.2 双端队列实现操作

3.2.1 双端队列 - 顺序表实现
# 双端队列 - 顺序表实现


class Deque:
    def __init__(self):
        self.items = []

    def is_empty(self):
        return self.items == []

    def length(self):
        return len(self.items)

    def push(self, item):
        self.items.append(item)

    def push_left(self, item):
        self.items.insert(0, item)

    def pop(self):
        return self.items.pop(0)

    def pop_right(self):
        return self.items.pop()

    def peek(self):
        return self.items[0]


if __name__ == '__main__':
    deque = Deque()
    deque.push(1)  # 1
    deque.push(2)  # 1, 2
    deque.push_left(3)  # 3, 1, 2
    deque.push_left(4)  # 4, 3, 1, 2
    print(deque.items)
    deque.pop()
    print(deque.items)  # 3, 1, 2
    deque.pop_right()
    print(deque.items)  # 3, 1

3.2.2 双端队列 - 链表实现
# 双端队列 -- 链表实现


class Node:
    def __init__(self, data, _next=None):
        self.data = data  # 数据域
        self.next = _next  # 指针域


class Deque:
    def __init__(self):
        self.head = None  # 队首
        self.rear = None  # 队尾
        self._length = 0  # 队列的长度

    def is_empty(self):
        return self._length == 0

    def length(self):
        return self._length

    def items(self):
        cur = self.head
        while cur:
            print(cur.data, '->', end=' ')
            cur = cur.next
        print()

    def push(self, item):
        # 在队尾添加一个元素item
        node = Node(item)
        # 队列为空
        if self.is_empty():
            self.head = node
            self.rear = node
        # 队列不为空
        else:
            self.rear.next = node
            self.rear = node
        self._length += 1

    def push_left(self, item):
        # 在队首添加一个元素item
        node = Node(item)
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            node.next = self.head
            self.head = node
        self._length += 1

    def pop(self):
        # 弹出队首元素
        if self.is_empty():
            raise ValueError('双端队列为空')
        value = self.head.data
        self.head = self.head.next
        self._length -= 1
        if self._length == 0:
            self.rear = None
        return value

    def pop_right(self):
        # 弹出队尾元素
        if self.is_empty():
            raise ValueError('双端队列为空')
        if self.length() == 1:
            return self.pop()
        cur = self.head
        value = self.rear.data
        while cur.next != self.rear:
            cur = cur.next
        self.rear = cur
        cur.next = None
        self._length -= 1
        return value

    def peek(self):
        if self.is_empty():
            raise ValueError('双端队列为空')
        return self.head.data


if __name__ == '__main__':
    deque = Deque()
    deque.push(1)  # 1
    deque.push(2)  # 1, 2
    deque.push_left(3)  # 3, 1, 2
    deque.push_left(4)  # 4, 3, 1, 2
    deque.items()
    deque.pop()
    deque.items()
    deque.pop_right()
    deque.items()

五、排序

1、排序算法的稳定性

稳定的排序算法会让原本有相等键值的记录维持相对次序;

不稳定的排序算法可能会改变相等键值的记录的相对次序。

2、冒泡排序

  • 重复比较相邻的元素,如果前面的比后面的大,就交换它们两个
  • 每次遍历整个数组,遍历完成后,下一次遍历的范围往左缩1位
  • 重复前面步骤,直到排序完成
  • 时间复杂度o(n^2)

# 冒泡排序(稳定的排序)

def bubbleSort(nums):
    n = len(nums)  # 得到数组的长度
    for i in range(n-1):
        flag = False  # 表示本轮是否有进行变量交换
        for idx in range(0, n-1-i):
            if nums[idx] > nums[idx+1]:
                nums[idx], nums[idx+1] = nums[idx+1], nums[idx]
                flag = True
            print(f'第{i+1}趟排序:', nums)
        # 如果flag为False,那说明本轮排序没有进行任何变量交换
        # 数组已经是有序的了,
        if not flag:
            break

# O(n^2)

test = [6, 5, 4, 3, 2, 1]
bubbleSort(test)  # 1, 2, 3, 4, 5, 6
print(test)

3、选择排序

  • 初始状态: 有序区为空, 无序区为[0,…,n]
  • 每次找到无序区里的最小元素,添加到有序区的最后
  • 重复前面步骤,直到排序完成
  • 时间复杂度o(n^2)

# 选择排序(是不稳定的排序)
def selectionSort(nums):
    n = len(nums)  # 数组的长度
    print(nums)
    for i in range(n-1):
        # 找无序区中最小的元素
        min_idx = i  # 无序区中的最小元素的索引
        for j in range(i+1, n):
            if nums[j] < nums[min_idx]:
                min_idx = j
        # 执行完上面的循环后
        # min_idx就是无序区中的最小元素的索引
        # 把最小元素和有序区的后一个元素交换位置
        nums[i], nums[min_idx] = nums[min_idx], nums[i]
        print(f'第{i+1}排序:', nums)


test = [9, 3, 1, 2, 4, 7]
selectionSort(test)
print(test)

4、插入排序

每次都将后面没排好序的数插入到前面排好序的数中。(稳定的排序)

时间复杂度o(n^2)

# 插入排序


def insertSort(nums):
    n = len(nums)  # 数组的长度
    # 设定一个增量gap
    # gap = n // 2
    # while gap >= 1:
    #     分组 + 对每一组进行插入排序
    #     缩小增量 gap //= 2
    for i in range(n-1):
        curNum = nums[i+1]  # 无序区的第一个元素的值
        idx = i  # 有序区的最后一个元素的索引
        while idx >= 0 and nums[idx] > curNum:
            nums[idx+1] = nums[idx]  # 把有序区的元素往后挪一位
            idx -= 1  # 指针往前移,以此来从后往前遍历有序区
        nums[idx+1] = curNum
        print(f'第{i+1}趟排序', nums)


test = [9, 3, 1, 2, 7, 5]
insertSort(test)
print(test)

5、希尔排序

希尔排序是插入排序的优化版本,步骤:

  • 设定一个增量gap,将数组按照gap分组
  • 对每一组进行插入排序
  • 缩小增量gap,重复前两个步骤,直到gap缩小到1,那么最后一次排序就是 插入排序

# 希尔排序
def shellSort_1(nums):
    n = len(nums)  # 数组的长度
    # 设定一个增量gap
    gap = 1
    while gap < n // 3:
        gap = gap * 3 + 1
    while gap >= 1:
        # 分组
        for i in range(gap):  # gap=5, i=>0,1,2,3,4
            # 对每一个小组进行插入排序
            for j in range(i, n-gap, gap):
                curNum = nums[j+gap]  # 无序区的第一个元素的值
                idx = j  # 有序区的最后一个元素的索引
                while idx >= 0 and nums[idx] > curNum:
                    nums[idx+gap] = nums[idx]  # 把小组的有序区的元素往后挪
                    idx -= gap  # 指针往前移,以此来从后往前遍历小组的有序区
                nums[idx+gap] = curNum
            print(f'当前增量是{gap}, ', nums)
        gap //= 3  # 缩小增量


def shellSort_2(nums):
    n = len(nums)  # 数组的长度
    # 设定一个增量gap
    gap = n // 2
    while gap >= 1:
        # 分组
        for i in range(gap, n):
            # i = 5, curNum=43, idx = 0
            # [43, ..., 44, ...., 85]
            # i=7, curNum=7, idx=2
            # [..., 7, ... 59, ...]
            curNum = nums[i]  # 当前要插入的无序区的元素的值
            idx = i - gap  # 当前元素所在小组的有序区的最后一个元素的索引
            while idx >= 0 and curNum < nums[idx]:
                nums[idx+gap] = nums[idx]
                idx -= gap
            nums[idx+gap] = curNum
        gap //= 2  # 缩小增量


# gap=5 [44, 43, 85], [12, 94], [59, 7], [36, 35], [62, 52]
test = [44, 12, 59, 36, 62, 43, 94, 7, 35, 52, 85]
shellSort_2(test)
print(test)

6、快速排序(重点)

分而治之

  • 设定一个基准值pivot
  • 将数组重新排列,所有比pivot小的放在其前面,比pivot大的放后面,这操作 称为分区(partition)操作
  • 对两边的数组重复前两个步骤

# 快速排序

def quickSort(nums):
    n = len(nums)
    if n<=1:
        return nums
    pivot = nums[0]
    left = []
    right = []
    for i in range(1,n):
        if nums[i]>pivot:
            right.append(nums[i])
        else:
            left.append(nums[i])
    print(left)
    print(right)       
    print('-'*30)
    return quickSort(left)+[pivot]+quickSort(right)


# 快速排序的优化
"""
1.基准值的选取
(1)随机选取
(2)三数 mid

2.排序序列长度到一定大小后,改用插入排序

3.重复元素的处理
每次分割时,将与本次基准值相等的元素聚集在一起
(1)遇到相等的元素,放到区域的最左边或最右边
(2)分好区之后,相等的元素与基准值一边的元素进行交换

4.尾递归
"""
def partition(nums, left, right):
    pivot = nums[left]  # 区域的第一个元素作为基准值
    while left < right:
        # 挖坑,填坑
        while left < right and nums[right] > pivot:
            right -= 1
        nums[left] = nums[right]
        while left < right and nums[left] <= pivot:
            left += 1
        nums[right] = nums[left]
    nums[left] = pivot  # 基准值的正确位置
    return left


def quickSort(nums, left, right):
    if left >= right:
        return
    # 分区  --> 分好区之后的基准值的索引
    pivot_idx = partition(nums, left, right)
    print(nums, pivot_idx)
    # 左边的区域, left->pivot_idx-1
    quickSort(nums, left, pivot_idx-1)
    # 右边的区域, pivot_idx+1->right
    quickSort(nums, pivot_idx+1, right)


test = [44, 12, 59, 36, 62, 43, 94, 7, 35, 52, 85]
quickSort(test, 0, len(test)-1)
print(test)

7、归并排序

# 归并排序


def merge(left, right):
    # 最终返回一个合并好的有序的数组
    # 定义两个变量,分别代表当前left与right的未添加进有序数组的第一个元素
    left_idx, right_idx = 0, 0
    res = []  # 有序数组
    while left_idx < len(left) and right_idx < len(right):
        # 左边数组的元素小于右边数组
        if left[left_idx] <= right[right_idx]:
            # 把左边元素添加到有序区中
            res.append(left[left_idx])
            # 索引往后移
            left_idx += 1
        else:
            # 把右边元素添加到有序区中
            res.append(right[right_idx])
            # 索引往后移
            right_idx += 1
    res += right[right_idx:]  # 把剩余的未添加的元素全部添加到有序数组后面
    res += left[left_idx:]  # 为什么可以直接添加?因为left,right本身就是一个有序数组
    # 如果说left_idx走完了,right还剩一些元素,说明right剩下的元素全部都比有序数组的最后一个元素要大
    return res


def mergeSort(nums):
    # 分
    # 数组不能再分了
    if len(nums) <= 1:
        return nums
    mid = len(nums) // 2  # 求出数组的中间位置
    print(nums[:mid], nums[mid:])
    left = mergeSort(nums[:mid])  # 左边的数组
    right = mergeSort(nums[mid:])  # 右边的数组
    # 合
    return merge(left, right)


test = [44, 12, 59, 36, 62, 43, 94, 7, 35, 52, 85]
test = mergeSort(test)
print(test)


# 时间复杂度 O(nlogn)
# 稳定性 稳定

8、桶排序

# 桶排序


def bucketSort(nums, size=5):
    # 根据数组的最大值与最小值确定要申请的桶的数量
    maxVal = max(nums)  # 最大值
    minVal = min(nums)  # 最小值
    bucketCount = (maxVal - minVal) // size + 1  # 桶的数量
    buckets = [[] for _ in range(bucketCount)]  # 申请桶
    for num in nums:
        idx = (num - minVal) // size  # num应该在哪个桶中,索引为idx
        n = len(buckets[idx])  # 求出当前桶中的元素个数
        i = 0
        # 找到第一个比num要大的元素
        while i < n and buckets[idx][i] <= num:
            i += 1
        buckets[idx].insert(i, num)
    print(buckets)
    # 合并桶
    nums.clear()
    for bucket in buckets:
        nums.extend(bucket)  # 将每个桶中的元素放到nums中


test = [1, 2, 3, 3, 3, 3, 3, 99]
bucketSort(test)
print(test)

9、哈希排序

空间换时间的一种算法,时间复杂度o(1)

9.1 哈希冲突

9.1.1  开链法

适合数据量比较大的

9.1.2 多哈希法

有多个哈希函数,当使用一个哈希函数发生冲突时,尝试下一个哈希函数,直到冲突不再发生。

9.1.3 开放寻址法

适合数据量比较大的

如果哈希函数得到的位置i已经有数据了,那么就往后探查新的位置来存储这个值

线性探测 如果i有数据了,则探查i+1,i+2..以此类推,直到找到空的位置

二次探测 如果位置i被占用,则探查i+1^2,i+2^2…以此类推,直到找到空的位置

9.2 扩容问题

使用开放寻址法,那么顺序表总归会有一天会填满

一般为了保证插入和查找的效率,哈希表一般在元素数量在容量的2/3时

就会进行扩容 扩容之后,计算的哈希函数也会随之变化,那么里面的数据存储的顺序也会 变化

10、二分查找(重点)

10.1 查找

在一组数据中找某一个特定项的算法过程

通常用来判断某个特定项是否在一组数据中,最终返回True或False

常用的查找算法: 顺序查找,二分查找,树表查找,哈希查找等

顺序查找:遍历列表,看数据是否等于目标查找的数

10.2 二分查找

又称折半查找

要求待查表为有序表

表中间位置记录的关键字查找关键字比较,如果相等则比较成功;否则 利用中间位置的记录缩小区间,继续查找缩小后的区间.

重复上面的步骤直到查找成功,或者子表不存在,则查找失败.

# 二分查找

# 递归


def binary_search(nums, target, left, right):
    """
    二分查找递归版
    :param nums: 待查找的数组,要求是升序的
    :param target: 要找的数字
    :param left: 区间的左边索引
    :param right: 区间的右边索引
    :return: target在nums中就返回True,否则返回False
    """
    # 递归的结束条件, left > right
    print(nums[left:right+1])
    if left > right:
        return False
    # 找中间值
    mid = (left + right) // 2  # 中间值的索引
    print(mid)
    # 判断中间值是否等于目标值
    if nums[mid] == target:
        return True
    # 如果中间值小于目标值,说明目标值只可能在中间值的右边区间
    if nums[mid] < target:
        # 左边区间的范围往右边缩
        return binary_search(nums, target, mid + 1, right)
    # 如果中间值大于目标值,说明目标值只可能在中间值的左边区间
    return binary_search(nums, target, left, mid - 1)


test = [1, 3, 4, 6, 8, 9, 15, 19, 44, 44]
print(binary_search(test, 15, 0, len(test)-1))  # True
print(binary_search(test, 14, 0, len(test)-1))  # False

六、树

1、树&二叉树

1.1 树

由n个有限节点组成一个具有层次关系的集合,看起来像一颗 倒挂的树。

特点:

  • 每个节点有0个或多个子节点
  • 没有父节点的节点称为根节点
  • 每一个非根节点有且只有一个父节点
  • 除了根节点外,每个子节点可以分为多个不相交的子树

1.2 树的语术

节点的度:     一个节点含有的子树的个数

树的度:         树中所有节点的度的最大值

叶节点:        度为0的节点

子节点:        一个节点含有的子树的根节点称为该节点的子节点

父节点:        若一个节点有子节点,那么这个节点就是其子节点的父节点

兄弟节点:    具有相同父节点的节点互称兄弟节点

堂兄弟节点:    在同一层的节点互称堂兄弟节点

祖先节点:    从根到该节点所经路径上的所有节点

子孙节点:    以某节点为根的子树中的所有节点

节点层次:    根节点层次为1,其他节点层次是父节点的层次加1

树的深度:    树中所有节点的层次的最大值

森林:        多颗不相交的树的集合

1.3 二叉树

二叉树:    每个节点最多含有两个子树的树称为二叉树

完全二叉树:  除了最底层外,其他各层的节点数目均达到最大值,且最底层的节点应从左往右紧密排列

满二叉树:  所有叶节点都在最底层的完全二叉树

二叉搜索树: 对于一个节点,它的左子树上的所有节点的值都比它小,右子树上的所有节点的值都比它大。

1.4 二叉树存储结构

顺序存储: 从上往下,从左往右的将树存到顺序表

优点: 遍历方便,可以用索引来表示节点间的关系

缺点: 可能会对存储空间造成极大的浪费 适用于存完全二叉树

链式存储: 每个节点具有 左指针域, 数据域, 右指针域, 以此来连接.

2、树的广度优先遍历

class Node:
    def __init__(self, val):
        self.val = val  # 数据域
        self.left = None  # 左指针域
        self.right = None  # 右指针域


class Tree:
    def __init__(self):
        self.root = None  # 树的根节点

    def add(self, val):
        # 层次遍历, 广度优先遍历
        # val 要添加的节点的值
        # 往树中添加一个节点,并保证添加之后这棵树依旧是一棵完全二叉树
        node = Node(val)
        # 判断树是否为空,如果为空,直接将node设置为根节点
        if not self.root:
            self.root = node
            return
        # 从上往下,从左往右的去遍历整棵树,然后找到第一个空位
        # 把节点添加进去
        queue = [self.root]  # 存每一层的节点
        while True:
            # 第一次 queue = [root]
            # 第二次 queue = [root.left, root.right]
            # queue = [root.right, root.left.left, root.left.right]
            # queue = [root.left.left, root.left.right, root.right.left, root.right.right]
            cur_node = queue.pop(0)
            # 先找左边,看有没有空位
            if not cur_node.left:
                cur_node.left = node
                return
            # 左边没有空位,就找右边
            elif not cur_node.right:
                cur_node.right = node
                return
            # 如果都没有空位,那就把左边节点与右边节点都加到之后要判断的节点中
            queue.extend((cur_node.left, cur_node.right))
    def show(self):
        # 展示树
        if not self.root:
            return
        # 从上往下,从左往右的去遍历整棵树,然后找到第一个空位
        # 把节点添加进去
        queue = [self.root]  # 存每一层的节点
        i = 1
        while queue:
            size = len(queue)  # 当前层的元素个数
            print(f'第{i}层', end='\t')
            for _ in range(size):
                node = queue.pop(0)  # 队列中的第一个元素抛出来
                print(node.val, end=' ')  # 对当前元素进行操作
                # 节点的左孩子与右孩子添加到队列里
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            print()
            i += 1
tree=Tree()
tree.add(0)
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
tree.add(6)
tree.add(7)
tree.add(8)
tree.show()

3、树的深度优先遍历

先序遍历:

中序遍历:

后序遍历:

class Node:
    def __init__(self, val):
        self.val = val  # 数据域
        self.left = None  # 左指针域
        self.right = None  # 右指针域


class Tree:
    def __init__(self):
        self.root = None  # 树的根节点

    def add(self, val):
        # 层次遍历, 广度优先遍历
        # val 要添加的节点的值
        # 往树中添加一个节点,并保证添加之后这棵树依旧是一棵完全二叉树
        node = Node(val)
        # 判断树是否为空,如果为空,直接将node设置为根节点
        if not self.root:
            self.root = node
            return
        # 从上往下,从左往右的去遍历整棵树,然后找到第一个空位
        # 把节点添加进去
        queue = [self.root]  # 存每一层的节点
        while True:
            # 第一次 queue = [root]
            # 第二次 queue = [root.left, root.right]
            # queue = [root.right, root.left.left, root.left.right]
            # queue = [root.left.left, root.left.right, root.right.left, root.right.right]
            cur_node = queue.pop(0)
            # 先找左边,看有没有空位
            if not cur_node.left:
                cur_node.left = node
                return
            # 左边没有空位,就找右边
            elif not cur_node.right:
                cur_node.right = node
                return
            # 如果都没有空位,那就把左边节点与右边节点都加到之后要判断的节点中
            queue.extend((cur_node.left, cur_node.right))

    def show(self):
        # 展示树
        if not self.root:
            return
        # 从上往下,从左往右的去遍历整棵树,然后找到第一个空位
        # 把节点添加进去
        queue = [self.root]  # 存每一层的节点
        i = 1
        while queue:
            size = len(queue)  # 当前层的元素个数
            print(f'第{i}层', end='\t')
            for _ in range(size):
                node = queue.pop(0)  # 队列中的第一个元素抛出来
                print(node.val, end=' ')  # 对当前元素进行操作
                # 节点的左孩子与右孩子添加到队列里
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            print()
            i += 1

    def preorder(self):
        # 先序遍历 前序遍历 根 左子树 右子树
        def helper(root):
            if not root:
                return
            print(root.val, end=' ')  # 输出根节点
            helper(root.left)  # 先序遍历左子树
            helper(root.right)  # 先序遍历右子树
        helper(self.root)
        print()

    def inorder(self):
        # 中序遍历  左子树 根 右子树
        def helper(root):
            if not root:
                return
            helper(root.left)  # 中序遍历左子树
            print(root.val, end=' ')  # 输出根节点
            helper(root.right)  # 中序遍历右子树
        helper(self.root)
        print()

    def postorder(self):
        # 后序遍历  左子树 右子树 根
        def helper(root):
            if not root:
                return
            helper(root.left)  # 后序遍历左子树
            helper(root.right)  # 后序遍历右子树
            print(root.val, end=' ')  # 输出根节点
        helper(self.root)
        print()

4、根据前序遍历与中序遍历确定树

问题:如何通过一棵无重复节点的树的

先序遍历0->1->3->7->8->4->9->2->5->6

与 中序遍历7->3->8->1->9->4->0->5->2->6

去构造这棵树的结构?

def buildTree(preorder, inorder):
    """
    根据前序遍历与中序遍历去构建一棵无重复节点的二叉树
    :param preorder: 前序遍历的结果, list
    :param inorder:  中序遍历的结果, list
    :return: 树的根节点
    """
    # 递归结束条件
    if not preorder:
        return None
    in_idx = inorder.index(preorder[0])  # 在中序遍历结果中找到根节点的索引
    root = Node(preorder[0])  # 构建根节点
    root.left = buildTree(preorder[1:in_idx+1], inorder[:in_idx])  # 构建左子树
    root.right = buildTree(preorder[in_idx+1:], inorder[in_idx+1:])  # 构建右子树
    return root


if __name__ == '__main__':
    tree = Tree()
    tree.root = buildTree(
        [0, 1, 3, 7, 8, 4, 9, 2, 5, 6],
        [7, 3, 8, 1, 9, 4, 0, 5, 2, 6]
    )
    tree.show()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值