数据结构是指相互间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成. 简单的来说,数据结构就是计算机储存,组织数据的方式.

程序=数据结构+算法

数据结构按照其逻辑结构可分为线型结构,树结构,图结构

  线性结构:数据结构中的元素存在一对一的相互关系 .  典型的有 数组,栈,队列,线性表.

  树结构:数据结构中的元素存在一对多的相互关系    典型的有树和堆

  图结构:数据结构中的元素存在多对多的相互关系

 内存的存储方式

存储器:主要是针对内存而言的。

需要存储多项数据时,有两种基本的方式数组链表

数组

python列表的底层数据结构就是数组。

使用数组: 意味着所有的要存储的数据在内存中都是相连的。是把数据元素存放在地址连续的储存单元里

数组和列表的区别:

数组:

  1. 可以访问数组中的每一项,查询数组的长度,
  2. 不能增加或删除某个位置的项,或者让数组的长度变大变小。

数组的特点:

  1. 支持随机访问
  2. 连续内存
  3. 支持动态内存。  有一种方法可以在运行时根据应用程序的数据需求来调整数组的长度,这种调整可以采取如下的3种方式
    1. 在程序开始的时候创建一个具有合理默认大小的数组
    2. 当这个数组不能保存更多的数据的时候,创建一个新的‘更大的数组,并且从旧的数组中转移数据项
    3. 当数组似乎存在浪费的内存的时候,以一种类似的方式来减小数组的长度’

数组的操作

数组大小的增加或减小

主要通过以下三个步骤:

  1. 创建一个新的‘更大的数组
  2. 将数据从旧的数组中复制到新的数组中。
  3. 将旧的数组变量重新设置为新的数组对象。

在数组中插入或删除一项

向数组中插入一项和替代数组中的一项是不同的,替代数组只是在给定的数组索引位置上重新赋值就够了,数组的逻辑大小并不会改变。但是插入程序员就必须做如下4件事情。

  1.  检查可用的空间
  2. 从数组的逻辑末尾开始d,知道目标索引位置,将每一项都向后移动一个单元
  3. 将新的赋值给目标索引项
  4. 将逻辑大小增加1

链表

链表:是把数据元素存放在任意的存储单元里,这组储存单元可以是连续的也可以是不连续的。

链表的基本单位是节点。

单链接的节点包含了以下信息:

  1. 一个数据项
  2. 到结构中的下一个节点的连接地址

除了这些部分外,双链接还包含了到结构中的前一个节点的链接地址。

使用链表:链表中的元素可以储存在内存中的任意地方.链表中的每个元素都储存了下一个元素的地址,从而使一系列的内存地址串连接在一起.

使用链表时,插入数据和删除数据很方便,只需要修改前边那个元素的指向地址.但是对于查询数组需要使用顺序访问的方式,速度慢

数组用得很多,因为它支持随机访问。
有两种访问方式:随机访问和顺序访问。
顺序访问意味着从第一个元素开始逐个地读取元素。链表只能顺序访问:要读取链表的第十个元素,得先读取前九个元素,并沿链接找到第十个元素。
随机访问:不管数组有多大,它访问第1项所需要的时间和访问最后1项的时间是相同的,计算机通过为数组项分配一段连续的内存单元,从而支持对数组的随机访问。数组的某一项的地址可以通过数组的基本地址和项的偏移地址
相加的出来的 a

 

 

 建立单链表的方法:

 

手动建立链表的方法

class Node:
    """
    节点类
    """
    def __init__(self,item):
        self.item=item #当前的数据
        self.next=None #下一个数据对象


c=Node(1)
d=Node(2)
f=Node(3)
c.next=d
d.next=f  ##这样就建立列表


print(c.next.item)


###结果为
2

 

用函数来创建列表:

 

分为两种方法: 头插法和尾插法,头插法就是插入的元素放到列表元素的最前面,尾插法就是插入的元素放到列表的最后面

 

头插法:

#头插法创建链表
class Node:
    def __init__(self,item):
        self.item=item
        self.next=None

def create_linklist(li):
    head=None#head指的是列表中头结点,头结点的数据域不存任何值,指针指向第一个节点
    for num in li:
        p=Node(num)
        p.next=head
        head=p
    return head 
link1=create_linklist([1,2,3,4,5]) 

print(link1.item)
print(link1.next.item)




#输出5,4,3,2,1    

结果:

5
4


#分析过程 当元素1先插入链表中时,p=Node(1)---> p.next=head=None ---> head=p ---》 p= Node(2) ----> p.next=p  ----> head=p 

最后返回的是头结点,

 

尾插法:

#尾插法
def create_linklist_tail(li):
    """
    尾插法
    :param li:
    :return:
    """
    if len(li)>0:
        head=Node(li[0])#head头节点,头结点指向第一个节点,数据域不存任何值,指针指向第一个节点
        tail=head#tail尾部节点
        for num in li[1:]:
            p=Node(num)
            tail.next=p#上部的tail=head
            tail=p
        return head #返回头结点
    else:
        return None

link=create_linklist_tail([1,2,3,4,5])
print(link.next.item)

 

结果:

1

 

节点的插入

比如现在有两个节点 p1-->p2 ,现在有一个节点p3要插到p1的后边,得到的结果为 p1---> p3-->p2

因为我们要断开p1和p2的连接,首先我们要先接收p2,不能直接 p1.next=p3  p3.next=p2,要先连接p2,再连接p1

p3.next=p2

p1.nest=p3

这样就可以了。

节点的删除:

现在要删除掉p3, 结果为 p1---> p2

p1.next=p1.next.next  这样就把p3删除了

 

单链表的反转

1->2->3->4->5 变成  1<-2<-3<-4<-5 

class Node:
    def __init__(self, data=None, next_node=None):
        """

        :param data: 第一个是数字,
        :param next_node: next是节点(包含data,next_code)
        """
        self.data = data
        self.next_node = next_node


def reverseNode(Node):
    """

    :param Node: 节点是一个对象
    :return:
    """
    if Node == None or Node.next_node == None:
        return Node

    # 递归反转子链链表
    new_node = reverseNode(Node.next_node)
    Node.next_node.next_node = Node  # 这步的作用就是有4->5 变成4->5->4
    Node.next_node = None  # 这步的作用就是 4->None   5->4  避免形成一个闭环   这里画个图很好理解的

    return new_node


# 1->2->3->4
if __name__ == '__main__':
    link = Node(1, Node(2, Node(3, Node(4))))
    print(link.data)
    print(link.next_node.data)
    print(link.next_node.next_node.data)
    print(link.next_node.next_node.next_node.data)
    new = reverseNode(link)
    print(new.data)
    print(new.next_node.data)
    print(new.next_node.next_node.data)
    print(new.next_node.next_node.next_node.data)

 

结果:

1
2
3
4


4
3
2
1

对该问题很好的解释的一篇文章

https://zhuanlan.zhihu.com/p/60117407

 

 

 

 

双链表:

 

 

哈希表(散列表)

 

哈希冲突

 由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突

 解决哈希冲突的方法:

 第一:开放寻址法

#哈希函数   
H(Key) = Key%7

 

 

开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
线性探查:如果位置i被占用,则探查i+1, i+2,……   H(Key) = (Key+1)%7
二次探查:如果位置i被占用,则探查i+1^2,i-1^2,i+2^2,i-2^2,……
二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2,h3,……

第二:拉链法

 

拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后

 

哈希表的应用

1.在python中的应用
字典与集合都是通过哈希表来实现的
2.投票防止相同的人再次投票
3.缓存.缓存是一种常用的网站加速的方式,而缓存的数据则存放在哈希表中.

 

哈希表和数据和列表的时间复杂度的对比

posted on 2018-05-03 20:05  程序员一学徒  阅读(158)  评论(0)    收藏  举报