zoukankan      html  css  js  c++  java
  • # day11数据结构

    day11数据结构

    目录:

    • 数据结构
      • 队列
      • 双端队列
      • 链表
      • 二叉树
    • 算法:
      • 二分查找
      • 冒泡
      • 选择
      • 快速
      • 插入
      • 希尔
      • 排序二叉树

    什么是算法分析?

    • 刚接触编程的学生经常会将自己编写的程序和别人的程序做比对,获取在比对的过程中会发现双方编写的程序很相似但又各不相同。那么就会出现一个有趣的现象:两组程序都是用来解决同一个问题的,但是两组程序看起来又各不相同,那么哪一组程序更好呢?
    • a+b+c = 1000 a2 + b2 = c**2 (a,b,c均为自然数),求出a,b,c可能的组合?

    1

    for a in range(0,1001):
        for b in range(0,1001):
            for c in range(0,1001):
                if a+b+c == 1000 and a**2 + b**2 == c**2:
                    print(a,b,c)
    

    2

    for a in range(0,1001):
        for b in range(0,1001):
            c = 1000 - a - b
            if a+b+c == 1000 and a**2 + b**2 == c**2:
                print(a,b,c)
    

    ---->

    0 500 500
    200 375 425
    375 200 425
    500 0 500
    

    评判程序优劣的方法

    • 消耗计算机资源和执行效率(无法直观)
    • 计算算法执行的耗时
    • 时间复杂度(推荐)

    时间复杂度

    • 评判规则:量化算法执行的操作/执行步骤的数量
    • 最重要的项:时间复杂度表达式中最有意义的项
    • 使用大O记法来表示时间复杂度:O(算法执行步骤数量表达式中最有意义的一项)

    def sumOfN(n):
    theSum = 0
    for i in range(1,n+1):
    theSum = theSum + i

    return theSum
    

    print(sumOfN(10))

    ---->55

    • 案例:计算下列算法的时间复杂度

    • a=5
      b=6
      c=10
      for i in range(n):
         for j in range(n):
            x = i * i
            y = j * j
            z = i * j
      for k in range(n):
         w = a*k + 45
         v = b*b
      d = 33
      
      ###########################
      3 + 3*n*n + 2*n + 1
      3n**2 +2n + 4
      O(n**2)
      

      常见的时间复杂度:

      • O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

    数据结构

    • 概念:对于数据(基本类型的数据(int,float,char))的组织方式就被称作为数据结构。数据结构解决的就是一组数据如何进行保存,保存形式是怎样的。

    • 案例: 需要存储一些学生的学生信息(name,score),那么这些数据应该如何组织呢?查询某一个具体学生的时间复杂度是什么呢?(三种组织方式)

    [
        [name,score],
        [name1,score1],
        
    ]
    O(n)
    
    (
        [name,score],
        [name1,score1],
    
    )
    O(n)
    
    {
        {'name':'bobo'},
        {'score':100},
    }
    
    
    {
        name:{score:100},
        name2:{score:99}
    }
    
    O(1)
    
    • 三种组织形式基于查询的时间复杂度?

    • 使用不同的形式组织数据,在基于查询时的时间复杂度是不一样的。因此认为算法是为了解决实际问题而设计的,数据结构是算法需要处理问题的载体。

    list(for in range()) 速度最快

    其次是 [] 推到式

    然后是???

    • 特性:先进后出的数据结构

    • 栈顶,栈尾

    • 应用:每个 web 浏览器都有一个返回按钮。当你浏览网页时,这些网页被放置在一个栈中(实际是网页的网址)。你现在查看的网页在顶部,你第一个查看的网页在底部。如果按‘返回’按钮,将按相反的顺序浏览刚才的页面。

    • Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈。

    • push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容。

    • pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改。

    • peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈。

    • isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。

    • size() 返回栈中的 item 数量。不需要参数,并返回一个整数。

    class Stack():
        def __init__(self):
            self.items = []
        def push(self,item):
            self.items.append(item)
        def drop(self):
            return self.items.pop()
        def isEmpty(self):
            return self.items == []
        def size(self):
            return len(self.items)
    s = Stack()
    s.push(1)
    len(s.items)
    print(s.drop())
    print(s.isEmpty())
    print(s.size())
    
    s.push('第一')
    s.push('第二')
    s.push('第三')
    s.items  # ['第一', '第二', '第三']
    # print(s.drop()) # 后进先出对的  # '第三
    

    --->

    1
    True
    0
    
    s = Stack()
    
    def getRequest(url):
        s.push(url)
    def showCurenrUrl():
        print('当前页面展示的url:'+s.pop())
    def back():
        print('回退按钮点击后显示的url:',s.pop())
    
    getRequest('www.1.com')
    getRequest('www.2.com')
    getRequest('www.3.com')
    
    showCurenrUrl()
    back()
    back()
    
    
    

    ---->

    当前页面展示的url:www.3.com
    回退按钮点击后显示的url: www.2.com
    回退按钮点击后显示的url: www.1.com
    

    队列

    • 队列:先进先出

    • 应用场景:

      • 我们的计算机实验室有 30 台计算机与一台打印机联网。当学生想要打印时,他们的打印任务与正在等待的所有其他打印任务“一致”。第一个进入的任务是先完成。如果你是最后一个,你必须等待你前面的所有其他任务打印
    • Queue() 创建一个空的新队列。 它不需要参数,并返回一个空队列。

    • enqueue(item) 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。

    • dequeue() 从队首移除项。它不需要参数并返回 item。 队列被修改。

    • isEmpty() 查看队列是否为空。它不需要参数,并返回布尔值。

    • size() 返回队列中的项数。它不需要参数,并返回一个整数。

    class Queue():
        def __init__(self):
            self.items = []
        def push(self,index,item):
            self.items.insert(index,item)
        def drop(self):
            return self.items.pop()
        def isEmpty(self):
            return self.items == []
        def size(self):
            return len(self.items)
        def peek(self): #最后一个的位置 因为从零开始
            return len(self.items)-1
        
    q = Queue()
    q.push(0,'第一')
    q.push(0,'第二')
    q.push(0,'第三')
    
    q.items # ['第三', '第二', '第一']
    # q.drop() # '第一'
    
    q.peek()
    

    烫手的山芋游戏

    • 案例:烫手的山芋
      • 烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
    • 结论:
      • 七秒即时到了之后,山芋被传递了6次。
      • 保证手里有山芋的孩子永远站在队列的头部
    class Queue():
        def __init__(self):
            self.items = []
        def push(self,index,item):
            self.items.insert(index,item)
        def drop(self):
            return self.items.pop()
        def isEmpty(self):
            return self.items == []
        def size(self):
            return len(self.items)
        def peek(self): #最后一个的位置 因为从零开始
            return len(self.items)-1
    # 7 秒 ,去除第一个人中手上不传出的1秒    
    q1 = Queue()
    kids = ['A','B','C','D','E','F']
    for kid in kids : 
        q1.push(0,kid)    # 把数据添加进去
    q1.items
    while len(q1.items)>1:  # 当数据只有1个时,为游戏的胜者
        for i in range(6):
            a = q1.drop()	# 先删,后添加到最初位置,就是换顺序
            q1.push(0,a)    # ,一个个往后走,然后保证山芋在第一位
        q1.drop()        # 删除第一位
    q1.items    
    

    双端队列

    • 同同列相比,有两个头部和尾部。可以在双端进行数据的插入和删除,提供了单数据结构中栈和队列的特性

    • Deque() 创建一个空的新 deque。它不需要参数,并返回空的 deque。

    • addFront(item) 将一个新项添加到 deque 的首部。它需要 item 参数 并不返回任何内容。

    • addRear(item) 将一个新项添加到 deque 的尾部。它需要 item 参数并不返回任何内容。

    • removeFront() 从 deque 中删除首项。它不需要参数并返回 item。deque 被修改。

    • removeRear() 从 deque 中删除尾项。它不需要参数并返回 item。deque 被修改。

    • isEmpty() 测试 deque 是否为空。它不需要参数,并返回布尔值。

    • size() 返回 deque 中的项数。它不需要参数,并返回一个整数。

    class Queue():
        def __init__(self):
            self.items = []
            # 队列方式加到栈首
        def addfront(self,index,item):
            self.items.insert(index,item)
            # 栈模式,从前面取,就是双端队列
        def addback(self,item):
            self.items.append(item)
            
        def dropfront(self):
            return self.items.pop()
        def dropback(self):
            return self.items.pop(0)  # 123  先进先出   
        
        def isEmpty(self):
            return self.items == []
        def size(self):
            return len(self.items)
        def peek(self): #最后一个的位置 因为从零开始
            return len(self.items)-1
        
    q = Queue()
    
    q.addfront(0,1)
    q.addfront(0,2)
    q.addfront(0,3)
    q.items
    q.dropfront()
    q.dropfront()
    q.dropfront()
    # 首先进先出
    
    q.addback(1)
    q.addback(2)
    q.addback(3)
    q.dropfront()
    q.dropfront()
    q.dropfront()
    q.items
    # 栈  删除方式就是栈了   [1]  
    
    
    q.addback(1)
    q.addback(2)
    q.addback(3)
    
    q.items
    q.dropback()
    q.dropback()
    q.dropback()
    # 后进后厨
    尾先进新出: 两边都是  
    

    双端队列应用案例:回文检查

    回文是一个字符串,读取首尾相同的字符,例如,radar toot madam。

    抄的老师的:

    ab = 'abcdaddcba'
        
    def huiwen(st):
        q = Queue()
        ex = 'True'
        for s in st:
            q.addback(s)
        while len(q.items)>1:
            if q.dropback()!= q.dropfront():
                ex = 'False'
                break 
        return ex
        
    print(huiwen(ab))      
    
    False
    

    内存

    • 计算机的作用

      • 用来存储和运算二进制的数据
    • 衡量计算机内存大小的单位:

      • bit(位):
      • 字节:8bit
      • kb:1024字节
      • mb:1024kb
    • 问题:计算机如何计算1+2?

      • 必须先在计算机的内存开辟内存空间
      • 才将对应的数值存储到该内存中
    • 变量的概念

      • a = 10 在计算机中表示的含义(内存图)
      • 内存空间开辟好了之后,每一块内存空间都会有两个默认的属性
        • 内存空间的大小:算机内存大小的单位
        • 内存空间的地址:十六进制的数值
          • 地址作用:用来定位(追踪)指定内存空间,就可以获取该内存空间中存储的数据
      • 变量本质表示的是内存空间的地址,通常变量想要表示的就是内存地址对应的内存空间
    • 理解a=10的内存图(引用,指向)

      • 引用:变量
      • 指向:如果某一个变量/引用存储了某一块内存空间的地址后,则表示该变量指向了该内存地址所对应的内存空间。
    • 不同数据占用内存空间的大小

    顺序表

    • 容器/数据结构中存储的元素是有顺序的,顺序表的结构可以分为两种形式:单数据类型(numpy)和多数据类型(list)。

    • python中的列表和元组就属于多数据类型的顺序表

    • 单数据类型顺序表的内存图(内存连续开启)

    • 多数据类型顺序表的内存图(内存非连续开辟)

    • 顺序表的弊端:顺序表的结构需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁。

    链表:

    相对于顺序表,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理且进行扩充时不需要进行数据搬迁。

    • 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是每一个结点(数据存储单元)里存放下一个结点的信息(即地址)

    要求

    . is_empty():链表是否为空
    
    . length():链表长度
    
    . travel():遍历整个链表
    
    . add(item):链表头部添加元素
    
    . append(item):链表尾部添加元素
    
    . insert(pos, item):指定位置添加元素
    
    . remove(item):删除节点
    
    . search(item):查找节点是否存在
    

    建立节点

    class Node():
        def __init__(self,item): #建立一个节点,新节点,后续的还不知道,所以没有next
    
    ​        self.item = item	# 添加的值
    ​        self.next = None   # 链子指向,因为是新值,还没来得及建链,故None
    node  = Node(3)       # 实例化一个
    
    
    
    class Link():
        def __init__(self):
            # 建立一个空链表
            self._head = None   # 第一个指向None ,还没有添加进来
            
    	def add(self,item): # 链表头部添加元素
            node = Node(item)   # 实例化一个, 没有后续,next=None  item=item 
            node.next = self._head  
            # 之前的head 1 空, 2次添加是上一个node,因为head被重新赋值了
            # 变成了上一个的node(实例化的) 下面一行代码 ,然后一直迭代
            self._head = node  # 给head指向,实例化的那个Node 第一次    	        
    

    1565874673548

    • 这两个函数都是用的一样的逻辑:
      • 都是不知道循环次数,所以用while循环,然后,一直next ,直到为空,然后退出
       def travel(self): # 遍历整个链表
            cur = self._head # 地址
            while cur: # 最后为空 
                print(cur.item) # 值
                cur = cur.next # 下一个
    
        def length(self):	# 链表长度
            cur = self._head # 地址
            count = 0  
            while cur: # 最后为空
                print(cur.item) # 值
                count += 1 
                cur = cur.next # 下一个          
    #             print('长度为',count)
            return count
    
    • 不要忘了_head就是代表的 节点
     def isEmpty(self):
            return self._head == None
    
    
    • for 循环 然后返回 find为True说明找到了 ,找不到,和上面一样的思想,next往下走
        def search(self,item):     #查找节点是否存在 
            cur = self._head 
            find = 'False' 
            for i in range(self.length()):
                if cur.item == item :
                    find = 'True'
                else:
                    cur = cur.next 
            return find
    
    • 犯得两个错误,因为copy的,结果冒号是中文,还没发现
    • 下面的cur,pre循环,链子的拼接
    • 1565875155955
    #加到最后
    #最后一个指向为none ,主要是对前一个操作
    #因为最后一个next = None,而前一个,指向的是最后一个,所以要链一下最后一个,一条链
        def append(self,item):
            node = Node(item)
            cur = self._head # 地址
            
            # 注意的项:
            # 第一次添加,没有前一项 # 的时候直接,没有最后一个,next 
            if self.isEmpty():
                self._head = node 
                return  # 不要忘了,return 退出
            pre = None 
            while cur: # 最后为空
                 
                pre = cur # 前一个    
                cur = cur.next # 下一个          
                 
            pre.next = node 
    
    	# 插入  
    	# 没想到不难,会了append之后,改一下链子,别的不大动,
    	# 值的注意的点,范围插入时,
    	def insert(self, pos, item):
            node = Node(item)
            cur = self._head # 地址
            if 0 > pos or pos > self.length()+1:
                print(pos,'超范围,没法插入,请重新插入')
                return 
            
            pre = None 
             # 最后为空
            for i in range(1,pos): # 第几个位置 前面添加后天添加建立两个连接
                pre = cur  # 前一个
                cur = cur.next  # 下一个   
                
            node.next = pre.next # node.next ✔  
            					 # 顺序不能错了,先把后面的链子给了中间的
            pre.next = node      # 然后给前面的链子,中间的
    
     #删除节点
        def remove(self,item):
            node = Node(item)
            cur = self._head # 地址
            if self.search(item)=='False': #用搜索,查,在里面然后删
                return '不在里面,不用删除'
            if self.length()==1 or self.length():  #1个或者多个的时候不走下面循环
                self._head == None
                return 
            pre = None 
             # 最后为空  
            for i in range(1,self.length()): # 第几个位置 前面添加后天添加建立两个连接
                pre = cur   # 前一个    
                cur = cur.next   # 下一个   
                
            pre.next = cur.next   #上一个的链连接下一个的下一个的链,所以下一个断开连接 
            
            print('长度',self.length())  
    

    结果打印

    link = Link()
    link.add(1)
    link.add(2)
    link.add(3)
    # link.append(1)
    # link.append(2)
    # link.append(3)
    # link.insert(2,4)
    # link.insert(2,4)
    # link.insert(4,4)
    # link.insert(44,4)# 超范围的不行
    # link.travel()
    # link.length()  
    # link.isEmpty()
    # link.search(-1)
    link.remove(1)
    print(link.remove(4))
    # link.search(1)
    
    3
    2
    1
    3
    2
    1
    3
    2
    1
    3
    2
    1
    不在里面,不用删除
    
  • 相关阅读:
    常用集合比较
    windows和centos下安装ActiveMQ
    windows + maven + eclipse
    Eclipse常用插件 + Eclipse快捷键
    Zookeeper + Dubbo + SpringMVC + dubbo-admin
    Hession集成Spring + maven依赖通讯comm项目 + 解决@ResponseBody中文乱码
    Spring结合log4j(slf4j)
    nginx安装配置+集群tomcat:Centos和windows环境
    Manthan, Codefest 18 (rated, Div. 1 + Div. 2) ABCDE
    51nod 1483 化学变化
  • 原文地址:https://www.cnblogs.com/Doner/p/11361048.html
Copyright © 2011-2022 走看看