吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3008|回复: 36
收起左侧

[Python 原创] 【AVL树】python实现动态可视化

  [复制链接]
Lthero 发表于 2022-11-16 22:09
本帖最后由 Lthero 于 2022-11-16 22:11 编辑

最近复习数据结构,翻出快一年前写的代码……感慨当时好nb。


源码语言:
python


可视化:
tkinter实现


功能:
动态添加数据并构建可视化AVL树
删除部分有bug,懒得修了……

PS:
此代码为课程作业,部分地方写得不好,请大家不要喷……

本代码效果:
添加1~5【可以乱序添加】
Snipaste_2022-11-16_21-59-54.png
添加6、7
Snipaste_2022-11-16_22-00-04.png
添加8、9、10等数【距离会随着数据变多而改变】
Snipaste_2022-11-16_22-00-14.png


AVL基础内容查看:https://www.52pojie.cn/thread-1422907-1-1.html
AVL可视化体验【来源于互联网】https://www.cs.usfca.edu/~galles/visualization/AVLtree.html

代码
[Python] 纯文本查看 复制代码
from tkinter import Tk, ttk, font
from tkinter import *

Width = 1200
Height = 700
col_0 = 120
row_0 = 40
row_1 = 70

class node():
    def __init__(self, value=0, left=None, right=None, pre=None):
        self.value = value
        self.left = left
        self.right = right
        self.r_height = 0
        self.l_height = 0
        self.pre = pre


class link():
    def __init__(self, value):
        self.head = node(value)
        self.length = 0

    def T_adj(self, the_node):
        p = the_node
        if p is not None:
            x = p.value
            if abs(p.l_height - p.r_height) >= 2:
                self.adj(p)
            return self.T_adj(p.pre)
        return

    def adj(self, the_node):
        p = the_node
        if p.l_height - p.r_height >= 2:
            if p.left.right is None:
                # print('直接右转 ', p.value)
                self.r_rotate(p)
            else:
                if p.left.left is not None:
                    self.r_rotate(p)
                else:
                    # print('先左转%d再右转%d' % (p.left.value, p.value))
                    self.l_rotate(p.left)
                    self.r_rotate(p)
        if p.r_height - p.l_height >= 2:
            if p.right.left is None:
                # print('直接左转', p.value)
                self.l_rotate(p)
            else:
                if p.right.right is not None:
                    self.l_rotate(p)
                else:
                    # print('先右转%d再左转%d' % (p.right.value, p.value))
                    self.r_rotate(p.right)
                    self.l_rotate(p)

    def search(self, value, the_node):
        p = the_node
        if value > p.value:
            if p.right is not None:
                x = self.search(value, p.right)
                try:
                    p.r_height = max(x.l_height, x.r_height) + 1
                    # print(p.value, p.l_height, p.r_height)
                    self.update_height2(p)
                    #if abs(p.l_height - p.r_height) >= 2:
                    self.T_adj(p)
                except:
                    pass
            else:
                temp = node(value=value, pre=p)
                p.right = temp
                p.r_height = 1
                return p
        elif value < p.value:
            if p.left is not None:
                x = self.search(value, p.left)
                try:
                    # print(x.value)
                    p.l_height = max(x.l_height, x.r_height) + 1
                    self.update_height2(p)
                    #if abs(p.l_height - p.r_height) >= 2:
                    self.T_adj(p)
                except:
                    pass
            else:
                temp = node(value=value, pre=p)
                p.left = temp
                p.l_height = 1
                return p

    def add(self, value):
        self.search(value, self.head)
        self.length += 1

    # 选用层序
    def show_all(self):
        print('===========开始==============')
        p = self.head
        # print("head.value is  ", self.head.value)
        queue = [p]
        while len(queue) != 0:
            x = queue.pop(0)
            print(x.value, x.l_height, x.r_height)
            if x.left is not None:
                queue.append(x.left)
            if x.right is not None:
                queue.append(x.right)
        print('==========结束===============')

    def find(self, key, the_node, direction=''):
        p = the_node
        if key == p.value:
            #print(p.value,direction)
            return p, direction
        elif p.value < key:
            if p.right is not None:
                return self.find(key, p.right, 'right')
        else:
            if p.left is not None:
                return self.find(key, p.left, 'left')

    def find_min(self, the_node):
        if the_node.left is not None:
            return self.find_min(the_node.left)
        else:
            return the_node

    def find_max(self, the_node):
        if the_node.right is not None:
            return self.find_max(the_node.right)
        else:
            return the_node

    def update_height2(self, the_node):
        p = the_node
        if p is not None:
            if p.left is not None and p.right is not None:
                p.l_height = max(p.left.l_height,
                                 p.left.r_height) + 1
                p.r_height = max(p.right.l_height,
                                 p.right.r_height) + 1
            elif p.left is not None and p.right is None:
                p.r_height = 0
                p.l_height = max(p.left.l_height,
                                 p.left.r_height) + 1
            elif p.left is None and p.right is not None:
                p.r_height = max(p.right.l_height,
                                 p.right.r_height) + 1
                p.l_height = 0
            else:
                p.r_height = 0
                p.l_height = 0
            #if max(p.l_height,p.r_height)==0:
            self.update_height2(p.pre)
        return p

    def update_height(self, the_node):
        if the_node.left is not None and the_node.right is not None:
            the_node.l_height = max(self.update_height(the_node.left).l_height,
                                    self.update_height(the_node.left).r_height) + 1
            the_node.r_height = max(self.update_height(the_node.right).l_height,
                                    self.update_height(the_node.right).r_height) + 1
        elif the_node.left is not None and the_node.right is None:
            the_node.r_height = 0
            the_node.l_height = max(self.update_height(the_node.left).l_height,
                                    self.update_height(the_node.left).r_height) + 1
        elif the_node.left is None and the_node.right is not None:
            the_node.r_height = max(self.update_height(the_node.right).l_height,
                                    self.update_height(the_node.right).r_height) + 1
            the_node.l_height = 0
        else:
            the_node.r_height = 0
            the_node.l_height = 0
        return the_node

    def remove(self, key):
        the_node, direction = self.find(key, self.head)
        if the_node.left is not None:
            replace_max = self.find_max(the_node.left)
        else:
            replace_max = self.find_max(the_node)
        if the_node.right is not None:
            replace_min = self.find_min(the_node.right)
        else:
            replace_min = self.find_max(the_node)
        #print(replace_max.value,replace_min.value)
        if replace_max.value == replace_min.value == the_node.value:
            if direction == 'right':
                the_node.pre.right = None
            if direction == 'left':
                the_node.pre.left = None
            self.update_height2(the_node.pre)
            the_node = None
        elif replace_min.value != the_node.value:
            temp = replace_min
            temp.pre.left = temp.right
            self.update_height2(temp.pre)
            temp.right = the_node.right
            temp.left = the_node.left
            try:
                the_node.left.pre = temp
            except:
                pass
            try:
                the_node.right.pre = temp
            except:
                pass
            if the_node.value == self.head.value:
                temp.pre = None
                self.head = temp
            else:
                temp.pre = the_node.pre
                if direction == 'right':
                    the_node.pre.right = temp
                if direction == 'left':
                    the_node.pre.left = temp
            self.update_height2(temp)
            self.adj(temp)
            the_node = None
        elif replace_max.value != the_node.value:
            temp = replace_max
            temp.pre.right = temp.left
            self.update_height2(temp.pre)
            temp.left = the_node.left
            temp.right = the_node.right
            try:
                the_node.left.pre = temp
            except:
                pass
            try:
                the_node.right.pre = temp
            except:
                pass
            if the_node.value == self.head.value:
                temp.pre = None
                self.head = temp
            else:
                temp.pre = the_node.pre
                if direction == 'right':
                    the_node.pre.right = temp
                if direction == 'left':
                    the_node.pre.left = temp
            self.update_height2(temp)
            self.adj(temp)
            the_node = None
        self.length -= 1

    def l_rotate(self, the_node):
        direction = self.find(the_node.value, self.head)[1]
        temp = the_node.right
        # 把16的左子树要转给12,当12的右子树
        the_node.right = temp.left
        # 把16的父结点给12
        temp.pre = the_node.pre
        # 把16变成12的左子结点
        temp.left = the_node
        # 如果当前结点是头结点,把头结点重新标记成12
        if the_node.value == self.head.value:
            self.head = temp
        else:
            # 当前结点不是头结点,说明上面有父结点
            # 当前结点16是他父结点的左子结点,把这个关系转给12
            if direction == 'left':
                the_node.pre.left = temp
            else:
                the_node.pre.right = temp
        # 当前结点16的父结点变成(它的原右子结点)12
        the_node.pre = temp
        # 更新两个结点的层数
        if the_node.right is not None:
            the_node.r_height = max(the_node.right.r_height, the_node.right.l_height) + 1
        else:
            the_node.r_height = 0
        self.update_height2(temp)

    def r_rotate(self, the_node):
        direction = self.find(the_node.value, self.head)[1]
        temp = the_node.left
        the_node.left = temp.right
        temp.pre = the_node.pre
        temp.right = the_node
        if the_node.value == self.head.value:
            self.head = temp
        else:
            if direction == 'left':
                the_node.pre.left = temp
            else:
                the_node.pre.right = temp
        the_node.pre = temp
        if the_node.left is not None:
            the_node.l_height = max(the_node.left.l_height, the_node.left.r_height) + 1
        else:
            the_node.l_height = 0
        self.update_height2(temp)
    def clear_all(self):
        self.head=None
        self.length-=self.length

    def get_len(self):
        return self.length


class Window():
    def __init__(self):
        self.one_link = link(0)
        self.root = Tk()
        self.root.title('可视化AVL树-作者:lthero')
        frame = Frame(self.root, width=Width-20, height=Height-20)
        frame.grid(row=0, column=0)
        self.canvas = Canvas(frame, bg='#FFFFFF', width=Width-20, height=Height-20, scrollregion=(-800, 0, Width+800, Height+800))  #
        hbar = Scrollbar(frame, orient=HORIZONTAL)
        hbar.pack(side=BOTTOM, fill=X)
        hbar.config(command=self.canvas.xview)
        vbar = Scrollbar(frame, orient=VERTICAL)
        vbar.pack(side=RIGHT, fill=Y)
        vbar.config(command=self.canvas.yview)
        self.canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
        self.canvas.pack(side=LEFT, expand=True, fill=BOTH)

        self.input_label = ttk.Entry(self.root, font=font.Font(size=13, weight='bold'), width=10)
        self.input_label.place(x=10, y=row_0-30)
        self.input_label.focus_set()
        self.B_in = ttk.Button(self.root, text='add it', command=self.get_int, width=8)
        self.B_in.place(x=col_0, y=row_0-30)
        self.B_out = ttk.Button(self.root, text='remove it', command=self.delete, width=8)
        self.B_out.place(x=col_0, y=row_1-30)
        self.B_out = ttk.Button(self.root, text='restart', command=self.restart, width=8)
        self.B_out.place(x=col_0-100, y=row_1 - 30)
        self.root.bind('<Return>', self.get_int)

        self.is_first = 0
        screenwidth = self.root.winfo_screenwidth()
        screenheight = self.root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (Width, Height, (screenwidth - Width) / 2, (screenheight - Height) / 2)
        self.root.geometry(alignstr)
        def about():
            win2 = Toplevel(self.root)
            win2.title('关于')
            width = 280
            height = 200
            screenwidth = self.root.winfo_screenwidth()
            screenheight = self.root.winfo_screenheight()
            alignstr2 = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
            win2.geometry(alignstr2)
            win2.resizable(width=False, height=False)
            win2.focus_set()
            Help = Label(win2, text='可视化AVL树\n\n功能:可以添加,但删除有bug\n\n 完成于2021年4月21日,作者:lthero', font=('微软雅黑', 10))
            Help.place(x=40, y=40)
        menu = Menu(self.root, tearoff=False)
        Cmenu = Menu(menu, tearoff=False)  # 新建菜单,删除顶部虚线
        Cmenu.add_command(label='关于', command=about)
        menu.add_cascade(label='点我', menu=Cmenu)  # 实例化菜单
        self.root.config(menu=menu)  # 实例化菜单
        self.root.mainloop()

    def restart(self):
        self.one_link.clear_all()
        self.is_first=0
        self.draw_all()
        self.input_label.focus_set()

    def get_int(self,*args):
        x = self.input_label.get()
        if self.is_first == 0:
            if x != '':
                self.add_one(int(x), 1)
                self.is_first = 1
        else:
            if x != '':
                self.add_one(int(x))
        self.input_label.delete(0,END)
        self.input_label.focus_set()

    def add_one(self, value, flag=0):
        if flag == 1:
            self.one_link.head=node(value)
        else:
            self.one_link.add(value)
        self.show()
        self.draw_all()

    def delete(self):
        x = self.input_label.get()
        self.one_link.remove(int(x))
        self.input_label.delete(0,END)
        self.input_label.focus_set()
        self.show()
        self.draw_all()

    def show(self):
        self.one_link.show_all()

    def draw_one(self,the_node,x,y):
        p=the_node
        if p is not None:
            if p.left is not None:
                temp=max(p.left.r_height, p.left.l_height)
                pian=30
                pian_y=1
                if temp>=2:
                    pian=60
                if temp>=3:
                    pian=80
                    pian_y+=2
                self.canvas.create_line(x, y, x - 30 - temp * pian, y + 35*pian_y)
                self.draw_one(p.left, x - 30 - temp * pian, y + 35*pian_y)
            if p.right is not None:
                temp=max(p.right.l_height, p.right.r_height)
                pian=30
                pian_y = 1
                if temp>=2:
                    pian=60
                if temp>=3:
                    pian=80
                    pian_y += 2
                self.canvas.create_line(x, y, x +40 + temp * pian, y + 35*pian_y)
                self.draw_one(p.right, x + 40 + temp * pian, y + 35*pian_y)
            self.canvas.create_oval(x - 15, y - 15, x + 15, y + 15, fill='yellow')
            self.canvas.create_text(x, y, text=the_node.value)
        else:
            return

    def draw_all(self):
        x = 450
        y = 50
        if self.one_link.head is not None:
            if max(self.one_link.head.r_height,self.one_link.head.l_height)>=3:
                x+=150
        self.canvas.create_rectangle(-800, 0,  Width+800,Height+800, fill='white',width=0)
        self.draw_one(self.one_link.head,x,y)


x = Window()

免费评分

参与人数 15威望 +1 吾爱币 +22 热心值 +13 收起 理由
meilidemm + 1 我很赞同!
hayyot + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lovexzy + 1 我很赞同!
苏紫方璇 + 1 + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
HAHAYAYA + 1 + 1 谢谢@Thanks!
坡婆子 + 1 + 1 我很赞同!
lcg2014 + 1 + 1 你离大佬不远了
yyb414 + 1 + 1 热心回复!
hxw0204 + 1 + 1 热心回复!
Bob5230 + 1 谢谢@Thanks!
Sisypheee + 1 + 1 谢谢@Thanks!
耿梦莹123 + 1 + 1 我很赞同!
QQ12349qq + 1 + 1 谢谢@Thanks!
axin0529 + 1 + 1 热心回复!
Magicy + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| Lthero 发表于 2022-11-22 22:57
hayyot 发表于 2022-11-22 09:24
大佬大佬!!!这就是算法工程师嘛!!!

在下只是个学生,这是数据结构课的作业啦
 楼主| Lthero 发表于 2022-11-24 10:38
tyjjk 发表于 2022-11-23 09:33
记得大学期间data structure是核心课程

是的,数据结构一直挺重要的……不过我学过就忘
天真Aro 发表于 2022-11-16 22:44
XJCQ2014 发表于 2022-11-16 23:12
这个厉害,必须学习!
ztscope 发表于 2022-11-16 23:29
这个厉害了
QQ12349qq 发表于 2022-11-16 23:45
这个厉害,必须学习!
lhp462 发表于 2022-11-17 00:48
数据结构可视化,之前本科的时候写过类似地,谢谢楼主分享
yyb414 发表于 2022-11-17 08:24
感谢分享
chujian2021 发表于 2022-11-17 08:26
这个很好,必须支持学习
bj9ye666 发表于 2022-11-17 08:28
来学习来打卡,早日成为python大神
Gijia 发表于 2022-11-17 08:36
非常不错的分享,受教中... 感谢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 02:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表