数据结构——Python实现

  • 数据间的结构关系
  • 线性表
  • 线性表的顺序存储
  • 线性表的链式存储
  • 栈(Stack)
  • 队列(Queue)
  • 二叉树(Binary Tree)



前段时间在别人的推荐下读了《数据结构与算法》这本书,今天将笔记进行整理。

数据间的结构关系

  1. 逻辑结构
    表示数据间的抽象关系,根据每个元素可能具有直接前趋数和直接后继数将逻辑结构分为“线性结构”和“非线性结构”。
  2. 存储结构
    逻辑结构就是在计算机中的具体实现方式,可以分为顺序存储结构、链接存储结构、索引存储结构。

以下是Python里常见的几种基本的数据结构

线性表

线性表的定义是描述其逻辑结构,而通常会在线性表上进行的查找、插入、删除等操作。在计算机存储器中的存储一般有两种形式,一种是顺序存储,一种是链式存储

线性表的顺序存储

  1. 定义:若将线性表L=(a0,a1, ……,an-1)中的各元素依次存储于计算机一片连续的存储空间,这种机制表
    示为线性表的顺序存储结构。
  2. 特点:
    逻辑上的相邻的元素ai,ai+1,其存储位置也是相邻的;存储密度高,方便对数据的遍历查找;对表的插入和删除等运算的效率较差。
  3. 程序实现:
    Python中可以用列表list实现线性表的顺序存储。

线性表的链式存储

  1. 定义
    将线性表L=(a0,a1,……,an-1)中各元素分布在存储器的不同存储块,称为结点,每个结点(尾节点除外)中都持有一个指向下一个节点的引用,这样所得到的存储结构为链表结构。下图是链式存储示意图。
  2. 特点:
    逻辑上相邻的元素 ai, ai+1,其存储位置也不一定相邻。
    存储稀疏,不必开辟整块存储空间。
    对表的插入和删除等运算的效率较高。
    逻辑结构复杂,不利于遍历。
  3. 程序实现
"""
功能: 实现单链表的构建和操作
"""
# 创建节点类
class Node:
    """
    思路 : *自定义类视为节点类,类中的属性为数据内容
          *写一个next属性,用来和下一个 节点建立关系
    """
    def __init__(self,val,next = None):
        """
        val: 有用数据
        next: 下一个节点引用
        """
        self.val = val
        self.next = next
# 链式线性表操作类
class LinkList:
    """
    思路 : 生成单链表,通过实例化的对象就代表一个链表
          可以调用具体的操作方法完成各种功能
    """
    def __init__(self):
        # 链表的初始化节点,没有有用数据,但是便于标记链表的开端
        self.head = Node(None)
    # 初始化链表,添加一组节点
    def init_list(self,list_):
        p = self.head  # p 作为移动变量
        for i in list_:
            # 遍历到一个值就创建一个节点
            p.next = Node(i)
            p = p.next
    # 遍历链表
    def show(self):
        p = self.head.next  # p代表第一个有值的节点
        while p is not None:
            print(p.val)
            p = p.next  # p向后移动
    # 判断链表为空
    def is_empty(self):
        if self.head.next is None:
            return True
        else:
            return False
    # 清空链表
    def clear(self):
        self.head.next = None
    # 尾部插入
    def append(self,val):
        p = self.head
        # p移动到最后一个节点
        while p.next is not None:
            p = p.next
        p.next = Node(val) # 最后添加节点
    # 头部插入
    def head_insert(self,val):
        node = Node(val)
        node.next = self.head.next
        self.head.next = node
    # 指定位置插入
    def insert(self,index,val):
        # 设置个p 移动到待插入位置的前一个
        p = self.head
        for i in range(index):
            # 如果index超出了最大范围跳出循环
            if p.next is None:
                break
            p = p.next
        # 插入节点
        node = Node(val)
        node.next = p.next
        p.next = node
    # 删除节点
    def remove(self,val):
        p = self.head
        # p 移动,待删除节点上一个
        while p.next is not None and p.next.val != val:
            p = p.next

        if p.next is None:
            raise ValueError("x not in linklist")
        else:
            p.next = p.next.next
    # 获取某个节点的值 (通过索引获取)
    def search(self,index):
        if index < 0:
            raise IndexError("index out of range")

        p = self.head.next
        # 循环移动p
        for i in range(index):
            if p is None:
                raise IndexError("index out of range")
            p = p.next
        return p.val

栈(Stack)

  1. 定义
    栈是只能在一端进行插入和删除操作的线性表,一般称之为堆栈,可以操作的那一端称为“栈顶”,另一端称为“栈尾”,栈中没有元素时称为“空栈”。
  2. 特点
    具有先入后出的特点
    栈结构示意图如下:
  3. 程序实现
    栈的操作有入栈、出栈、判断栈是否为空栈。
    栈的顺序存储代码实现:
"""
栈的顺序存
功能: 出栈, 入栈,判断栈空,查看栈顶元素
"""
# 自定义异常
class StackError(Exception):
    pass
# 顺序栈
class SStack:
    def __init__(self):
        # 空列表就是栈的存储空间
        # 列表的最后一个元素作为栈顶元素,这是因为列表list.pop()方法默认移除最后一个元素
        self.__elems = []
        
    # 入栈
    def push(self,val):
        self.__elems.append(val)
        
    # 判断栈空
    def is_empty(self):
        return self.__elems == []

    # 出栈
    def pop(self):
        if self.is_empty():
            raise StackError("pop from empty stack")
        return self.__elems.pop()

    # 查看栈顶
    def top(self):
        if self.is_empty():
            raise StackError("pop from empty stack")
        return self.__elems[-1]

栈的链式存储代码实现

"""
栈的链式模型
思路:
1. 通过节点存储数据达到链式存储的目的
2. 封装成类实现栈的基本操作(入栈,出栈,栈空,查看栈顶)
3. top为栈顶,在链表的头作为栈顶位置。
"""

# 自定义异常
class StackError(Exception):
    pass

# 节点类
class Node:
    def __init__(self,val,next = None):
        self.val = val
        self.next = next

# 链式栈模型
class LStack:
    def __init__(self):
        # top作为栈顶的标记
        self.__top = None

    def is_empty(self):
        return self.__top is None

    # 入栈
    def push(self,val):
        self.__top = Node(val,self.__top)
        #上面这行代码可以拆分以下三行代码,易读
        # node = Node(val)
        # node.next = self.__top
        # self.__top = node

    # 出栈
    def pop(self):
        if self.__top is None:
            raise StackError("pop from empty stack")
        data =  self.__top.val
        self.__top = self.__top.next
        return data
    
    # 查看栈顶元素
    def top(self):
        if self.__top is None:
            raise StackError("pop from empty stack")
        return self.__top.val

队列(Queue)

  1. 定义:
    队列是可以在两端进行插入和删除操作的线性表。进行存入操作的一端称为“队尾”,可以进行删除操作的一端称为“队头”。
  2. 特点:
    队列只能在对头队尾进行数据操作,并且具有先进先出的特点。
    队列结构示意图如下:
  3. 队列的程序实现
    队列的操作一般有入队、出队、判断队列是否为空、判断队列是否为满队列。
    队列的顺序存储结构代码:
"""
队列的顺序存储
思路 :
1. 基于列表完成数据存储
2. 对列表功能进行封装
3. 列表的头部作为队头,尾部作为队尾
功能: 入队(enqueue),出队(dequeue),判断队列为空
"""

# 自定义异常
class QueueError(Exception):
    pass

class SQueue:
    # 设置空列表作为队列存储空间
    def __init__(self):
        self.__elems = []

    # 判断队列是否为空
    def is_empty(self):
        return self.__elems == []

    # 入队
    def enqueue(self,val):
        self.__elems.append(val)

    # 出对
    def dequeue(self):
        if not self.__elems:
            raise QueueError("Queue is empty")
        return self.__elems.pop(0)

队列的链式存储结构代码:

"""
链式队列
思路:
1. 基于链表构建队列模型
2. 链表的开端作为队头, 结尾作为队尾
3. 对头队尾分别添加标记,避免每次插入数据都遍历链表
4. 队头和队尾重叠时认为队列为空
"""
# 自定义异常
class QueueError(Exception):
    pass

# 节点类
class Node:
    def __init__(self,val,next = None):
        self.val = val
        self.next = next

# 队列操作
class LQueue:
    def __init__(self):
        # 定义队头,队尾
        self.front = self.rear = Node(None)

    def is_empty(self):
        return self.front == self.rear

    # 如队  rear动
    def enqueue(self,val):
        self.rear.next = Node(val)
        self.rear = self.rear.next

    # 出队  front动
    def dequeue(self):
        if self.front == self.rear:
            raise QueueError("Queue is empty")

        # front移动到的节点已经出队
        self.front = self.front.next
        return self.front.val

二叉树(Binary Tree)

以上三种数据结构都属于线性表结构,二叉树属于一种常见的树形结构。

  1. 定义
    二叉树(Binary Tree)是n(n≥0)个节点的有限集合,可能是空集(n=0),也可能是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。二叉树严格区分左子树和右子树,即使只有一个子节点也要区分左右。
  2. 特征
    二叉树第i(i≥1)层上的节点最多2^(k-1)为个。
    深度为k(k≥1)的二叉树最多有2^k-1个节点。
    在任意一棵二叉树中,树叶的数目比度数为2的节点的数目多一。
    满二叉树 :深度为k(k≥1)时有2^k-1个节点的二叉树。
  3. 二叉树遍历
    遍历 :沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次。
    先序遍历: 先访问树根,再访问左子树,最后访问右子树;
    中序遍历: 先访问左子树,再访问树根,最后访问右子树;
    后序遍历: 先访问左子树,再访问右子树,最后访问树根;
    层次遍历: 从根节点开始,逐层从左向右进行遍历。
  4. 代码实现
    二叉树的顺序存储结构可以用Python list进行存储,但是list本身是一种浪费内存空间的存储方式,如果二叉树结构比较稀疏的话,就更加浪费空间了。如果使用列表list存储的话结构如下:
B_tree = ['A', ['B',None,None
               ],
               ['C',['D',['F',None,None],
                    ['G',None,None],
                    ],
                    ['E', ['H',None,None],
                          ['I',None,None],
                    ],
               ]
          ]
# None表示空节点
# 非空二叉树用包含三个元素的列表[d,l,r]表示,其中d表示根结点,l,r左子树和右子树。

上面代码表示的结构图如下:

Python怎么构建逻辑数组 python逻辑结构分类_二叉树

  1. 二叉树遍历
    由于二叉树的顺序结构存储比较浪费空间,所以一般使用链式存储存储,一并在代码里说明:
"""
二叉树的遍历实践
思路分析:
1. 使用链式结构存储二叉树的节点数据
2. 节点中存储 数据, 左子树链接,右子树链接 三个属性
"""
import squeue # 导入上面的顺序存储的队列类下面代码需要借助队列实现

# 二叉树节点类
class Node:
    def __init__(self,val,left=None,right=None):
        self.val = val
        self.left = left
        self.right = right

# 二叉树遍历方法
class Bitree:
    def __init__(self,root):
        self.root = root

    # 先序遍历
    def preOrder(self,node):
        if node is None:
            return
        print(node.val)
        self.preOrder(node.left)
        self.preOrder(node.right)

    # 中序遍历
    def inOrder(self, node):
        if node is None:
            return
        self.inOrder(node.left)
        print(node.val)
        self.inOrder(node.right)

    # 后序遍历
    def postOrder(self, node):
        if node is None:
            return
        self.postOrder(node.left)
        self.postOrder(node.right)
        print(node.val)

    # 层次遍历
    def levelOrder(self,node):
        """
        层次遍历:先建立一个队列,将根节点,其左子节点、右子节点依次入队,谁出对列遍历谁。从根节点开始。
        """
        sq = SQueue()
        sq.enqueue(node)
        while not sq.is_empty():
            node = sq.dequeue()
            print(node.val) # 遍历元素
            if node.left:
                sq.enqueue(node.left)
            if node.right:
                sq.enqueue(node.right)