数据结构与算法之线性结构

什么是数据结构

数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系的组成。python

  • 数据结构就是设计数据以何种方式存储在计算机中,列表、字典等都算是数据结构。linux

  • 程序=数据结构+算法,数据结构属于静态的部分,算法的调用为动态部分算法

数据结构的分类

根据逻辑结构划分:编程

  • 线性结构:数据结构中的元素一对一的关系,一前驱,一后继。
  • 树结构:数据结构中元素一对多的关系,一前驱,多后继。
  • 图结构:数据结构中元素存在多对多的关系,多前驱,多后继,我也不会。
    • 判断一个图形能不能一笔画完,就判断它的奇数度节点数目是否为0或2.这种能一笔画完的就是欧拉图,奇数度节点为四个,就是两笔画完。

线性结构

列表

列表和数组

python中的列表和其余语言中的数组很类似,区别为:数组

  • 数组是定长的。
  • 数组的数据类型也必须一致。
  • 对列表或数组来讲,它们的下标操做是最快的。

列表解决的变长问题的方式

  • 假设一开始在内存中分配了四个元素存储的空间,那么前四个元素的append操做不会出现问题。
  • 当第五次append操做时,会先在内存中分配一个可以存储八个元素的空间,也就是翻倍。
  • 而后进行复制,把之前的四个元素依次放到相应的位置上。
  • 若再次超出长度,则继续执行上述操做。
  • 也就是使用了动态表的原理

append操做会不会使速度变慢?安全

  • 根据摊还分析,没有变长时的append和变长时的append均摊,最后的复杂度时O(3).
  • append越日后,变长时的出现频率就会越小
  • 浪费了一部分空间,最坏状况应该是浪费了长度除二减一的空间。

列表解决多数据类型问题的方式

  • 对于纯整数的数组,它的每个元素占4个字节,那么就事先计算好内存分配的大小,计算方法为:- 第一个元素的地址+元素个数 乘 4
  • python的列表里存的不是值,而是指向这个值的内存地址。
  • 地址的大小是同样的,32位里地址是4个字节,64位里地址是8个字节。
  • 这种方法的缺点是内存开销翻倍,这也是python被人诟病的地方。

相关知识点

老是能听到一个词 堆栈 ,堆(heap)和栈(stack)是两个东西,传统的编程语言中把内存分为两个地方,堆空间和栈空间,堆存储的是一些动态生成的对象,与数据结构中的堆是不一样的,栈空间由系统调用,存放函数的参数值,局部变量的值。
应该是早年间翻译的问题,通常听到堆栈指的就是栈。数据结构

  • 栈是一个数据集合,能够理解为只能在一端进行插入和删除操做的列表。
  • 栈的特色:后进先出(last-in,first-out)
    • 栈顶:操做永远在栈顶。
    • 栈底:最后一个元素。
  • 栈的基本操做:
    • 进栈(压栈):push
    • 出栈:pop
    • 取栈顶: gettop
  • 关于出栈顺序的问题:
    • 对于某个元素,若是进展顺序在它前面的元素出栈时在它后面,那么前面的元素顺序是相反的。
    • 不知道说的明不明白
    • 卡特兰数,n个数的出栈顺序,就是卡特兰数的第n项。

栈的应用--括号匹配问题

  • 给定一个字符串,问其中字符串是否匹配。
  • 括号自己知足栈的性质
  • 匹配失败的状况:
    • 括号不匹配
    • 匹配完毕栈没空
    • 栈空了又进元素
     
    1. def brace_match(s):app

    2. stack = []编程语言

    3. d ={'(':')','[':']','{':'}'}函数

    4. for ch in s:

    5. if ch in {'(','[','{'}:

    6. stack.append(ch)

    7. elif len(stack):

    8. print('多了%s' %ch)

    9. return False

    10. elif d[stack[-1]] == ch:

    11. stack.pop()

    12. else:

    13. print('%s不匹配'%ch)

    14. if len(stack)==0:

    15. return True

    16. else:

    17. print("未匹配")

    18. return False

队列

相关知识点:

队列是一个数据集合,仅容许在列表的一端插入,另外一端删除。

  • 进行插入的时队尾,进行删除操做的是队首,插入和删除操做也被称为进队(push)和出队(pop)。
  • 队列的性质:先进先出(first-in,first-out)
  • 双向队列:两边都能进行插入删除操做的队列。

队列的数组实现:

  • 简单的pop(0)操做复杂度太高,不采用。
  • 因为数组定长,不能继续添加数据,若是是列表,出队的操做就会出现空位,因此想办法让数组变成一个圆环。

  • 设置两个指针,队首指针front,队尾指针rear。
  • 因为,队列满的时候和队列空的时候rear和front都在一个位置,那么就没法判断了。因而设置成队列满的时候减去一作为队满的标志。
  • 这种队列就叫作环形队列。
    • 当队尾指针front=最大长度+1时,再前进一个位置就自动到0.
    • 实现方式:求余数运算
      • 队首指针前进1:front=(www.tiaotiaoylzc.com front+1)%maxsize
      • 队尾指针前进1:rear=(www.yongshi123.cn rear+1)%maxsize
      • 队空条件:rear=www.yongshiyule178.com front
      • 队满条件:(rear+1)www.dfgjpt.com%maxsize=front

经过两个栈作一个队列的方法

  • 1号栈进栈 模拟进队操做。
  • 2号站出栈,若是2号栈空,把1号站依次出栈并进2号栈,模拟出队操做。
  • 经过摊还分析,时间复杂度仍是O(1)。

python关于队列的模块

 
  1. import queue #涉及线程安全用queue

  2. from collections import deque #经常使用解题的用deque

  3.  
  4. q = deque() #是一种双向队列,popleft出队

  5.  
  6. #模拟linux命令 head和tail,假如是tail 5

  7. deque(open('a.text','r',encooding='utf8'),5)

  8. #创建一个定长的队列,当队列满了以后,就会删除第一行,继续添加

链表

相关知识点:

链表就是非顺序表,与队列和栈对应。

  • 链表中每个元素都是一个对象,每一个对象称为一个节点,包含有数据域key和指向下一个节点的next,经过各个节点之间的相互链接,最终串联成一个链表。

  • 在机械硬盘中,文件就是以链表的形式存储的。
  • 以FAT32为例,文件的单位是文件块(block),一个文件块的大小是4k,一个文件的内容是由链表的方式链接文件块组成的。
  • 链表的第一个节点被称为头节点,数据能够是空的,也能够有值。
  • 头节点为空也是为了表示空链表,也叫作带空节点的链表,头节点也能够记录链表的长度

节点定义

 
  1. class Node(object):

  2. def __init__(self,item):

  3. self.item=item

  4. self.next=None

  5. #eg

  6. a=Node(1)

  7. b=Node(2)

  8. c=Node(3)

  9. a.next=b

  10. b.next=c #链表的最后一个节点的next就为None

链表类的实现

 
  1. class LinkList:

  2. def __init___(self,li,method='tail'):

  3. self.head = None

  4. self.tail = None

  5. if method == 'head':

  6. self.create_linklist_head(li)

  7. if method == 'tail'

  8. self.create_linklist_tail(li)

  9. else:

  10. rais ValueError('unsupport')

  11.  
  12. #头插法

  13. def create_linklist_head(self,li):

  14. self.head = Node(0)

  15. for v in li:

  16. n = Node(v)

  17. n.next = l.next #当插入下一个元素时,应该与下一个节点链接后再跟头节点链接

  18. self.head.next = n

  19. self.head.data += 1

  20.  
  21. #尾插法

  22. def create_linlist_tail(self,li):

  23. self.head = Node(0)

  24. self.tail = self.head

  25. for v in li:

  26. p = Node(v)

  27. self.tail.next = p

  28. self.tail = p

  29. self.head.data += 1

  30.  
  31. #链表的遍历输出

  32. def traverse_linlist(self):

  33. p = self.head.next

  34. while p:

  35. yield p.data

  36. p = p.next

插入删除总结

  • 插入
 
  1. #p表示待插入节点,curNode表示当前节点

  2. p.next = curNode.next #不能当前链接直接断开

  3. curNode,next = p

  • 删除
 
  1. p = curNode.next

  2. curNode.next = p.next

  3. del p #不写也同样,引用计数,python的内存回收机制

双链表

双链表中每一个节点有两个指针:一个指向后面节点、一个指向前面节点。
节点定义:

 
  1. class Node(object):

  2. def __init__(self, item=None):

  3. self.item = item

  4. self.next =www.myzx1.com None

  5. self.prior = None

双链表的插入和删除

  • 插入
 
  1. p.next = curNode.next

  2. curNode.www.ycjszpgs.com next.prior = p

  3. p.prior =www.dfzx157.com curNode

  4. curNode.next = p

  • 删除
 
  1. p = curNode.next

  2. curNode.next = p.next

  3. p.next.prior = curNode

  4. del p

链表的复杂度分析

链表与列表相比

  • 按元素值查找:列表可使用二分法是O(logn),链表是O(n)
  • 按下标查找:O(1),O(n)
  • 再某元素后插入:O(n),O(1)
  • 删除莫元素:O(n),O(1)
    总的来讲链表再插入和删除某元素的操做时明显快于顺序表,并且经过双链表能够更容易实现栈和队列。

哈希表

直接寻址表

哈希表就是直接寻址表的改进。当关键字的全域U比较小时,直接寻址是一种简单有效的方法。

  • 全域的意思就是它的取值范围。
  • 也就是直接把关键字为key的value放在key的位置上
    直接寻址的缺点:
  • 当域U很大时,须要消耗大量内存。
  • 若是U很大,但关键字不多,浪费大量空间。
  • 若关键字不是数字则没法处理。
    直接寻址表的改进:
  • 构建大小为m的寻址表T
  • key为k的元素放到h(k)上
  • h(k)是一个函数,其将域U映射到表T(0,1,..,m-1)

哈希表

哈希表是一个经过哈希函数计算数据存储位置的线性表的存储结构,又叫作散列表。

  • 哈希表由一个直接寻址表和一个哈希函数组成。
  • 哈希函数h(k)将元素关键字k做为自变量,返回元素的存储下标。
  • 哈希表的基本操做:
    • insert(key,value):插入键值对。
    • get(key):若是存在键为key的键值对则返回其value。
    • delete(key):删除键为key的键值对。

简单哈希函数

  • 除法哈希:h(k)= k mod m
  • 乘法哈希:h(k) = floor(m(KA mod 1)) 0<A<1

哈希冲突

因为哈希表的大小是有限的,而要存储信息的数量是无限的,所以,对于任何哈希函数,都会出现两个元素映射到同一个位置的状况,这种状况就叫作哈希冲突。
解决哈希冲突的方法:
开放寻址法:若是哈希函数返回的位置已经有值,则能够向后探查新的位置来储存这个值。

  • 线性探查:若是位置p被占用,则探查 p+1,p+2....
  • 二次探查:若是位置p被占用,则探查p+1**2,p-1**2,p+2**2
  • 二度哈希:有n个哈希函数,当使用第一个哈希函数h1发生冲突时,则使用h2。
  • 哈希表的快速查找能够以空间换时间,须要保证元素个数除以数组容积小于0.5,这个比值就是装载率。
    拉链法:哈希表的每一个位置都链接一个链表,当冲突发生时,冲突的元素被加到该位置链表的最后。
  • 拉链表须要保证每个链表的长度都不要太长。
  • 拉链法的装载率是能够大于一的。
  • 插入、查找等操做的时间复杂度是O(1)的。

哈希在python中的应用

  • 字典和集合都是经过哈希表来实现的
  • 集合能够看做没有value的字典,由于集合也有不重复的性质。
  • 经过哈希函数把字典的键映射为函数:
 
  1. dic = {'name':'cui'}

  2. #能够认为是h('name')=1,则哈希表为[None,'cui

相关文章
相关标签/搜索