数据结构与算法整理2

双向链表

双向链表:可以从头遍历到尾,也可以从尾遍历到头,链表相连的过程是双向的。

单项链表的缺点:当我们需要回到前一个节点时很困难。

双向链表的缺点:

  • 插入或者删除时我们需要处理的引用有四个,而不是单向节点的两个,相对麻烦一点
  • 占用的空间更大

双向链表的图解

在这里插入图片描述

双向链表的常用功能实现:

is_empty:判断链表是否为空

append:尾部追加数据

insert:任意位置插入数据

remove_at:根据位置移除数据

index_of :获取元素位置

remove:根据元素删除数据

size:获取链表长度

class Node:
    def __init__(self, element):
        self.element = element
        self.prev = None
        self.next = None


class DoubleLinklist:
    def __init__(self):
        self.head = None
        self.tail = None
        self.length = 0

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

    # 向链表的末尾添加数据
    def append(self, element):
        node = Node(element)
        if self.is_empty():
            self.head = node
            self.tail = node
        else:
            self.tail.next = node
            node.prev = self.tail
            self.tail = node
        self.length += 1

    # 在任意位置插入数据
    def insert(self, element, position):
        node = Node(element)
        # 判断是否越界
        if position < 0 or position > self.length:
            return False
        # 在链表末尾插入
        elif position == self.length:
            self.append(element)
        # 在链表第一个位置插入
        elif position == 0:
            if self.is_empty():
                self.head = node
                self.tail = node
        # 在中间位置插入
        else:
            current = self.head
            previous = None
            for i in range(position):
                previous = current
                current = current.next
            previous.next = node
            current.prev = node
            node.prev = previous
            node.next = current

        self.length += 1
        return True

    # 移除任意位置的数据并返回该值
    def remove_at(self, position):
        current = self.head
        # 判断是否越界
        if position < 0 or position > self.length:
            return False
        # 移除第一个位置
        elif position == 0:
            if self.length == 1:
                self.head = None
                self.tail = None
            else:
                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:
            previous = None
            for i in range(position):
                previous = current
                current = current.next
            previous.next = current.next
            current.next.prev = previous

        self.length -= 1
        return current.element

    # 获取元素位置
    def index_of(self, element):
        current = self.head
        position = 0
        while current:
            if current.element == element:
                return position
            position += 1
            current = current.next
        return False

    # 根据元素删除
    def remove(self, element):
        index = self.index_of(element)
        return self.remove_at(index)

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


dlinklist = DoubleLinklist()
dlinklist.append("qwer")
dlinklist.append(123)
dlinklist.append("abcd")
dlinklist.append(456)
print(dlinklist.insert("efg", 1))
print(dlinklist.insert(234, 8))
print(dlinklist.index_of(123))

树也是一种非常常用的数据结构, 特别是二叉树.

二叉树是程序中一种非常重要的数据结构, 它的优势是前面介绍的数据结构所没有的.

1.树的定义

  • 树(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.二叉树

二叉树的定义

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

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

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

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

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

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

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

完全二叉树(Complete Binary Tree)

  • 除二叉树最后一层外, 其他各层的节点数都达到最大个数.
  • 且最后一层从左向右的叶结点连续存在, 只缺右侧若干节点.
  • 完美二叉树是特殊的完全二叉树.

2.二叉搜索树

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

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

    • 非空左子树的所有键值小于其根结点的键值。
    • 非空右子树的所有键值大于其根结点的键值。
    • 左、右子树本身也都是二叉搜索树。

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

  • insert(key):向树中插入一个新的键。
  • search(key):在树中查找一个键,如果结点存在,则返回true;如果不存在,则返回false
  • preOrderTraverse:通过先序遍历方式遍历所有结点。
  • inOrderTraverse:通过中序遍历方式遍历所有结点。
  • postOrderTraverse:通过后序遍历方式遍历所有结点。
  • min:返回树中最小的值/键。
  • max:返回树中最大的值/键。
  • remove(key):从树中移除某个键。
# 创建结点类
class Node:
    def __init__(self, key):
        self.key = key
        self.left = None
        self.right = None
#创建BinarySearchTree
class BinarySerachTree:  
  def __init__(self):
      self.root = None  # 保存根的属性

  # 向树中插入数据
  def insert(self, key):
      # 1.根据key创建对应的node
      newNode = Node(key)
      # 2.判断根结点是否有值
      if self.root == None:
          self.root = newNode
      else:
          self.insertNode(self.root, newNode)

  def insertNode(self, node, newNode):
      # 1.准备向左子树插入数据
      if newNode.key < node.key:
          if (node.left == None):
              # 1.1.node的左子树上没有内容
              node.left = newNode
          else:
              # 1.2.node的左子树上已经有了内容
              self.insertNode(node.left, newNode)
      # 2.准备向右子树插入数据
      else:
          if node.right == None:
              # 2.1.node的右子树上没有内容
              node.right = newNode
          else:
              # 2.2.node的右子树上有内容
              self.insertNode(node.right, newNode)

  # 先序遍历
  def preOrderTraversal(self, handler):
      self.preOrderTranversalNode(self.root, handler)

  def preOrderTranversalNode(self, node, handler):
      if node is not None:
          # 1.打印当前经过的节点
          handler(node.key)
          # 2.遍历所有的左子树
          self.preOrderTranversalNode(node.left, handler)
          # 3.遍历所有的右子树
          self.preOrderTranversalNode(node.right, handler)

  # 中序遍历
  def inOrderTraversal(self, handler):
      self.inOrderTraversalNode(self.root, handler)

  def inOrderTraversalNode(self, node, handler):
      if node is not None:
          # 1.遍历所有的左子树
          self.inOrderTraversalNode(node.left, handler)
          # 2.打印当前经过的节点
          handler(node.key)
          # 3.遍历所有的右子树
          self.inOrderTraversalNode(node.right, handler)

  # 后续遍历
  def postOrderTraversal(self, handler):
      self.postOrderTraversalNode(self.root, handler)

  def postOrderTraversalNode(self, node, handler):
      if node is not None:
          # 1.遍历所有的左子树
          self.postOrderTraversalNode(node.left, handler)
          # 2.遍历所有的右子树
          self.postOrderTraversalNode(node.right, handler)
          # 3.打印当前经过的节点
          handler(node.key)

  # 获取最小值
  def min(self):
      node = self.root
      while node.left is not None:
          node = node.left
      return node.key

  # 获取最大值
  def max(self):
      node = self.root
      while node.right is not None:
          node = node.right
      return node.key

  # 搜搜特定的值
  def search(self, key):
      return self.searchNode(self.root, key)

  def searchNode(self, node, key):
      # 1.如果传入的node为None那么, 那么就退出递归
      if node is None:
          return False
      # 2.判断node节点的值和传入的key大小
      if node.key > key:  # 2.1.传入的key较小, 向左边继续查找
          return self.searchNode(node.left, key)
      elif node.key < key:  # 2.2.传入的key较大, 向右边继续查找
          return self.searchNode(node.right, key)
      else:  # 2.3.相同, 说明找到了key
          return True

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

  # 删除结点
  def remove(self, key):
      # 1.定义临时保存的变量
      current = self.root
      parent = None
      isLeftChild = True

      # 2.开始查找节点
      while current.key != key:
          parent = current
          if key < current.key:
              isLeftChild = True
              current = current.left
          else:
              isLeftChild = False
              current = current.right
          # 如果发现current已经指向None, 那么说明没有找到要删除的数据
          if current is None:
              return False
      # 3.找到了开始删除
      # 3.1删除的结点是叶结点
      if current.left is None and current.right is None:
          if current == self.root:
              self.root = None
          elif isLeftChild:
              parent.left = None
          else:
              parent.right = None
      # 3.2删除有一个子节点的节点
      elif current.right is None:
          if current == self.root:
              self.root = current.left
          elif isLeftChild:
              parent.left = current.left
          else:
              parent.right = current.left
      elif current.left is None:
          if current == self.root:
              self.root = current.right
          elif isLeftChild:
              parent.left = current.right
          else:
              parent.right = current.right

      # 3.3删除有两个子节点的节点
      else:
          # 3.3.1.获取后继节点
          successor = self.getSuccessor(current)
          # 3.3.2. 判断是否是根节点
          if current == self.root:
              self.root = successor
          elif isLeftChild:
              parent.left = successor
          else:
              parent.right = successor
          # 3.3.3.将删除节点的左子树赋值给successor
          successor.left = current.left

      # 4.删除完毕返回True
      return True
      
  bst = BinarySerachTree()
  bst.insert(11)
  bst.insert(7)
  bst.insert(15)
  bst.insert(5)
  bst.insert(3)
  bst.insert(9)
  bst.insert(8)
  bst.insert(10)
  bst.insert(13)
  bst.insert(12)
  bst.insert(14)
  bst.insert(20)
  bst.insert(18)
  bst.insert(25)
  bst.insert(6)
  bst.preOrderTraversal(lambda key: print(key, end=" "))
  #bst.inOrderTraversal(lambda key: print(key, end=" "))
  #bst.postOrderTraversal(lambda key: print(key, end=" "))
  print(bst.min())
  print(bst.max())
  print(bst.search(25))
  print(bst.search(100))
  bst.remove(15)
  bst.inOrderTraversal(lambda key: print(key, end=" "))

st.insert(12)
bst.insert(14)
bst.insert(20)
bst.insert(18)
bst.insert(25)
bst.insert(6)
bst.preOrderTraversal(lambda key: print(key, end=" “))
#bst.inOrderTraversal(lambda key: print(key, end=” “))
#bst.postOrderTraversal(lambda key: print(key, end=” “))
print(bst.min())
print(bst.max())
print(bst.search(25))
print(bst.search(100))
bst.remove(15)
bst.inOrderTraversal(lambda key: print(key, end=” "))


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值