• 线性结构的两种应用:栈与队列


    一 栈

    ① 什么是栈:一种可以实现先进后出的数据结构。

    栈类似于一个箱子,先放进去的书,最后才能取出来,同理,后放进去的书,先取出来

    栈的定义.png

    ② 栈的分类:静态栈和动态栈

    静态栈:

      静态栈的核心是数组,类似于一个连续内存的数组,我们只能操作其栈顶元素。

    动态栈:

      动态栈的核心是链表。

    栈的分类.png

     ③ 栈的算法:

    栈的算法主要是压栈和出栈两种操作:

    那么对于栈的操作,我们应该知道:

      1、栈操作的对象是一个一个的节点。

      2、栈本身也是一种存储的数据结构。

      3、栈有  初始化压栈出栈判空遍历清空等主要方法

    class Stack(object):
        def __init__(self):
            self.pTop = None
            self.pBottom = None
    class Node(object):
        def __init__(self, data=None, pNext = None):
            self.data = data
            self.pNext = pNext
    def push(s, new):
        new.pNext = s.pTop
        s.pTop = new
    def pop(s):
        cur = s.pTop
        # while cur != s.pBottom:
        #     if cur.data == val:
        #         s.pTop = cur.pNext
        #         break
        #     cur = cur.pNext
        # else:
        #     print('没有找到此元素')
        while cur != s.pBottom:
            s.pTop = cur.pNext
            print("出栈的元素是: %d" % cur.data)
            cur = cur.pNext
        else:
            print("出栈失败")
    def getAll(s):
        cur = s.pTop
        while cur != s.pBottom:
            print(cur.data)
            cur = cur.pNext
    def is_empty(s):
        if s.pTop == s.pBottom:
            return True
        else:
            return False
    def clear(s):
        if is_empty(s):
            return None
        p = s.pTop
        q = None
        while p != s.pBottom:
            q = p.pNext
            del p
            p = q
        else:
            s.pBottom = s.pTop
    head = Node()
    s = Stack()
    s.pTop = s.pBottom = head
    n1 = Node(2)
    push(s, n1)
    n1 = Node(5)
    push(s, n1)
    n1 = Node(89)
    push(s, n1)
    print("##############遍历元素##########")
    getAll(s)
    # print("##############出栈元素#######")
    # pop(s)
    print("##############清空栈##########")
    clear(s)
    print("##############遍历元素##########")
    getAll(s)
    栈的操作

    ④ 栈的应用:

    • 函数调用:先调用的函数后返回,栈顶的函数永远先执行。
    • 中断
    • 表达式求值
    • 内存分配
    • 走迷宫

    二 队列

    什么叫队列:一种可以实现先进先出的数据结构。

    队列分类:

      静态队列

      链式队列(动态队列)

    class Node:
        def __init__(self, value):
            self.data = value
            self.next = None
    class Queue:
        def __init__(self):
            self.front = Node(None)
            self.rear = self.front
        def enQueue(self, element):
            n = Node(element)
            self.rear.next = n
            self.rear = n
        def deQueue(self):
            if self.empty():
                print('队空')
                return
            temp = self.front.next
            self.front = self.front.next
            if self.rear == temp:
                self.rear = self.front
            del temp
        def getHead(self):
            if self.empty():
                print('队空')
                return
            return self.front.next.data
        def empty(self):
            return self.rear == self.front
        def printQueue(self):
            cur = self.front.next
            while cur != None:
                print(cur.data)
                cur = cur.next
        def length(self):
            cur = self.front.next
            count = 0
            while cur != None:
                count += 1
                cur = cur.next
            return count
    if __name__ == '__main__':
        queue = Queue()
        queue.enQueue(23)
        queue.enQueue(2)
        queue.enQueue(4)
        queue.printQueue()
        queue.deQueue()
        # queue.printQueue()
        l = queue.length()
        print("长度是: %d" % l)
        queue.deQueue()
        # queue.printQueue()
        l = queue.length()
        print("长度是: %d" % l)
        queue.deQueue()
        # queue.printQueue()
        l = queue.length()
        print("长度是: %d" % l)
    链式队列的算法
    实际应用:

    所有和时间有关的操作都和队列有关

    递归定义:

    一个函数自己或者间接调用自己

    多个函数调用:

    当有多个函数调用时,按照“先调用后返回”的原则,函数之间的信息传递和控制转移必须借助栈来实现,即系统将整个程序运行时所需要的数据空间安排在一个栈中,每当调用一个函数时,就在栈顶分配一个存储区,进行压栈操作,每当一个函数退出时,就释放他的存储区,即进行出栈操作,当前运行的函数永远在栈顶的位置

    def f():
    print('FFFFFFF')
    g()
    print('111111')
     
    def g():
    print('GGGGGGG')
    k()
    print('222222')
     
    def k():
    print('KKKKKKK')
     
    if __name__ == "__main__":
    f()

    递归满足的条件自己调用自己也是和上面的原理一样:

    def f(n):
    if n == 1:
    print('hello')
    else:
    f(n-1)
    • 递归必须有一个明确的终止条件
    • 该函数所处理的数据规模是必须递减的
    • 这个转化必须是可解的
    求阶乘

    n规模的实现,得益于n-1规模的实现

    # for循环实现
    multi = 1
    for i in range(3):
    multi = multi * (i+1)
    print(multi)
     
    # 递归实现
     
    def f(n):
    if 1 == n:
    return 1
    else:
    return f(n-1)*n

    1+2+3…+100的和

    def sum(n):
    if 1 == n:
    return n
    else:
    return sum(n-1) + n
    递归和循环的区别
    • 递归
      • 不易于理解
      • 速度慢
      • 存储空间大
    • 循环
      • 易于理解
      • 速度快
      • 存储空间小
    递归的应用

    树和森林就是以递归的方式定义的
    树和图的算范就是以递归的方式实现的
    很多数学公式就是以递归的方式实现的(斐波那楔序列)

     
    阶段总结

    数据结构:
    从狭义的方面讲:

    • 数据结构就是为了研究数据的存储问题
    • 数据结构的存储包含两个方面:个体的存储 + 个体关系的存储

    从广义方面来讲:

    • 数据结构既包含对数据的存储,也包含对数据的操作
    • 对存储数据的操作就是算法

    算法
    从狭义的方面讲:

    • 算法是和数据的存储方式有关的

    从广义的方面讲:

    • 算法是和数据的存储方式无关,这就是泛型的思想
    非线性结构
    树的定义

    我们可以简单的认为:

    • 树有且仅有一个根节点
    • 有若干个互不相交的子树,这些子树本身也是一颗树

    通俗的定义:
    1.树就是由节点和边组成的
    2.每一个节点只能有一个父节点,但可以有多个子节点。但有一个节点例外,该节点没有父节点,此节点就称为根节点

    树的专业术语
    • 节点
    • 父节点
    • 子节点
    • 子孙
    • 堂兄弟
    • 兄弟
    • 深度
      • 从根节点到最底层节点的层数被称为深度,根节点是第一层
    • 叶子节点
      • 没有子节点的节点
      • 子节点的个数
    树的分类
      • 一般树
        • 任意一个节点的子节点的个数不受限制
      • 二叉树
        • 定义:任意一个节点的子节点的个数最多是两个,且子节点的位置不可更改
          • 满二叉树
            • 定义:在不增加层数的前提下,无法再多添加一个节点的二叉树
          • 完全二叉树
            • 定义:只是删除了满二叉树最底层最右边连续的若干个节点
          • 一般二叉树

    二叉树.jpg

     
    树的操作(伪算法)
     

    如何把一个非线性结构的数据转换成一个线性结构的数据存储起来?

    • 一般树的存储
      • 双亲表示法
        • 求父节点方便
      • 孩子表示法
        • 求子节点方便
      • 双亲孩子表示法
        • 求父节点和子节点都很方便
      • 二叉树表示法
        • 即把一般树转换成二叉树,按照二叉树的方式进行存储
        • 具体的转化办法:
          • 设法保证任意一个节点的:
            • 左指针域指向它的第一个孩子
            • 右指针域指向它的下一个兄弟
          • 只要能满足上述的条件就能够转化成功
    • 二叉树的操作

      • 连续存储 (完全二叉树,数组方式进行存储)
        • 优点:查找某个节点的父节点和子节点非常的快
        • 缺点:耗用内存空间过大
        • 转化的方法:先序 中序 后序
      • 链式存储 (链表存储)
        • data区域 左孩子区域 右孩子区域
    • 森林的操作

      • 把所有的树转化成二叉树,方法同一般树的转化
    二叉树具体的操作

    1.二叉树的先序遍历[先访问根节点]

    • 先访问根节点
    • 再先序遍历左子树
    • 再先序遍历右子树

    2.二叉树的中序遍历 [中间访问根节点]

    • 先中序遍历左子树
    • 再访问根节点
    • 再中序遍历右子树

    3.二叉树的后序遍历 [最后访问根节点]

    • 先中序遍历左子树
    • 再中序遍历右子树
    • 再访问根节点

    4.已知先序和中序,如何求出后序?

    #### 例一
    先序:ABCDEFGH
    中序:BDCEAFHG
     
    求后序?
    后序:DECBHGFA
     
    #### 例二
    先序:ABDGHCEFI
    中序:GDHBAECIF
     
    求后序?
    后序:GHDBEIFCA

    树的应用

    中序:BDCEAFHG
    后序:DECBHGFA
     
    求先序?
    先序:ABCDEFGH

    5.已知中序和后序,如何求出先序?

    • 树是数据库中数据组织的一种重要形式
    • 操作系统子父进程的关系本身就是一颗树
    • 面型对象语言中类的继承关系
    总结

    数据结构研究的就是数据的存储和数据的操作的一门学问

    数据的存储又分为两个部分:

    • 个体的存储
    • 个体关系的存储

    从某个角度而言,数据存储最核心的就是个体关系如何进行存储
    个体的存储可以忽略不计

  • 相关阅读:
    java private修饰的类和变量
    Volatile和Synchronized对可见性和原子性的支持
    Socket套接字
    Spring MVC请求执行流程
    Spring AOP术语解释
    equals()和==的区别
    约瑟夫环之递归算法
    数据库特性之原子性和一致性
    设计模式之单例模式
    平衡二叉树的插入旋转
  • 原文地址:https://www.cnblogs.com/zhangsanfeng/p/9587749.html
Copyright © 2020-2023  润新知