数据结构:队列、栈、链表、图、并查集、二叉树、字典树、堆(python实现)

队列

数组实现

class Queue(object):
    """数组实现"""

    def __init__(self):
        self.entries = []  # 表示队列内的参数
        self.length = 0  # 表示队列的长度
        self.front = 0  # 表示队列头部位置

    def enqueue(self, item):
        self.entries.append(item)  # 添加元素到队列里面
        self.length = self.length + 1  # 队列长度增加 1

    def dequeue(self):
        self.length = self.length - 1  # 队列的长度减少 1
        dequeued = self.entries[self.front]  # 队首元素为 dequeued
        self.front += 1  # 队首的位置减少 1
        self.entries = self.entries[self.front:]  # 队列的元素更新为退队之后的队列
        return dequeued

    def peek(self):
        return self.entries[0]  # 直接返回队列的队首元素

链表实现

class Queue(object):
    def __init__(self):
        self.head = None  # 头部链点为 None
        self.rear = None  # 尾部链点为 None

    def is_empty(self):
        """判断队列是否为空"""
        return self.head is None  # 判断队列是否为空

    def enqueue(self, elem):
        """往队列中添加一个 elem 元素"""
        p = Node(elem)  # 初始化一个新的点
        if self.is_empty():
            self.head = p  # 队列头部为新的链点
            self.rear = p  # 队列尾部为新的链点
        else:
            self.rear.next = p  # 队列尾部的后继是这个新的点
            self.rear = p  # 然后让队列尾部指针指向这个新的点

    def dequeue(self):
        """从队列头部删除一个元素"""
        if self.is_empty():  # 判断队列是否为空
            print('Queue_is_empty')  # 若队列为空,则退出 dequeue 操作
        else:
            result = self.head.elem  # result为队列头部元素
            self.head = self.head.next  # 改变队列头部指针位置
            return result  # 返回队列头部元素

    def peek(self):
        """查看队列头部的元素"""
        if self.is_empty():  # 判断队列是否为空
            print('NOT_FOUND')  # 为空则返回 NOT_FOUND
        else:
            return self.head.elem  # 返回队列头部元素

    def print_queue(self):
        """展现队列的元素"""
        print("queue:")
        temp = self.head
        myqueue = []  # 暂时存放队列数据
        while temp is not None:
            myqueue.append(temp.elem)
            temp = temp.next
        print(myqueue)

class Stack(object):
    def __init__(self, limit=10):
        self.stack = []  # 存放元素
        self.limit = limit  # 栈容量极限

    def push(self, data):  # 进栈
        # 判断栈容量是否超出范围
        if len(self.stack) >= self.limit:
            raise IndexError('超出栈容量极限')
        self.stack.append(data)

    def pop(self):  # 退栈
        if self.stack:
            return self.stack.pop()
        else:
            # 空栈不能弹出元素
            raise IndexError('pop from an empty stack')

    def peek(self):  # 查看栈顶元素
        if self.stack:
            return self.stack[-1]

    def is_empty(self):  # 判断栈是否为空
        return not bool(self.stack)

    def size(self):  # 返回栈的大小
        return len(self.stack)

链表

双向链表

class Node(object):
    # 双向链表节点
    def __init__(self, item):
        self.item = item
        self.next = None
        self.prev = None


class DLinkList(object):
    # 双向链表
    def __init__(self):
        self._head = None

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

    def get_length(self):
        # 返回链表的长度
        cur = self._head
        count = 0
        while cur != None:
            count = count + 1
            cur = cur.next
        return count

    def travel(self):
        # 遍历链表
        cur = self._head
        while cur != None:
            print(cur.item)
            cur = cur.next
        print("")

    def add(self, item):
        # 头部插入元素
        node = Node(item)
        if self.is_empty():
            # 如果是空链表,将 node 赋值给 _head
            self._head = node
        else:
            # 将 node 的 next 属性指向头节点 _head
            node.next = self._head
            # 将头节点 _head 的 prev 属性指向 node
            self._head.prev = node
            # 将 node 赋值给 _head
            self._head = node

    def append(self, item):
        # 尾部插入元素
        node = Node(item)
        if self.is_empty():
            # 如果是空链表,将 node 赋值给 _head
            self._head = node
        else:
            # 循环移动到链表尾部结点的位置
            cur = self._head
            while cur.next != None:
                cur = cur.next
            # 将尾结点 cur 的 next 属性指向 node
            cur.next = node
            # 将 node 的 prev 属性指向 cur
            node.prev = cur

    def search(self, item):
        # 查找元素是否存在
        cur = self._head
        while cur != None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

    def insert(self, pos, item):
        # 在指定位置添加节点
        if pos <= 0:
            self.add(item)
        elif pos > (self.length() - 1):
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            count = 0
            # 移动到指定位置的前一个位置
            while count < (pos - 1):
                count += 1
                cur = cur.next
            # 将 node 的 prev 属性指向 cur
            node.prev = cur
            # 将 node 的 next 属性指向 cur 的下一个节点
            node.next = cur.next
            # 将 cur 的下一个节点的 prev 属性指向 node
            cur.next.prev = node
            # 将 cur 的 next 指向 node
            cur.next = node

    def remove(self, item):
        # 删除元素
        if self.is_empty():
            return
        else:
            cur = self._head
            if cur.item == item:
                # 如果首节点的元素即是要删除的元素
                if cur.next == None:
                    # 如果链表只有这一个节点
                    self._head = None
                else:
                    # 将第二个节点的 prev 属性设置为 None
                    cur.next.prev = None
                    # 将 _head 指向第二个节点
                    self._head = cur.next
                return
            while cur != None:
                if cur.item == item:
                    # 将 cur 的前一个节点的 next 指向 cur 的后一个节点
                    cur.prev.next = cur.next
                    # 将 cur 的后一个节点的 prev 指向 cur 的前一个节点
                    cur.next.prev = cur.prev
                    break
                cur = cur.next

单链表

链表属于常见的一种线性结构,对于插入和移除而言,时间复杂度都为 O(1)。

但是对于搜索操作而言,不管从链表的头部还是尾部,都需要遍历 O(n),所以最好复杂度为 O(1),最坏的情况就是从头部遍历到尾部才搜索出对应的元素,所以最坏复杂度为 O(n),平均复杂度为 O(n)。

class Node(object):
    def __init__(self, data):
        self.data = data
        self.next = None


class LinkedList(object):
    def __init__(self, head=None):
        self.head = head

    def append(self, new_element):
        """
        在链表后面增加一个元素
        """
        current = self.head
        if self.head:
            while current.next:
                current = current.next
            current.next = new_element
        else:
            self.head = new_element

    def is_empty(self):
        """
        判断链表是否为空
        """
        return not self.head

    def get_length(self):
        """
        获取链表的长度
        """
        # 临时变量指向队列头部
        temp = self.head
        # 计算链表的长度变量
        length = 0
        while temp != None:
            length = length + 1
            temp = temp.next
        # 返回链表的长度
        return length

    def insert(self, position, new_element):
        """
        在链表中指定索引处插入元素
        """
        if position < 0 or position > self.get_length():
            raise IndexError('insert 插入时,key 的值超出了范围')
        temp = self.head
        if position == 0:
            # new_element.next = temp
            # self.head = new_element
            new_element.next, self.head = temp, new_element
            return
        i = 0
        # 遍历找到索引值为 position 的结点后, 在其后面插入结点
        while i < position:
            # pre = temp
            # temp = temp.next
            pre, temp = temp, temp.next
            i += 1
        # pre.next = new_element
        # node.next = temp
        pre.next, new_element.next = new_element, temp

    def print_list(self):
        """
        遍历链表,并将元素依次打印出来
        """
        print("linked_list:")
        temp = self.head
        new_list = []
        while temp is not None:
            new_list.append(temp.data)
            temp = temp.next
        print(new_list)

    def remove(self, position):
        """
        删除指定索引的链表元素
        """
        if position < 0 or position > self.get_length() - 1:
            # print("insert error")
            raise IndexError('删除元素的位置超出范围')

        i = 0
        temp = self.head
        # 遍历找到索引值为 position 的结点
        while temp != None:
            if position == 0:
                self.head = temp.next
                temp.next = None
                return True
            # pre = temp
            # temp = temp.next
            pre, temp = temp, temp.next

            i += 1
            if i == position:
                # pre.next = temp.next
                # temp.next = None
                pre.next, temp.next = temp.next, None
                return

    def reverse(self):
        """
        将链表反转:
        参考:https://blog.csdn.net/gongliming_/article/details/88712221
        """
        prev = None
        current = self.head
        while current:
            # next_node = current.next
            # current.next = prev
            # prev = current
            # current = next_node
            next_node, current.next = current.next, prev
            prev, current = current, next_node
        self.head = prev

    def init_list(self, data_list):
        """
        将列表转换为链表
        """
        # 创建头结点
        self.head = Node(data_list[0])
        temp = self.head
        # 逐个为 data 内的数据创建结点, 建立链表
        for i in data_list[1:]:
            node = Node(i)
            temp.next = node
            temp = temp.next

邻接表实现

邻接表中有两种结点,一种是头结点,另一种是表结点,头结点中存储一个顶点的数据和指向链表中第一个结点,表结点中存储当前顶点在图中的位置和指向下一条边或弧的结点,表头结点用链式或顺序结构方式存储

class Graph(object):
    def __init__(self):
        self.nodes = []  # 表示图的点集
        self.edge = {}  # 表示图的边集

    def insert(self, a, b):
        # 如果 a 不在图的点集里,则添加 a
        if not (a in self.nodes):
            self.nodes.append(a)
            self.edge[a] = list()
        # 如果 b 不在图的点集里,则添加 b
        if not (b in self.nodes):
            self.nodes.append(b)
            self.edge[b] = list()
        # a 连接 b
        self.edge[a].append(b)
        # b 连接 a
        self.edge[b].append(a)

    def succ(self, a):
        # 返回与 a 连接的点
        return self.edge[a]

    def show_nodes(self):
        # 返回图的点集
        return self.nodes

    def show_edge(self):
        print(self.edge)


graph = Graph()
graph.insert('0', '1')
graph.insert('0', '2')
graph.insert('0', '3')
graph.insert('1', '3')
graph.insert('2', '3')
graph.show_edge()
print(graph.show_nodes())
print(graph.succ('3'))

邻接矩阵实现

邻接矩阵表示法,用两个数组表示,一个一维数组和一个二维数组。
一维数组存储节点信息,二维数组存储节点之间的关系

class Graph(object):
    def __init__(self, vertex):
        self.vertex = vertex
        self.graph = [[0] * vertex for i in range(vertex)]

    def insert(self, u, v):
        # 对存在连接关系的两个点,在矩阵里置1代表存在连接关系,没有连接关系则置0
        self.graph[u - 1][v - 1] = 1
        self.graph[v - 1][u - 1] = 1

    def show(self):  # 展示图
        for i in self.graph:
            for j in i:
                print(j, end=' ')
            print(' ')


graph = Graph(5)
graph.insert(1, 4)
graph.insert(4, 2)
graph.insert(4, 5)
graph.insert(2, 5)
graph.insert(5, 3)
graph.show()

深度优先搜索

深度优先搜索(DFS) 是树的先根遍历的推广。
基本思想:
从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止。

def dfs(G, s, S=None, res=None):
    if S is None:
        # 储存已经访问节点
        S = set()
    if res is None:
        # 存储遍历顺序
        res = []
    res.append(s)
    S.add(s)
    for u in G[s]:
        if u in S:
            continue
        S.add(u)
        dfs(G, u, S, res)

    return res


G = {'0': ['1', '2'],
     '1': ['2', '3'],
     '2': ['3', '5'],
     '3': ['4'],
     '4': [],
     '5': []}

print(dfs(G, '0'))

广度优先搜索

广度优先搜索(BFS)是树的按层次遍历的推广。
基本思想:
首先访问初始点 vi,并将其标记为已访问过,接着访问 vi 的所有未被访问过的邻接点 vi1,vi2,…, vin,并均标记已访问过,
然后再按照 vi1,vi2,…, vin 的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依次类推,直到图中所有和初始点 vi 有路径相通的顶点都被访问过为止。

def bfs(graph, start):
    # explored:已经遍历的节点列表,queue:寻找待遍历的节点队列
    explored, queue = [], [start]
    explored.append(start)
    while queue:
        # v:将要遍历的某节点
        v = queue.pop(0)
        # w:节点 v 的邻居
        for w in graph[v]:
            # w:如果 w 未被遍历,则遍历
            if w not in explored:
                # 添加 w 节点到已遍历的节点列表
                explored.append(w)
                # 添加 w 节点到寻找待遍历的节点队列
                queue.append(w)
    return explored


G = {'0': ['1', '2'],
     '1': ['2', '3'],
     '2': ['3', '5'],
     '3': ['4'],
     '4': [],
     '5': []}

print(bfs(G, '0'))

最短路径

Dijkstra

Dijkstra 算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。

该算法常用于路由算法或者作为其他图算法的一个子模块。

Dijkstra 的算法是一个贪婪算法,时间复杂度是 O(VLogV)(使用最小堆)。

但是迪杰斯特拉算法在有负权值边的图中不适用,Bellman-Ford 适合这样的图。在网络路由中,该算法会被用作距离向量路由算法。

import heapq


# hrapq默认产生最小堆,heapq.heappop()取出堆的根节点,即最小值
def dijkstra(graph, start, end):
    heap = [(0, start)]  # heap为未标识节点组,从开始节点到目标节点的成本cost(最短路径),start为开始节点,初始成本为0
    visited = []  # 已经访问过的节点列表
    while heap:
        (cost, u) = heapq.heappop(heap)
        if u in visited:
            continue
        visited.append(u)
        if u == end:
            return cost
        for v, c in graph[u]:  # v是节点,c是节点权值
            if v in visited:
                continue
            next = cost + c
            heapq.heappush(heap, (next, v))  # (next, v)这里把next放在前面,是为了以next(成本)为最小堆的排序依据
    return (-1, -1)


G = {'0': [['1', 2], ['2', 5]],
     '1': [['0', 2], ['3', 3], ['4', 1]],
     '2': [['0', 5], ['5', 3]],
     '3': [['1', 3]],
     '4': [['1', 1], ['5', 3]],
     '5': [['2', 3], ['4', 3]]}
shortDistance = dijkstra(G, '4', '2')
print(shortDistance)

Bellman-Ford

Bellman-Ford 也比迪杰斯特拉算法更简单和同时也适用于分布式系统。

但 Bellman-Ford 的时间复杂度是 O(VE),这要比迪杰斯特拉算法慢。(V 为顶点的个数,E 为边的个数)

含负权边的带权有向图的单源最短路问题。(不能处理带负权边的无向图)
算法原理讲解

并查集

class union_find(object):
    def __init__(self, data_list):
        # 保存节点的父节点
        self.father_dict = {}
        # 保存父节点的大小
        self.size_dict = {}
        for node in data_list:
            self.father_dict[node] = node  # 将节点的父节点设为自身
            self.size_dict[node] = 1  # size 设为 1

    def find(self, node):
        # 使用递归的方式来查找父节点
        father = self.father_dict[node]
        # 递归查找父节点
        if (node != father):
            father = self.find(father)
        # 在查找父节点的时候,顺便把当前节点移动到父节点上面这个操作算是一个优化
        self.father_dict[node] = father
        return father

    def is_same_set(self, node_a, node_b):
        # 查看两个节点是不是在一个集合里面。通过调用 find 函数,判断两个节点是否是同一个父节点,如果是则认为两个节点属于一个集合
        return self.find(node_a) == self.find(node_b)

    def union(self, node_a, node_b):
        # 将两个集合合并在一起
        # 对合并的两个节点做初步判断,判断是否为空
        if node_a is None or node_b is None:
            return
        # 分别查找两个节点的父节点
        a_head = self.find(node_a)
        b_head = self.find(node_b)
        # 当两个节点的父节点不一样时,才能做合并操作
        if (a_head != b_head):
            a_set_size = self.size_dict[a_head]
            b_set_size = self.size_dict[b_head]
            # 根据集合的大小做判断,尽量使小集合并到大集合
            if (a_set_size >= b_set_size):
                self.father_dict[b_head] = a_head
                self.size_dict[a_head] = a_set_size + b_set_size
            else:
                self.father_dict[a_head] = b_head
                self.size_dict[b_head] = a_set_size + b_set_size


if __name__ == '__main__':
    a = [1, 2, 3, 4, 5]
    union_find = union_find(a)
    union_find.union(1, 2)
    union_find.union(3, 5)
    union_find.union(3, 1)
    print(union_find.is_same_set(2, 5))  # (2,5的父节点都是3)

二叉树

先序遍历

    def preorder(self, node):  # 先序遍历
        if node is None:
            return []
        result = [node.item]
        left_item = self.preorder(node.left)
        right_item = self.preorder(node.right)
        return result + left_item + right_item

中序遍历

    def inorder(self, node):  # 中序遍历 运行时间为 O(n)
        if node is None:
            return []
        result = [node.item]
        left_item = self.inorder(node.left)
        right_item = self.inorder(node.right)
        return left_item + result + right_item

后序遍历

 def postorder(self, node):  # 后序遍历 运行时间为 O(n)
        if node is None:
            return []
        result = [node.item]
        left_item = self.postorder(node.left)
        right_item = self.postorder(node.right)
        return left_item + right_item + result

添加、删除节点、查找父节点

class Node(object):
    def __init__(self, item):
        self.item = item  # 表示对应的元素
        self.left = None  # 表示左子节点
        self.right = None  # 表示右子节点

    def __str__(self):
        # print 一个 Node 类时会打印 __str__ 的返回值
        return str(self.item)


class Tree(object):
    def __init__(self):
        # 根节点定义为 root 永不删除,作为哨兵使用。
        self.root = Node('root')

    def add(self, item):
        """
        添加子节点到树里面
        这里add实现的只是普通的二叉树(不具有任何大小关系),并不是二叉排序数
        """
        node = Node(item)
        # 如果二叉树为空,那么添加的点将插入 root 节点处
        if self.root is None:
            self.root = node
        else:
            # 在 q 列表中,添加二叉树的根节点
            q = [self.root]
            while True:
                pop_node = q.pop(0)
                # 左子树为空则将点添加到左子树
                if pop_node.left is None:
                    pop_node.left = node
                    return
                # 右子树为空则将点添加到右子树
                elif pop_node.right is None:
                    pop_node.right = node
                    return
                else:
                    q.append(pop_node.left)
                    q.append(pop_node.right)

    def get_parent(self, item):
        """找到 item 的父节点"""
        if self.root.item == item:
            # 根节点没有父节点
            return None
        # 在 tmp 列表中,添加二叉树的根节点
        tmp = [self.root]
        while tmp:
            pop_node = tmp.pop(0)
            # 如果点的左子树为要寻找的点
            if pop_node.left and pop_node.left.item == item:
                # 返回这个点,即为寻找点的父节点
                return pop_node
            # 如果点的右子树为要寻找的点
            if pop_node.right and pop_node.right.item == item:
                # 返回这个点,即为寻找点的父节点
                return pop_node
            # 添加 tmp 列表里的元素
            if pop_node.left is not None:
                tmp.append(pop_node.left)
            if pop_node.right is not None:
                tmp.append(pop_node.right)
        return None

    def delete(self, item):
        """
        从二叉树中删除一个子节点
        思路:
        先获取待删除节点 item 的父节点(以下简称 item)。
            如果父节点不为空,判断 item 的左右子树是否存在:
                当左子树为空时,进一步判断 item 是父节点的左孩子,还是右孩子;
                    如果是左孩子,将父节点的左指针指向 item 的右子树,反之将父节点的右指针指向 item 的右子树。
                当右子树为空时,进一步判断 item 是父节点的左孩子,还是右孩子;
                    如果是左孩子,将父节点的左指针指向 item 的左子树,反之将父节点的右指针指向 item 的左子树。
                当左右子树均不为空时,寻找右子树中的最左叶子节点 x,将 x 替代要删除的节点。
            删除成功,返回 True。
            删除失败, 返回 False。
        """
        # 如果根为空,就什么也不做
        if self.root is None:
            return False

        parent = self.get_parent(item)
        if parent:
            # 确定待删除节点
            # 类似if-then语句  a=1 if a>0 else 0   如果a>0,a赋值1,否则赋值0
            del_node = parent.left if parent.left.item == item else parent.right
            # 待删除节点的左子树为空时
            if del_node.left is None:
                # 如果待删除节点是父节点的左孩子
                if parent.left.item == item:
                    parent.left = del_node.right
                # 如果待删除节点是父节点的右孩子
                else:
                    parent.right = del_node.right
                # 删除变量 del_node
                del del_node
                return True
            # 待删除节点的右子树为空时
            elif del_node.right is None:
                # 如果待删除节点是父节点的左孩子
                if parent.left.item == item:
                    parent.left = del_node.left
                # 如果待删除节点是父节点的右孩子
                else:
                    parent.right = del_node.left
                # 删除变量 del_node
                del del_node
                return True
            else:  # 左右子树都不为空
                tmp_pre = del_node
                # 待删除节点的右子树
                tmp_next = del_node.right

                # 寻找待删除节点右子树中的最左叶子节点并完成替代
                if tmp_next.left is None:
                    # 替代(因为tmp_pre = del_node,此处理解为同一个节点)
                    tmp_pre.right = tmp_next.right
                    tmp_next.left = del_node.left
                    tmp_next.right = del_node.right
                else:
                    # 让 tmp_next 指向右子树的最左叶子节点,tmp_pre指向右子树的最左叶子节点的父节点
                    while tmp_next.left:
                        tmp_pre = tmp_next
                        tmp_next = tmp_next.left
                    # 替代
                    tmp_pre.left = tmp_next.right
                    tmp_next.left = del_node.left
                    tmp_next.right = del_node.right
                # 如果待删除节点是父节点的左孩子
                if parent.left.item == item:
                    parent.left = tmp_next
                # 如果待删除节点是父节点的右孩子
                else:
                    parent.right = tmp_next
                del del_node
                return True
        else:
            return False

    def inorder(self, node):  # 中序遍历 运行时间为 O(n)
        if node is None:
            return []
        result = [node.item]
        left_item = self.inorder(node.left)
        right_item = self.inorder(node.right)
        return left_item + result + right_item

    def postorder(self, node):  # 后序遍历 运行时间为 O(n)
        if node is None:
            return []
        result = [node.item]
        left_item = self.postorder(node.left)
        right_item = self.postorder(node.right)
        return left_item + right_item + result

    def preorder(self, node):  # 先序遍历
        if node is None:
            return []
        result = [node.item]
        left_item = self.preorder(node.left)
        right_item = self.preorder(node.right)
        return result + left_item + right_item


if __name__ == '__main__':
    t = Tree()
    for i in range(1, 11):
        t.add(i)
    print('中序遍历:', t.inorder(t.root))
    print('先序遍历:', t.preorder(t.root))
    print('后序遍历:', t.postorder(t.root))

字典树

class TrieNode:
    def __init__(self):
        """构建内置字典结构"""
        self.nodes = dict()  # 构建字典
        self.is_leaf = False  # bool值代表当前节点是否是结束的位置,既是否是一个完整单词

    def insert(self, word: str):
        """插入一个字到字典树中"""
        curr = self
        for char in word:
            if char not in curr.nodes:
                curr.nodes[char] = TrieNode()
            curr = curr.nodes[char]  # 这是当前字符创建的对象
        curr.is_leaf = True

    def insert_many(self, words: [str]):
        """插入一列表的字到字典树中"""
        for word in words:
            self.insert(word)

    def search(self, word: str):
        """在字典树里面查询一个字"""
        curr = self
        for char in word:
            if char not in curr.nodes:
                return False
            curr = curr.nodes[char]
        print(curr.is_leaf)
        return

    def all(self):
        print(self.nodes.keys())
        for node in self.nodes.values():
            print(node.nodes.keys())


if __name__ == '__main__':
    x = TrieNode()
    x.insert("word")
    x.insert("wohello")
    """
        构造结果:
        h->e->l->l->o(True)
              |
              |->y(true)
    """
    x.insert("hello")
    x.insert("hely")

    x.search("hello")
    x.search("word")
    x.search("hel")
    x.search("wo")
    x.all()

堆 (heap) 是一种经过排序的完全二叉树,其中任一非叶子节点的值均不大于(或不小于)其左孩子和右孩子节点的值。

最大堆 根结点的键值是所有堆结点键值中最大者

最小堆 根结点的键值是所有堆结点键值中最小者

在最大堆中,父节点的值比每一个子节点的值都要大。在最小堆中,父节点的值比每一个子节点的值都要小。这就是所谓的“堆属性”,并且这个属性对堆中的每一个节点都成立。

堆的操作详解

堆结论:根据当前节点i
找父节点 (i-1)//2
找左节点 i2+1
找右节点 i
2+2

class heap(object):
    def __init__(self):
        # 初始化一个空堆,使用数组来在存放堆元素,节省存储
        self.data_list = []

    def get_parent_index(self, index):
        if index == 0 or index > len(self.data_list) - 1:
            return None
        # 返回父节点的下标
        else:
            # >> 是右移动运算符
            # 将 >> 左边的运算数二进位全部移动
            # 例:2 >> 1 的结果为 1
            # 该操作本质为 0000 0010 运算后为 0000 0001
            return (index - 1) >> 1  # 可算出当前节点父节点的下标  等价于 (index -1) // 2

    def swap(self, index_a, index_b):
        # 交换数组中的两个元素
        self.data_list[index_a], self.data_list[index_b] = self.data_list[index_b], self.data_list[index_a]

    def insert(self, data):
        # 先把元素放在最后,然后从后往前依次堆化
        # 这里以大顶堆为例,如果插入元素比父节点大,则交换,直到最后
        self.data_list.append(data)
        index = len(self.data_list) - 1  # 当前插入的节点下标
        parent = self.get_parent_index(index)  # 当前插入节点的父节点下标
        # 循环,直到该元素成为堆顶,或小于父节点(对于大顶堆)
        while parent is not None and self.data_list[parent] < self.data_list[index]:
            # 交换操作
            self.swap(parent, index)
            index = parent  # 把父节点作为当前节点
            parent = self.get_parent_index(parent)

    def removeMax(self):
        # 删除堆顶元素,然后将最后一个元素放在堆顶,再从上往下依次堆化
        remove_data = self.data_list[0]
        self.data_list[0] = self.data_list[-1]
        del self.data_list[-1]

        # 堆化
        self.heapify(0)
        return remove_data

    def heapify(self, index):
        # 从上往下堆化,从index 开始堆化操作 (大顶堆)
        total_index = len(self.data_list) - 1
        while True:
            maxvalue_index = index
            # index左右子节点索引为n*2+1和n*2+2,查询子节点数据, 与最大交换, 之后继续进行交换 直到叶子节点都小于当年节点
            if 2 * index + 1 <= total_index and self.data_list[2 * index + 1] > self.data_list[maxvalue_index]:
                maxvalue_index = 2 * index + 1
            if 2 * index + 2 <= total_index and self.data_list[2 * index + 2] > self.data_list[maxvalue_index]:
                maxvalue_index = 2 * index + 2
            # 若在子节点中没有找到比父节点大的,则maxvalue_index依然等于原来的index
            if maxvalue_index == index:
                break
            self.swap(index, maxvalue_index)  # maxvalue_index当前小树中最大的节点
            index = maxvalue_index


if __name__ == "__main__":
    myheap = heap()
    for i in range(10):
        myheap.insert(i + 1)
    print('建堆:', myheap.data_list)
    print("删除堆顶元素:", [myheap.removeMax() for _ in range(10)])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值