Python数据结构与算法(学习笔记_Day2)

2.双向链表的介绍

双向链表

既可以从头遍历到尾, 又可以从尾遍历到头

链表相连的过程是双向的. 

一个节点既有向前连接的引用, 也有一个向后连接的引用.

双向连接的图解:

3.双向链表的创建

#创建节点类
class Node:
    def __init__(self,el):
        self.prev=None
        self.next=None
        self.el=el

#创建双向链表
class double_link_list:
#定义属性
    def __init__(self):
        self.__head=None
        self.__tail=None
        self.length=0
  • 代码解析:

    • 基本思路和单向链表比较相似, 都是创建节点结构函数以及定义一些属性和方法.

    • 只是Node中添加了一个self.prev属性, 该属性用于指向上一个节点.

    • 另外属性中添加了一个self.tail属性, 该属性指向末尾的节点

 3.1 尾部追加数据

    def append(self,el):
        #把传入的数据封装为创建的新节点
        node = Node(el)
        #判断链表是不是空的
        # if self.length==0:
        if self.is_empty():
            # 是空的 把head和tail指向新节点
            self.__head = node
            self.__tail = node
        else:
            #不是空的 把tail指向的节点的next属性指向新节点 新节点的pre属性指向tail指向的节点 tail指向新节点
            self.__tail.next=node
            node.prev=self.__tail
            self.__tail=node
        self.length+=1

3.2 任意位置插入

    def insert(self,position,el):
        #判断越界问题
        if position<0 or position>self.length:
            return False
        else:
            #把传入的数据封装为一个节点
            node=Node(el)
            #插入到头部
            if position==0:
                self.__head.prev=node
                node.next=self.__head
                self.__head=node
            #插入到尾部
            elif position==self.length:
                self.__tail.next=node
                node.prev=self.__tail
                self.__tail=node
            #插入到中间
            else:
                current=self.__head
                previous=None
                index=0
                while(index<position):
                    previous=current
                    current=current.next
                    index+=1
                previous.next=node
                node.prev=previous
                current.prev=node
                node.next=current
            self.length+=1
            return True

 3.3 通过位置移除数据

    def removeAt(self,position):
        #判断是否越界
        if position<0 or position>=self.length:
            return False
        #不越界
        else:
            # 移除中间的
            current = self.__head
            previous = None
            index = 0
            if self.length==1:
                self.__head = None
                self.__tail = None
            #移除第一个
            if position==0:
                current=self.__head
                self.__head=self.__head.next
                self.__head.prev = None
            #移除最后一个
            elif position==self.length-1:
                current=self.__tail
                self.__tail=self.__tail.prev
                self.__tail.next=None
            else:
                while(index<position):
                    previous=current
                    current=current.next
                    index+=1
                # current
                previous.next=current.next
                current.next.prev=previous
            self.length-=1
            return current.el

3.4 获取位置元素

# 根据元素获取再链表中第一个出现的位置, 没有返回 - 1
    def indexOf(self,element):
        #1.定义变量保存信息
        current = self.head
        index = 0
        #2.查找正确的信息
        while current:
            if current.element == element:
                return index
            index+=1
            current = current.next
        #3.来到这个位置, 说明没有找到, 则返回-1
        return -1

3.5 根据元素删除

    def remove(self,el):
        index=self.indexOf(el)
        return self.removeAt(index)

 3.6 其他方法

  # 判断是否为空
    def isEmpty(self):
        return self.length == 0

    # 获取链表长度
    def size(self):
        return self.length

    # 获取第一个元素
    def getHead(self):
        return self.head.element

    # 获取最后一个元素
    def getTail(self):
        return self.tail.element

树的结构:

  • 树的定义:

    • 树(Tree): n(n≥0)个结点构成的有限集合。

      • 当n=0时,称为空树;

      • 对于任一棵非空树(n> 0),它具备以下性质:

      • 树中有一个称为“根(Root)”的特殊结点,用 root 表示;

      • 其余结点可分为m(m>0)个互不相交的有限集T1,T2,... ,Tm,其中每个集合本身又是一棵树,称为原来树的“子树(SubTree)”

    • 注意:

      • 子树之间不可以相交

      • 除了根结点外,每个结点有且仅有一个父结点;

      • 一棵N个结点的树有N-1条边。

  • 树的术语:

    • 1.结点的度(Degree):结点的子树个数.

    • 2.树的度:树的所有结点中最大的度数. (树的度通常为结点的个数N-1)

    • 3.叶结点(Leaf):度为0的结点. (也称为叶子结点)

    • 4.父结点(Parent):有子树的结点是其子树的根结点的父结点

    • 5.子结点(Child):若A结点是B结点的父结点,则称B结点是A结点的子结点;子结点也称孩子结点。

    • 6.兄弟结点(Sibling):具有同一父结点的各结点彼此是兄弟结点。

    • 7.路径和路径长度:从结点n1到nk的路径为一个结点序列n1 , n2,… , nk, ni是 ni+1的父结点。路径所包含边的个数为路径的长度。

    • 8.结点的层次(Level):规定根结点在1层,其它任一结点的层数是其父结点的层数加1。

    • 9.树的深度(Depth):树中所有结点中的最大层次是这棵树的深度。

2.二叉树

2.1 二叉树的概念

  • 二叉树的定义

    • 二叉树可以为空, 也就是没有结点.

    • 若不为空,则它是由根结点和称为其左子树TL和右子树TR的两个不相交的二叉树组成。

2.2 二叉树的特性

二叉树有几个比较重要的特性, 在笔试题中比较常见:

  • 一个二叉树第 i 层的最大结点数为:2^(i-1), i >= 1;

  • 深度为k的二叉树有最大结点总数为: 2^k - 1, k >= 1;

  • 对任何非空二叉树 T,若n0表示叶结点的个数、n2是度为2的非叶结点个数,那么两者满足关系n0 = n2 + 1。

2.3 特殊的二叉树

  • 完美二叉树(Perfect Binary Tree) , 也称为满二叉树(Full Binary Tree)

    • 在二叉树中, 除了最下一层的叶结点外, 每层节点都有2个子结点, 就构成了满二叉树.

  • 完全二叉树(Complete Binary Tree)

    • 除二叉树最后一层外, 其他各层的节点数都达到最大个数.

    • 且最后一层从左向右的叶结点连续存在, 只缺右侧若干节点.

    • 完美二叉树是特殊的完全二叉树.

2.5 二叉树的存储

  • 二叉树的存储常见的方式是链表.

  • 链表存储:

    • 二叉树最常见的方式还是使用链表存储.

    • 每个结点封装成一个Node, Node中包含存储的数据, 左结点的引用, 右结点的引用.

3.二叉搜索树

3.1 什么是二叉搜索树?

  • 二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树

  • 二叉搜索树是一颗二叉树, 可以为空;如果不为空,满足以下性质:

    • 非空左子树的所有键值小于其根结点的键值。

    • 非空右子树的所有键值大于其根结点的键值。

    • 左、右子树本身也都是二叉搜索树。

    • 下面哪些是二叉搜索树, 哪些不是?

  • 二叉搜索树的特点:
    • 查找效率非常高, 这也是二叉搜索树中, 搜索的来源.

    • 那么利用这个特点, 我们可以做什么事情呢?

    • 二叉搜索树的特点就是相对较小的值总是保存在左结点上, 相对较大的值总是保存在右结点上.

3.2、二叉搜索树的操作

  • 二叉搜索树有哪些常见的操作呢?

    • insert(key):向树中插入一个新的键。

    • search(key):在树中查找一个键,如果结点存在,则返回true;如果不存在,则返回false

    • preOrderTraverse:通过先序遍历方式遍历所有结点。

    • inOrderTraverse:通过中序遍历方式遍历所有结点。

    • postOrderTraverse:通过后序遍历方式遍历所有结点。

    • min:返回树中最小的值/键。

    • max:返回树中最大的值/键。

    • remove(key):从树中移除某个键。

3.3 二叉树的创建

3.3.1 创建二叉搜索树

class Node:
    def __init__(self,el):
        self.el=el
        self.left=None
        self.right=None

class binary_serach_tree:
    def __init__(self):
        self.root=None

3.3.2 插入数据

    def insert_node(self,node_tree,new_node):
        if node_tree.el<=new_node.el:
            if node_tree.right is None:
                node_tree.right=new_node
            else:
                self.insert_node(node_tree.right,new_node)
        else:
            if node_tree.left is None:
               node_tree.left=new_node
            else:
                self.insert_node(node_tree.left,new_node)
    def insert(self,key):
        #把传入的key封装为node
        node=Node(key)
        #判断是否为空树
        if self.root is None:
            self.root=node
        else:
            self.insert_node(self.root,node)
            # current=self.root
            # last=None
            # while(current):
            #     last=current
            #     if current.el<key:
            #         current = current.right
            #     else:
            #         current = current.left
            # if last.el<key:
            #     last.right=node
            # else:
            #     last.left=node

3.3.3 遍历二叉搜索树

#先序遍历
    def preOrderTranversalNode(self,node,cb):
        cb(node.el)
        if node.left:
            self.preOrderTranversalNode(node.left,cb)
        if node.right:
            self.preOrderTranversalNode(node.right, cb)
    #中序遍历
    def inOrderTranversalNode(self,node,cb):

        if node.left:
            self.inOrderTranversalNode(node.left,cb)
        cb(node.el)
        if node.right:
            self.inOrderTranversalNode(node.right, cb)
    #后序遍历
    def postOrderTranversalNode(self,node,cb):
        if node.left:
            self.postOrderTranversalNode(node.left,cb)
        if node.right:
            self.postOrderTranversalNode(node.right, cb)
        cb(node.el)
    def TranversalNode(self,cb):
        # print(self.root.el)
        # cb(self.root.el)
        if self.root:
            self.preOrderTranversalNode(self.root,cb)#先序
            # self.inOrderTranversalNode(self.root, cb)  # 中序
            # self.postOrderTranversalNode(self.root, cb)  # 后序

3.3.4 搜索特定值

    def min(self):
        if self.root is None:
            return  False
        current=self.root
        while (current.left):
            current=current.left
        return current.el
    def max(self):
        if self.root is None:
            return  False
        current=self.root
        while (current.right):
            current=current.right
        return current.el

    def searchNode(self,node,el):
        if node is None:
            return False
        if node.el>el:
            return self.searchNode(node.left,el)
        elif node.el<el:
            return self.searchNode(node.right,el)
        else:
            return True
    def search(self,el):
        return self.searchNode(self.root,el)

    # 找后继的方法
    def getSuccessor(self, delNode):
            # 1.使用变量保存临时的节点
            successorParent = delNode
            successor = delNode
            current = delNode.right  # 要从右子树开始找
            # 2.寻找节点
            while current is not None:
                successorParent = successor  # while循环完毕-后继的父节点
                successor = current  # while循环完毕-要删除的节点的右子树种最left节点:后继
                current = current.left  # while循环完毕-None
            # 3.如果是删除图中15的情况, 还需要如下代码
            if successor != delNode.right:
                successorParent.left = successor.right
                successor.right = delNode.right
            return successor

3.3.6 二叉搜索树的删除

    def remove(self,el):
        current=self.root
        previous=None
        isleft=True
        while current:
            if current.el>el:
                previous=current
                current=current.left
                isleft=True
            elif current.el<el:
                previous = current
                current=current.right
                isleft = False
            else:
                #current就是要删除的节点
                #previous就是要删除的节点的父节点
                #情况一 current是叶节点
                if current.left is None and current.right is None:
                    if self.root == current:
                        self.root=None
                        return True
                    if isleft:
                        # 把current的后代放在previous的left
                        previous.left=None
                    else:
                        # 把current的后代放在previous的right
                        previous.right = None
                # 情况二 current只有一个儿子
                # 只有左儿子并且没有右儿子
                elif current.left and current.right is None:
                    if self.root == current:
                        self.root = current.left
                        return True
                    #如果删除的元素是它父节点的左边
                    if isleft:
                        # 把current的left后代放在previous的left
                        previous.left = current.left
                    else:
                        # 把current的left后代放在previous的right
                        previous.right = current.left
                #只有右儿子并且没有左儿子
                elif  current.right and current.left is None:
                    if self.root == current:
                        self.root = current.right
                        return True
                    # 如果删除的元素是它父节点的左边
                    if isleft:
                        # 把current的right后代放在previous的left
                        previous.left = current.right
                    else:
                        # 把current的right后代放在previous的right
                        previous.right = current.right
                # 情况三
                #左右儿子都有值
                else:
                    # 3.3.1.获取后继节点
                    successor = self.getSuccessor(current)
                    # 3.3.2. 判断是否是根节点
                    if current == self.root:
                        self.root = successor
                    elif isleft:
                        previous.left = successor
                    else:
                        previous.right = successor
                    # 3.3.3.将删除节点的左子树赋值给successor
                    successor.left = current.left
        return False

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值