数据结构与算法

算法概述

  • 算法-前序

    【1】Everybody!全场动作必须跟我整齐划一,来,我们一起来做一道题
    若n1+n2+n3=1000,且n1^2+n2^2=n3^2(n1,n2,n3为自然数),求出所有n1、n2、n3可能的组合 【2】解题思路
    n1 = 0
    n2 = 0
    n3 = 0
    判断n1+n2+n3是否等于1000,之后变n3=1,n3=2,n3=3,... 然后再变n2 【3】代码实现
    import time start_time = time.time()
    for n1 in range(0,1001):
    for n2 in range(0,1001):
    for n3 in range(0,1001):
    if n1 + n2 + n3 == 1000 and n1**2 + n2**2 == n3**2:
    print('[%d,%d,%d]' % (n1,n2,n3))
    end_time = time.time()
    print('执行时间:%.2f' % (end_time-start_time)) 【4】算法概念
    4.1) 解决问题的方法,是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令
    4.2) 算法代表着用系统的方法描述解决问题的策略机制

时间复杂度概述

  • 时间复杂度 - 前序

    【1】各位,一万年太久,只争朝夕,来提升一下上题的效率吧!!!
    for n1 in range(0,1001):
    for n2 in range(0,1001):
    n3 = 1000 - n1 - n2
    if n1**2 + n2**2 == n3**2:
    print('[%d,%d,%d]'%(n1,n2,n3)) 【2】总结与思考 : 解决同一个问题有多种算法,但是效率有区别,那么如何衡量呢?
    2.1) 执行时间反应算法效率 - 绝对靠谱吗?
    不是绝对靠谱: 因机器配置有高有低,不能冒然绝对去做衡量 2.2) 那如何衡量更靠谱???
    运算数量 - 执行步骤的数量 【4】时间复杂度概念
    4.1) 同一个算法,由于机器配置差异,每台机器执行的总时间不同,但是执行基本运算的数量大体相同,所以把算法执行步骤的数量称为时间复杂度
  • 时间复杂度 - 大O表示法前序

    ################################################################
    【1】计算此算法的时间复杂度
    for n1 in range(0,1001):
    for n2 in range(0,1001):
    for n3 in range(0,1001):
    if n1 + n2 + n3 == 1000 and n1**2 + n2**2 == n3**2:
    print('[%d,%d,%d]' % (n1,n2,n3))
    ################################################################
    【2】计算步骤
    T = 1000 * 1000 * 1000 * 2
    T = n * n * n * 2
    T(n) = n ** 3 * 2 即时间复杂度为: T(n)=n**3 * 2 【3】时间复杂度T(n)的 大O表示法
    3.1) 函数1: T(n)=k * n**3 + c
    3.2) 函数2: g(n)=n**3
    3.3) 特点: 在趋向无穷的极限意义下,函数T(n)的增长速度受到函数g(n)的约束,也为函数T(n)与函数g(n)的特征相似,则称 g(n) 是 T(n) 的渐近函数,大O表示法则使用渐近函数来表示
    即: O(g(n))
    即: O(n^3)
    即: 上述时间复杂度为 O(n^3) 【4】时间复杂度总结
    4.1) 假设存在函数g,使得算法A处理规模为n的问题所用时间为T(n)=O(g(n)),则称O(g(n))为算法A的渐近时间复杂度,简称时间复杂度,记为T(n)
    4.2) 对算法进行特别具体细致分析虽然好,但实践中实际价值有限。对我们来说算法的时间性质和空间性质最重要的是数量级和趋势,这些是分析算法效率的主要部分。所以忽略系数,忽略常数,比如5*n^2 和 100*n^2属于一个量级,时间复杂度为O(n^2)
  • 时间复杂度分类

    【1】最优时间复杂度 - 最少需要多少个步骤
    【2】最坏时间复杂度 - 最多需要多少个步骤
    【3】平均时间复杂度 - 平均需要多少个步骤 我们平时所说的时间复杂度,指的是最坏时间复杂度

时间复杂度 - 计算规则

  • 计算原则

    【1】基本操作,只有常系数,认为其时间复杂度为O(1)
    顺序 - 基本步骤之间的累加
    print('abc') -> O(1)
    print('abc') -> O(1) 【2】循环: 时间复杂度按乘法进行计算 【3】分支: 时间复杂度取最大值(哪个分支执行次数多算哪个) 【4】练习:请计算如下代码的时间复杂度
    for n1 in range(0,1001):
    for n2 in range(0,1001):
    n3 = 1000 - n1 - n2
    if n1**2 + n2**2 == n3**2:
    print('[%d,%d,%d]'%(n1,n2,n3)) T(n) = n * n * (1+1+max(1,0))
    T(n) = n**2 * 3
    T(n) = n**2
    T(n) = O(n**2)
    用大O表示法表示为 Tn = O(n^2)
  • 常见时间复杂度

    执行次数 时间复杂度
    20(20个基本步骤) O(1) 常数阶
    8n+6 O(n) 线性阶
    2n^2 + 4n + 2 O(n^2) 平方阶
    8logn + 16 O(logn) 对数阶
    4n + 3nlogn + 22 O(nlog(n)) nlog阶
    2n^3 + 2n^2 + 4 O(n^3) 立方阶
    2 ^ n O(2^n) 指数阶
  • O(1)

    【1】O(1)
    print('全场动作必须跟我整齐划一') 【2】O(1)
    print('左边跟我一起画个龙')
    print('在你右边画一道彩虹')
    print('走起')
    print('左边跟我一起画彩虹')
    print('在你右边再画一条龙')
  • O(n)

    for i in range(n):
    print('在胸口比划一个郭富城')
  • O(n^2)

    【1】O(n^2)
    for i in range(n):
    for j in range(n):
    print('左边右边摇摇头') 【2】O(n^2)
    for i in range(n):
    print('两根手指就像两个窜天猴')
    for j in range(n):
    print('指向闪耀的灯球')
  • O(n^3)

    for i in range(n):
    for j in range(n):
    for k in range(n):
    print('走你')
  • O(logn)

    n = 64
    while n > 1:
    print(n)
    n = n // 2 【解释】
    2的6次方 等于 64,log264 = 6,所以循环减半的时间复杂度为O(log2n),即O(logn)
    如果是循环减半的过程,时间复杂度为O(logn)或O(log2n)
  • O(nlogn)

    n = 64
    while n > 1:
    for i in range(n):
    print('哪里有彩虹告诉我')
    n = n // 2
  • 常见时间复杂度排序

    O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
  • 练习: 写出如下的时间复杂度

    O(5)          --> O(1)
    O(2n+1) --> O(n)
    O(n**2+n+1) --> O(n**2)
    O(3n**3+1) --> O(n**3)
  • 终极总结两句话

    【1】时间复杂度是多少: T(n) = O(???)
    【2】去掉系数、去掉常数、去掉低次幂,最终得到时间复杂度

数据结构概述

  • 数据结构描述

    【1】概述
    1.1) 在工作中,我们为了解决问题,需要将数据保存下来,然后根据数据存储方式设计算法进行处理,根据数据的存储方式我们使用不同的算法处理,而我们现在需要考虑算法解决问题的效率问题,所以需要考虑数据究竟如何保存,这就是数据结构 【2】概念
    2.1) 数据是一个抽象的概念,将其进行分类后得到程序设计语言中的基本类型,如:list、tuple等。数据元素之间不是独立的,存在特定的关系,这些关系便是结构。数据结构指数据对象中数据元素之间的关系
    2.2) Python提供了很多现成的数据结构类型,如列表、元组、字典等,无须我们自己去定义,而Python没有定义的,就需要我们自己去定义实现这些数据的组织方式,称为Python扩展数据结构,如:栈、队列等 【3】为什么学习数据结构
    在真正的项目开发中,大部分时间都是从数据库取数据 -> 数据操作和结构化 -> 返回给前端,在数据操作过程中需要合理地抽象,组织、处理数据,如果选用了错误的数据结构,就会造成代码运行低效
  • 数据结构分类

    【1】线性结构 : n个数据元素的有序集合
    1.2) 顺序表 : 将数据结构中各元素按照其逻辑顺序存放于存储器一片连续的存储空间中
    1.3) 链表 : 将数据结构中各元素分布到存储器的不同点,用记录下一个结点位置的方式建立它们之间的联系
    1.4) 栈 : 后进先出
    1.5) 队列 : 先进先出 【2】非线性结构
    2.1) 树形结构
    2.2) 图状结构
  • 数据结构+算法总结

    【1】数据结构只是静态描述了数据元素之间的关系
    【2】高效的程序需要在数据结构的基础上设计和选择算法
    【3】程序 = 数据结构 + 算法
    【4】算法是为了解决实际问题而设计的,数据结构是算法需要处理的问题载体

抽象数据类型

  • 概念

    【1】定义
    抽象数据类型是指一个数学模型以及定义在此数学模型上的一组操作,及把数据类型和数据类型上的运算捆在一起进行封装。引入抽象数据类型的目的是把数据类型的表示和数据类型上的运算的实现与这些数据类型和运算在程序中的引用隔开,使他们相互独立 【2】描述
    把原有的基本数据和这个数据所支持的操作放到一起,形成一个整体 【3】最常用的数据运算
    3.1) 插入
    3.2) 删除
    3.3) 修改
    3.4) 查找
    3.5) 排序

线性表 - 顺序表

  • 顺序表的基本形式

    【1】特点 : 内存连续
    【2】分类
    2.1) 基本形式: 数据元素本身连续存储,每个元素所占存储单元大小固定相同
    2.2) 元素外置: 数据元素不连续存储,地址单元连续存储

线性表 - 链表

  • 定义

    【1】特点:
    1.1) 内存不连续的,而是一个个串起来的,需要每个链表的节点保存一个指向下一个节点的指针 【2】和顺序表的对比
    2.1) 顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,使用起来不灵活,而链表结构可以充分利用计算机的内存空间,实现灵活的内存动态管理
  • 示例 - 强化理解

    将线性表L=(a0,a1,……,an-1)中各元素分布在存储器的不同存储块,称为结点,每个结点(尾节点除外)中都持有一个指向下一个节点的引用,这样所得到的存储结构为链表结构







  • 单链表 - 代码实现

    """
    创建单链表的数据结构:
    1、节点类 - 数据区、链接区
    2、单链表类(数学模型): 增加、删除... ...
    """ class Node:
    """节点类"""
    def __init__(self, value):
    self.value = value
    self.next = None class SingleLinkList:
    """单链表类"""
    def __init__(self, node=None):
    """创建链表时: s=SingleLinkList()表示空链表,s=SingleLinkList(Node(100)) 表示有1个节点的单链表"""
    self.head = node def is_empty(self):
    """判断链表是否为空"""
    return self.head == None def lengh(self):
    """获取链表长度"""
    # 游标:从头节点开始,一直往后移动,移动一次,+1
    current = self.head
    count = 0
    while current is not None:
    count += 1
    current = current.next return count def travel(self):
    """遍历整个链表"""
    current = self.head
    while current is not None:
    print(current.value,end=" ")
    current = current.next
    # 因为上面是end=" ",所以此处打印一个换行
    print() def add(self, item):
    """链表头部添加1个节点"""
    node = Node(item)
    # 1、把新添加的节点指针指向原来头节点
    node.next = self.head
    # 2、添加的节点设置为新的头
    self.head = node def append(self, item):
    """链表尾部添加1个节点,考虑空链表特殊情况"""
    node = Node(item)
    if self.is_empty():
    self.head = node
    else:
    current = self.head
    while current.next is not None:
    current = current.next
    # 循环结束后,current指向尾节点
    current.next = node
    node.next = None def search(self, item):
    """查看在链表中是否存在"""
    current = self.head
    while current != None:
    if current.value == item:
    return True
    else:
    current = current.next return False def insert(self, pos, item):
    """在指定索引添加一个节点,索引值从0开始"""
    if pos < 0:
    self.add(item)
    elif pos > self.lengh() - 1:
    self.append(item)
    else:
    pre = self.head
    count = 0
    while count < (pos - 1):
    count += 1
    pre = pre.next # 循环结束后,pos指向(pos-1)位置
    node = Node(item)
    node.next = pre.next
    pre.next = node if __name__ == '__main__':
    s = SingleLinkList()
    # 终端1:True
    print(s.is_empty())
    # 链表:Node(100) -> Node(200) -> Node(300)
    s.add(200)
    s.add(100)
    s.append(300)
    # 终端2:3
    print(s.lengh())
    # 终端3:100 200 300
    s.travel()
    # 100 666 200 300
    s.insert(1, 666)
    # 终端4: 100 666 200 300
    s.travel()
    # 终端5: True
    print(s.search(666))

链表练习一

  • 题目

    【1】题目描述
    输入一个链表,按链表值从尾到头的顺序返回一个 ArrayList 【2】试题解析
    将链表的每个值取出来然后存放到一个列表 ArrayList 中
    解题思路1: 将链表中从头节点开始依次取出节点元素,append到array_list中,并进行最终反转
    解题思路2: 将链表中从头节点开始依次取出节点元素,insert到array_list中的第1个位置
  • 代码实现

    """
    输入一个链表,按链表值从尾到头的顺序返回一个 ArrayList
    """ class Node:
    """链表节点类"""
    def __init__(self,x):
    self.val = x
    self.next = None class Solution:
    # 返回从尾部到头部的序列,node为头节点
    def get_list_from_tail_to_head(self,node):
    array_list = []
    while node:
    array_list.insert(0,node.val)
    node = node.next return array_list if __name__ == '__main__':
    s = Solution()
    head = Node(100)
    head.next = Node(200)
    head.next.next = Node(300)
    print(s.get_list_from_tail_to_head(head))

链表练习二

  • 题目

    【1】题目描述
    输入一个链表,反转链表后,输出新链表的表头 【2】试题解析
    可以将链表的每一个节点取出来,插入到新的链表表头,同时要保存原链表的下一个节点
  • 代码实现

    """
    输入一个链表,反转链表后,输出新链表的表头
    思路:
    1、创建2个游标,代表要进行反转操作的节点 和 上一个节点
    2、代码逻辑:
    当前节点指针指向上一个节点
    两个游标同时往后移动
    结束标准: 当要操作的节点为None时,结束! 此时pre代表的是新链表的头节点
    """
    class Node:
    def __init__(self, value):
    self.value = value
    self.next = None class Solution:
    def reverse_link_list(self, head):
    # 1、空链表情况
    if head == None:
    return
    # 2、非空链表情况
    pre = None
    cur = head
    while cur:
    # 记录下一个要操作反转的节点
    next_node = cur.next
    # 反转节点cur,并移动两个游标
    cur.next = pre
    pre = cur
    cur = next_node return pre.value if __name__ == '__main__':
    s = Solution()
    # 1、空链表情况
    head = None
    print(s.reverse_link_list(head))
    # 2、只有1个节点情况
    head = Node(100)
    print(s.reverse_link_list(head))
    # 3、有多个节点情况
    head = Node(100)
    head.next = Node(200)
    head.next.next = Node(300)
    print(s.reverse_link_list(head))

线性表 - 栈(LIFO)

  • 定义

    栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈),允许进行操作的一端称为"栈顶",另一固定端称为"栈底",当栈中没有元素时称为"空栈"
  • 特点

    【1】栈只能在一端进行数据操作
    【2】栈模型具有后进先出的规律(LIFO)

  • 栈的代码实现

    # 栈的操作有入栈(压栈),出栈(弹栈),判断栈是否为空等操作
    """
    sstack.py 栈模型的顺序存储
    重点代码 思路:
    1. 利用列表完成顺序存储,但是列表功能多,不符合栈模型特点
    2. 使用类将列表封装,提供符合栈特点的接口方法
    """ # 顺序栈模型
    class Stack(object):
    def __init__(self):
    # 开辟一个顺序存储的模型空间
    # 列表的尾部表示栈顶
    self.elems = [] def is_empty(self):
    """判断栈是否为空"""
    return self.elems == [] def push(self,val):
    """入栈"""
    self.elems.append(val) 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("Stack is empty")
    return self.elems[0] if __name__ == '__main__':
    st = Stack()
    st.push(1)
    st.push(3)
    st.push(5)
    print(st.top())
    while not st.is_empty():
    print(st.pop())

线性表 - 队列(FIFO)

  • 定义

    队列是限制在两端进行插入操作和删除操作的线性表,允许进行存入操作的一端称为"队尾",允许进行删除操作的一端称为"队头"
  • 特点

    1) 队列只能在队头和队尾进行数据操作
    2) 队列模型具有先进先出规律(FIFO)

  • 队列的代码实现

    # 队列的操作有入队,出队,判断队列的空满等操作
    """
    思路分析:
    1. 基于列表完成数据的存储
    2. 通过封装功能完成队列的基本行为
    3. 无论那边做对头/队尾 都会在操作中有内存移动
    """ # 队列操作
    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 Exception("Queue is empty")
    return self.elems.pop(0) # 弹出第一个数据 if __name__ == '__main__':
    sq = SQueue()
    sq.enqueue(10)
    sq.enqueue(20)
    sq.enqueue(30)
    while not sq.is_empty():
    print(sq.dequeue())

    作业

【1】手写单链表
【2】输入一个链表,输出该链表中倒数第 k 个节点
【3】输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则
【4】使用链式存储方式实现栈
【5】使用链式存储方式实现队列



数据结构(DataStructure)-01的更多相关文章

  1. c语言数据结构:01背包问题-------动态规划

    两天的时间都在学习动态规划:小作业(01背包问题:) 数据结构老师布置的这个小作业还真是让人伤头脑,自己实在想不出来了便去网上寻找讲解,看到一篇不错的文章: http://www.cnblogs.co ...

  2. 数据结构(DataStructure)与算法(Algorithm)、STL应用

    catalogue . 引论 . 数据结构的概念 . 逻辑结构实例 2.1 堆栈 2.2 队列 2.3 树形结构 二叉树 . 物理结构实例 3.1 链表 单向线性链表 单向循环链表 双向线性链表 双向 ...

  3. 数据结构笔记01:编程面试过程中常见的10大算法(java)

    以下是在编程面试中排名前10的算法相关的概念,我会通过一些简单的例子来阐述这些概念.由于完全掌握这些概念需要更多的努力,因此这份列表只是作为一个介绍.本文将从Java的角度看问题,包含下面的这些概念: ...

  4. Redis入门笔记-redis内部数据结构(01)

    redis是一个轻量级的Nodsql数据库,使用kev-value的形式存储数据,在redis的世界里,没有整数.浮点数等概念,大多数情况下数据以字符串形式展现,偶尔会出现Long类型数据的场景. 一 ...

  5. java数据结构复习01

    1.数组 package javaDataStruct.array01; public class MyArray { private int[] arr; // 表示有效数据的长度 private ...

  6. 解惑结构体与结构体指针(struct与typedef struct在数据结构的第一道坎)

    /* 数据结构解惑01  在数据结构中会看到 typedef struct QNode { QElemType data; //数据域 struct QNode *next; //指针域 }QNode ...

  7. sphinx教程

    http://www.php100.com/html/it/focus/2013/0916/6188.html### 以上一篇的email数据表为例: 数据结构: 01.CREATE TABLE em ...

  8. Golang通过Thrift框架完美实现跨语言调用

    每种语言都有自己最擅长的领域,Golang 最适合的领域就是服务器端程序. 做为服务器端程序,需要考虑性能同时也要考虑与各种语言之间方便的通讯.采用http协议简单,但性能不高.采用TCP通讯,则需要 ...

  9. Java多线程系列--“JUC集合”04之 ConcurrentHashMap

    概要 本章是JUC系列的ConcurrentHashMap篇.内容包括:ConcurrentHashMap介绍ConcurrentHashMap原理和数据结构ConcurrentHashMap函数列表 ...

  10. Java多线程系列--“JUC集合”05之 ConcurrentSkipListMap

    概要 本章对Java.util.concurrent包中的ConcurrentSkipListMap类进行详细的介绍.内容包括:ConcurrentSkipListMap介绍ConcurrentSki ...

随机推荐

  1. CGAL的demo运行的步骤

    首先使用CMake,找到demo的源文件目录,并且指定生成文件目录: 点击configur,done 点击generate,done 找到build目录中的.sln 文件,打开 ALL_BUILD 生 ...

  2. shell多进程并发数控制

    在批量执行任务时,单进程执行速度太慢,使用&不加数量控制,又担心资源占用过多,导致宕机等问题,因此我们需要控制并发进程的数量,保证效率的同时,保证资源占用不会太高. 其中一个解决思路是利用简单 ...

  3. django+ajax实现xlsx文件下载功能

    前端代码 $("#id_pullout").click(function () { //发送ajax请求 $.ajax({ url: '/pullout/', //请求的url m ...

  4. 《MySQL是怎样运行的》第五章小结

  5. 二叉树、B树、B*树、AVL树... 这么多树你真的搞清楚了吗?

    经常在面试或者平时工作中,我们都会听到类似的树,类似于二叉树.B树.B*树.AVL树等等,很多情况下可能对他们都是只有一知半解.今天我总结了所有常见的树的原理,深入浅出的分析了其中的优缺点和注意事项, ...

  6. Java笔记第十一弹

    TCP通信程序 TCP发送数据 //需要进行三次握手 import java.io.*; public class Main{ public static void main(String[] arg ...

  7. MyBatis 重点知识归纳

    一.MyBatis 简介 [1]MyBatis 是支持定制化 SQL,存储过程以及高级映射的优秀持久化框架.[2]MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取查询结果集.[3 ...

  8. H5 visibilityChange事件 --- 监听页面的显示或者隐藏 新开一个webview

    mounted() { document.addEventListener('visibilityChange', 事件处理函数) }, destoryed() { document.removeEv ...

  9. [C++STL教程]1.vector容器是什么?实用教程来啦!超简单易懂,拿来就用

    C++与传统的C语言有一个很大的区别,就是新增了标准模板库 STL(Standard Template Library),它是 C++ 标准库的一部分,不需要单独安装,只需要 #include 对应的 ...

  10. 6.sql注入

    sql注入 目录 sql注入 1.SQL注入原理 2.如何判断注入点 联合注入 报错注入(有错误报出) 布尔盲注(不管输入什么,界面只会出现两种结果) 时间盲注(不管输入什么,界面都是一样的) 堆叠注 ...