数据结构和算法-数据结构-线性结构-顺序表 链表和哈希表

#######################################################

"""
# 线性表是最基本的数据结构之一,在实际程序中应用非常广泛,它还经常被用作更复杂的数据结构的实现基础。

# 根据线性表的实际存储方式,分为两种实现模型:
# 顺序表,
# 链表,
# 下面分别进行研究,


"""

#######################################################

"""

# 顺序表的研究
# 顺序表的基本形式,数据元素本身连续存储,

# 第一种情况:
# 如果顺序表的存储元素都是固定大小的,只需要一块空间,
# 第二种情况,
# 但是存储的每一个元素的大小不是全部固定的,比如列表中,可以是存储数字,但是也有字符串,等,
# 所以可以每一个值都是放到另外一个存储块中存储,然后把每一个存储块的地址按照顺序表存起来,
# 这种取值的时候,需要先找到地址,然后通过地址去找到存储块,找到具体的内容,

# 顺序表的实现,
# 比如存储8个元素,这个存储空间是一次性的向操作系统申请到的,
# 所以一个顺序表,除了要保存具体的存储内容以外,还需要一个表头,
# 表头需要两个信息一个是容量是多大也就是最大的存储信息,还有一个就是现在的存储内容,也就是现在存储了多少个元素了,
# 怎么把表头和信息存储到一起?
# 一种是存储在一起,这叫做一体式的存储,
# 一种是分开存储,这叫做分离式存储,在存储表头的时候加一个信息,就是存储内容的地址,也就是表头有三个了,容量,目前的存储数量,存储信息的地址,


# 一体式的存储和分离式存储,哪一个好?
# 读取:一体式的存储,直接跳过表头就可以读取了,分离式存储是多一步,先从表头获取到地址信息,然后到地址获取具体信息,
# 扩充数据:
# 一体式的存储容量是固定的,如果要多存储数据,必须要重新申请空白的空间,然后数据搬过来,表头也要搬过来,
# 分离式存储,表头不需要改,只需要地址指向新的地址就可以了,
# 所以为了考虑到数据的动态变化,还是更多的采用的是分离式的存储,

# 扩充的时候如何预估申请的空间呢?
# 比如原来的空间只能存储4个元素,为了存储第5个元素, 我需要重新申请空间,
# 第一种方式,固定申请,比如每次再申请10个空间,就是14个,这种节省空间,但是操作频繁,
# 第二种方式,加倍的方式,一开始4个,下一次就是翻倍,就是8个,这种空间牺牲一些, 但是时间会更有效率,这就是空间换取时间,

# 顺序表的弊端也很明显了,就是每次扩充数据,都需要重新申请空间,那么有没有一种结构,多一个数据,就往上加,而不是重新申请空间呢?
# 那就是链表了


"""

 #######################################################

"""
# 链表的研究
# 一个列表,需要存储3个元素,200,300,400
# 不用顺序表,这种受限,所以采用每加一个元素就申请一个空间,然后在每一个元素的后面新开一个空间,用来指向另外一个元素,
# 这样层层链接,就可以了,这种就是链表,

# 链表如何实现:
# 链表中每一个元素都是一个对象,每一个对象都是一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的互相连接,最终串联成一个列表 # 每一个元素保持两个内容,准确来讲不能叫做元素了,而是叫做节点, # 一个是信息,叫做数据区, # 一个是下个元素的链接地址,这叫做链接区,尾结点指向的区域就是指向空, # 单向链表, # 第一节点是头节点,第二个节点是尾结点, # Python中交换变量的操作, # a=10 # b=10 # a,b=b,a # 这就是交换,这只在Python中有,别的语言没有, # 为什么能达到这个效果,本质是什么? # a是一个空间,10是一个空间,a的空间指向10的空间, # 所以这种交换,就是指向的变化,所以不是真正的赋值 # 所以要记住,Python中的变量不是保存的内容,而是保持的地址,地址指向的地方保存的才是具体的信息, # 所以a只是等于数字,字符串,列表等,这就是因为这个变量就是一个指向,这是Python和其他语言的差异,
"""

 #######################################################

"""

实现单向链表,
逻辑:
1. 因为链表的每一个节点,需要保存数据和链接,我们自己定义一个数据类型叫做节点类型来保存这两个信息,
2. 定义一个单链表类,初始是没有节点的,head是空的
3,实现几个方法
判断是否为空,
返回链表长度
遍历
头部添加,
尾部添加
中间添加
删除节点,
查找,
"""

##################     python实现单向链表       #######################

class SingleNode(object):
    # 里面有两个元素
    def __init__(self,elem):
        self.elem=elem
        self.next=None  # 因为一开始只有一个元素,我不知道下一个元素是什么,
    

# 第一步:创建一个新的链表
# sll=SingleLinkList(),这里面是空的,没有任何的节点,head----指向None,
# 第二步:现在链表有了,要保存一个整数值100,就要构造一个节点,
# node=SingleNode(100)  # 这里面包含两个值,就是100,none,然后链表的head就不是none,就是第一个节点,
class SingleLinkList(object):
    """单链表"""
    def __init__(self,node=None):
        self.__head = None  # 私有属性    设置成空,就是说这个链表里面没有任何的节点,先

    def is_empty(self): """判断链表是否为空""" return self.__head == None # 只要链表的head是none就是空的链表, # 如何实现这个长度,因为链表是 head----100,next---20,next---300,next---none # 所以遍历这个链表,如果next是none就到头了, def length(self): """链表长度""" # cur游标,用来移动遍历节点, cur = self.__head # count用来计数, count = 0 # 尾节点指向None,当未到达尾部时 while cur != None: count += 1 cur = cur.next # 将cur后移一个节点,这一句很关键, return count def travel(self): """遍历链表""" cur = self.__head while cur != None: print(cur.elem,end=" ") cur = cur.next # 在头部插入数据的逻辑 # 第一步,把新元素的next指向第一个节点, # 第二步,把head指向这个新元素 # 这个逻辑也是可以处理空链表的情况, def add(self, item): """头部添加元素""" # 先创建一个保存item值的节点 node = SingleNode(item) # 将新节点的链接域next指向头节点,即_head指向的位置 node.next = self.__head # 这个__head是原来的头部节点, # 将链表的头_head指向新节点 self.__head = node def append(self, item): """尾部添加元素""" node = SingleNode(item) # 先判断链表是否为空,若是空链表,则将_head指向新节点 if self.is_empty(): self.__head = node # 若不为空,则找到尾部,将尾节点的next指向新节点 else: cur = self.__head while cur.next != None: cur = cur.next # 这个是一个属性,保存了一个节点类对象了,通过一通遍历,就把cur变成了尾结点了, cur.next = node # 这就是把尾结点的指针指向下一个节点, # 通过坐标插入的实现逻辑 # inset(2,300) # 第一步,让新的节点,指向原来节点是2的节点, # 第二步,让左边是1的节点,指向新的节点, # 因为传过来的坐标是用户使用的,所以需要控制, # 1,如果是传入的0 # 2,如果是传入的比长度还要大 def insert(self, pos, item): """指定位置添加元素""" # 若指定位置pos为第一个元素之前,则执行头部插入 if pos <= 0: self.add(item) # 若指定位置超过链表尾部,则执行尾部插入 elif pos > (self.length() - 1): self.append(item) # 找到指定位置 else: node = SingleNode(item) count = 0 # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置 pre = self.__head while count < (pos - 1): count += 1 pre = pre.next # 如果pos是2,那这个pre.next就是原来坐标是2的节点, node.next = pre.next # 新节点node的next指向插入位置的节点 pre.next = node # 将插入位置的前一个节点的next指向新节点 # 传入一个数据,需要删除具体的数据, # 实现逻辑, # 需要把删除的节点的上一个节点的指针指向下一个节点,让这个链表连接起来, # 用两个游标来解决这个问题, # 1,cur游标指向head # 2,pre游标指向cur, # 3,cur指向cur.next def remove(self, item): """删除节点""" cur = self.__head pre = None while cur != None: if cur.elem == item: # 找到了要删除的元素 if cur == self.__head: # 如果找到的要删除的节点正好是第一个节点,怎么判断是第一个节点?:cur == self.__head self.__head = cur.next # 直接是让head指向被删除节点的next else: # pre.next = cur.next # 将删除位置前一个节点的next指向删除位置的后一个节点,这样的写法即使只有一个节点,也能实现, break else: # 继续按链表后移节点,这两句就是把两个游标都往后移动, pre = cur cur = cur.next # 实现逻辑 # 还是需要遍历列表的,判断节点的数据和用户要找的数据,是否相等,如果相等返回true,否则就是false, def search(self, item): """链表查找节点是否存在,并返回True或者False""" cur = self.__head while cur != None: if cur.elem == item: return True cur = cur.next return False if __name__ == "__main__": ll=SingleLinkList() print(ll.is_empty()) print(ll.length()) ll.append(1) print(ll.is_empty()) print(ll.length()) ll.append(2) ll.append(3) ll.append(4) ll.append(5) ll.add(9) ll.insert(-1,200) ll.insert(1,300) ll.insert(11,400) ll.remove(400) ll.travel()

##################################################

"""

# 双向链表
# 每一个节点需要保存三个内容了,上一个节点的地址,本个节点的数据,下一个节点的地址,
# 这样就有一个后继节点和前驱节点,
# 对于头结点的前驱节点是空,对于尾节点的下一个节点是空,
 

"""

##################     python实现双向链表        #######################

class Node(object):
    """双向链表节点"""
    def __init__(self, item):
        self.item = item
        self.next = None
        self.prev = None


class DLinkList(object): """双向链表""" def __init__(self): self.__head = None def is_empty(self): # 和单链表是一样的, """判断链表是否为空""" return self.__head is None # self.__head == None你可以使用双等号,但是pep8推荐使用is, def length(self): # 和单链表是一样的, """返回链表的长度""" cur = self.__head count = 0 while cur != None: count += 1 cur = cur.next return count def travel(self): # 和单链表是一样的, """遍历链表""" cur = self.__head while cur != None: print(cur.item) cur = cur.next # 这个和单链表不一样, # 你在头部新增了一个节点 # 第一步,新的节点的next指向下一个节点 # 第二步,新节点的等于head # 第三步,原来的头部节点的prev指向新的节点, def add(self, item): """头部插入元素""" node = Node(item) if self.is_empty(): # 如果是空链表,将_head指向node self._head = node else: node.next = self.__head # 空链表的时候self.__head还是一个none,所以next是等于none的, self.__head=node # 让头部节点等于这个节点, node.next.prev = node # node.next这是原来的头节点,他的prev指向新节点 # 这个和单链表不一样, # 第一步,原来的尾结点的next指向新的节点, # 第二步,新的节点的prev,指向原来的尾结点, def append(self, item): """尾部插入元素""" node = Node(item) if self.is_empty(): # 如果是空链表,将__head指向node self.__head = node else: # 移动到链表尾部 cur = self.__head while cur.next != None: cur = cur.next # 这一步就是移动到尾节点 # 将尾节点cur的next指向node cur.next = node # 将node的prev指向cur node.prev = cur # 这个插入,和单链表不一样, # 第一步,新的节点的next指向原来位置的节点,node.next = cur # 第二步,新的节点的prev指向原来位置的上一个节点cur.prev,node.prev = cur.prev # 第三步,原来位置的上一个节点的next指向新的节点,node,prev.next = node # 第四步,原来位置的prev指向新的节点,cur.prev = node # 代码不是唯一的,但是要理清楚,这种可能是面试题,可以画图理解, def insert(self, pos, item): """在指定位置添加节点""" if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: cur = self._head count = 0 # 移动到指定位置的前一个位置 while count < pos: count += 1 cur = cur.next # 退出循环的时候,cur指向pos位置 node = Node(item) node.next = cur # 第一步,新的节点的next指向原来位置的节点 node.prev = cur.prev # 第二步,新的节点的prev指向原来位置的上一个节点 node.prev.next = node # 第三步,原来位置的上一个节点的next指向新的节点 cur.prev = node # 第四步,原来位置的prev指向新的节点 # 和单链表的逻辑不一样, # 第一步:让删除节点的上一个节点的next,指向删除节点的下一个节点,cur.prev.next = cur.next # 第二步,让删除节点的下一个的prev,指向删除节点的上一个节点,cur.next.prev = cur.prev # 如果是首节点, # 第一步:让head等于当前节点的下一个节点 # 第二步:把当前节点的下一个节点的prev变成none, # 只有一个节点的时候, # 直接让head等于当前节点的next,因为当前节点的next是none, def remove(self, item): """删除元素""" cur=self.__head while cur != None: if cur.item == item: if cur ==self.__head: self.__head=cur.next if cur.next: # 这是专门处理只有一个节点的时候,因为只有一个节点,cur.next.prev这是没有值的, cur.next.prev=None else: cur.prev.next = cur.next # 将cur的前一个节点的next指向cur的后一个节点 if cur.next: cur.next.prev = cur.prev # 将cur的后一个节点的prev指向cur的前一个节点 break else: cur=cur.next def search(self, item): """查找元素是否存在""" cur = self.__head while cur != None: if cur.item == item: return True cur = cur.next return False if __name__ == "__main__": ll = DLinkList() ll.add(1) ll.add(2) ll.append(3) ll.insert(2, 4) ll.insert(4, 5) ll.insert(0, 6) ll.travel() ll.remove(1) ll.travel()

##################     python实现单向循环链表        #######################

# 单向循环链表,
# 和单向链表的唯一区别在于尾结点的next指向了头结点了,


class Node(object):
    """节点"""
    def __init__(self, item):
        self.item = item
        self.next = None


class SinCycLinkedlist(object):
    """单向循环链表"""
    def __init__(self,node=None): self.__head = None if node: node.next=node # 只有一个元素也要指向自己, def is_empty(self): # 和单链表一样, """判断链表是否为空""" return self.__head == None def length(self): """返回链表的长度""" # 如果链表为空,返回长度0 if self.is_empty(): return 0 count = 1 cur = self.__head while cur.next != self.__head: # 判断尾部的条件变了,如果一个节点的next是头部节点,这就是尾结点了, count += 1 cur = cur.next return count def travel(self): """遍历链表""" if self.is_empty(): return cur = self.__head while cur.next != self.__head: print(cur.item) cur = cur.next # 退出循环,cur指向尾结点,但是尾结点的元素未打印, print(cur.item) # 对于空节点,一个节点的情况也是能满足的, # 逻辑 # 第一步,把新节点的next指向原来的头节点 # 第二步,把头结点指向到新节点, # 第三步,把尾结点的next指向头结点, # 如果是一个空的链表, def add(self, item): """头部添加节点""" node = Node(item) if self.is_empty(): self.__head = node node.next = self.__head else: node.next = self.__head # 添加的节点指向__head cur = self.__head # 这是设置游标 while cur.next != self.__head: cur = cur.next # 不断的往后移动 cur.next = node # 退出循环,cur就是指向了尾结点,将尾部节点的next指向node self.__head = node # 把头结点指向到新节点, # 逻辑 # 第一步,把新的节点的next指向head, # 第二步,把原来的尾结点的next指向新节点, def append(self, item): """尾部添加节点""" node = Node(item) if self.is_empty(): self.__head = node node.next = self.__head else: # 移到链表尾部 cur = self.__head while cur.next != self.__head: cur = cur.next cur.next = node # 将尾节点指向node node.next = self.__head # 将node指向头节点__head # 逻辑和单链表是一样的, def insert(self, pos, item): """在指定位置添加节点""" if pos <= 0: self.add(item) elif pos > (self.length()-1): self.append(item) else: node = Node(item) cur = self.__head count = 0 # 移动到指定位置的前一个位置 while count < (pos-1): count += 1 cur = cur.next node.next = cur.next cur.next = node # 逻辑 # 如果删除的是头结点, # 第一步,head指向删除节点的下一个节点 # 第二步,尾结点的next指向新的head, # 如果不是头结点, # 和单链表一样,直接把删除节点上一个节点的next指向,删除节点的下一个节点, def remove(self, item): """删除一个节点""" # 若链表为空,则直接返回 if self.is_empty(): return cur = self.__head pre = None while cur.next != self.__head: if cur.item==item: if cur.next == self.__head: # 这是头结点的情况, #如果是头结点还是需要遍历一遍, rcur=self.__head while rcur.next != self.__head: rcur=rcur.next self.__head = cur.next rcur.next=self.__head else: # 不是头结点的情况 pre.next=cur.next return else: pre= cur cur=cur.next # 退出循环,cur指向尾结点, if cur.item == item: if cur ==self.__head: self.__head=None else: pre.next = cur.next # 和单链表不一样 def search(self, item): """查找节点是否存在""" if self.is_empty(): return False cur = self.__head while cur.next != self.__head: # 这种遍历把尾部节点漏掉了,对尾结点单独处理一下, if cur.item == item: return True else: cur = cur.next # 退出循环,cur指向的尾结点, if cur.item == item: # 对尾节点进行判断, return True return False # 所以你会发现整个过程,代码不是问题,都是最基本的,关键是要把逻辑理清楚, # 你还可以对双向链表进行扩充,就是变成双向循环链表, if __name__ == "__main__": ll = SinCycLinkedlist() ll.add(1) ll.add(2) ll.append(3) ll.insert(2, 4) ll.insert(4, 5) ll.insert(0, 6) ll.travel() ll.remove(1) ll.travel()

##############################################

"""

哈希表
哈希表(又称为散列表),是一种线性表的存储结构。哈希表由一个顺序表(数组)和一个哈希函数组成。哈希函数h(k)将k作为自变量,返回元素的存储下标。

哈希表在Python中的应用
a、字典与集合都是通过哈希表来实现的
b、 在Python中的字典:
a = {'name': 'Alex', 'age': 18, 'gender': 'Man'},使用哈希表存储字典,通过哈希函数将字典的键映射为下标。
c、在字典键值对数量不多的情况下,几乎不会发生哈希冲突,此时查找一个元素的时间复杂度为O(1)。
 
"""

##############################################

##############################################

##############################################

##############################################

猜你喜欢

转载自www.cnblogs.com/andy0816/p/12348242.html