在此开始记录数据结构与算法(基于Python的)学习,主要参考视频https://www.bilibili.com/video/av21540971/?p=1
线性表
线性表有两种存储结构:顺序存储结构与链式存储结构。
1 顺序表
比如 int 类型的数据1,2,3,4要以顺序表格式存储,首先每个整型数据占4个字节(每个字节8位),计算机会分配4*4个连续的字节空间给它们,让它们依次存储。python中 list 可以放置不同类型的元素,比如 a = [1, 'adaf' , 2.6],不同元素每个所占内存又不同,它是怎么存储的尼?它其实是元素外置的顺序表。虽然每个元素所占的空间大小不同,可是储存元素的起始地址所占大小一样。比如1,占四个字节,它可能是从0x100(地址)开始存储;‘adafo’占五个字节,它可能从0x11开始存储。注意要存储地址编号,所需要的空间大小是一样的。所以 list 是顺序存储的 0x100,0x11...。其实python中已经分装好了,不需要考虑。
其他语言中构建一个顺序表的完整信息应该包括两个部分:表头(告知应该分配多大空间,里面包含多少元素)eg, li[8],真实信息。但是Python是动态语言,其实不需要考虑表头。表头与数据区的存储又分为:一体式与分离式两种。分离式好
2 链表
2.1 单链表
单链表就是元素+节点(节点指向下一个)
class SingleNode(object): ''' 单链表的节点 ''' def __init__(self,item): # _item存放数据元素 self.item = item # _next存放下一个节点的标识 self.next = None
单链表的操作:
1)is_empty() 链表是否为空
2)length() 链表长度
3)travel() 遍历整个链表
4) add(item) 链表头部添加元素
5)append(item) 链表尾部添加元素
6)insert(pos, item) 指定位置添加元素
7)remove(item) 删除节点,注:相同元素时,删除第一个,查找也是一样
8)search(item) 查找节点是否存在
编程前先理解一下python中的等于,方便编程。
为什么,a = 1, b =2 , a,b = b,a 可以。为什么python中变量不用声明,int a = 1。可以理解为引用。
如图,a中存放的是一个地址,地址指向1这个数,同理b;然后箭头交叉就可以了。所以python中的等于有点像->
#!/usr/bin/env python # -*-coding:utf-8 -*- class Node(object): # 节点 def __init__(self, elem): # _item存放数据元素 self.elem = elem # _next存放下一个节点的标识 self.next = None class SingleLinkList(object): """单链表""" def __init__(self, node=None): self.__head = node def is_empty(self): """链表是否为空""" return self.__head==None def length(self): """链表长度""" # 定义cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 0 while cur != None: count = count + 1 cur = cur.next # 退出循环时,cur指向的是尾节点的下一个(None) return count def travel(self): """遍历整个链表""" # 定义cur游标,用来移动遍历节点 cur = self.__head while cur != None: print(cur.elem, end=" ") cur = cur.next print("") def add(self, item): """单链表头部添加元素""" # 对于任意一种插入,一般都是先动插入那个节点的指针,最后再打破原来的链表 node = Node(item) node.next = self.__head self.__head = node def append(self, item): """链表尾部添加,尾插法""" node = Node(item) # 处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了 if self.is_empty(): self.__head = node else: cur = self.__head while cur.next != None: cur = cur.next # 退出循环时,cur指向的是尾节点 cur.next = node def insert(self, pos, item): ''' 指定位置添加,单项循环与单链表一样 :param pos: 从0开始 :param item: :return: ''' # 考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插 if pos<=0: self.add(item) elif pos > self.length()-1: self.append(item) else: pre = self.__head # 这时游标指向的是第一个节点,不是head count = 0 while count < (pos-1): count = count+1 pre = pre.next # pre指向的是插入位置的前一个节点,比如插入位置是2,这时指向的是位置1的节点 # 插入一般都是先设置插入节点的指针(游标),保证链表不断,然后再改变原有链表哪个节点的next node = Node(item) node.next = pre.next pre.next = node def remove(self,item): """删除指定元素的节点(多个相同删除第一个)""" cur = self.__head pre = None while cur != None: if cur.elem == item: # 特殊情况,看是否是头节点 if cur == self.__head: # 头结点的情况 self.__head = cur.next else: # 找到删除节点 pre.next = cur.next return else: # 同步移动两个游标,先移动前一个 pre = cur cur = cur.next def search(self, item): """查找节点是否存在""" cur = self.__head while cur != None: if cur.elem == item: return True else: cur = cur.next return False if __name__ == '__main__': '''测试''' # 先创建单链表对象 ll = SingleLinkList() print(ll.is_empty()) print(ll.length()) ll.append(1) # 尾插 ll.append(2) ll.append(3) ll.add(8) # 头插 ll.insert(1,0.5) # 位置插 ll.insert(0, 7.5) ll.insert(10, 4) ll.travel() # 假如是print(ll.travel()),输出会多一个None ll.remove(7.5) # 删除头 ll.remove(2) ll.remove(4) ll.travel() print(ll.search(1))
2.2 单向循环链表
与单链表的区别就在于它的尾节点指向链表的第一个元素。
#!/usr/bin/env python # -*-coding:utf-8 -*- class Node(object): # 节点 def __init__(self, elem): # _item存放数据元素 self.elem = elem # _next存放下一个节点的标识 self.next = None class SingleCycleLinkList(object): """单向循环链表""" def __init__(self, node=None): self.__head = node # 单项循环,有节点时得指向自己 if node: node.next = node def is_empty(self): """链表是否为空""" return self.__head==None def length(self): """链表长度""" if self.is_empty(): return 0 # 定义cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 1 # 必须从1开始,与单链表不同 while cur.next != self.__head: count = count + 1 cur = cur.next return count def travel(self): """遍历整个链表""" if self.is_empty(): return # 定义cur游标,用来移动遍历节点 cur = self.__head while cur.next != self.__head: print(cur.elem, end=" ") cur = cur.next # 退出循环,cur指向尾节点,但是尾节点的元素没有打印 print(cur.elem) def add(self, item): """单向循环链表头部添加元素""" node = Node(item) if self.is_empty(): self.__head = node node.next = node else: cur = self.__head while cur.next != self.__head: cur = cur.next # 退出循环,cur指向尾节点 node.next = self.__head self.__head = node # cur.next = node # 与下面那行等价 cur.next = self.__head def append(self, item): """链表尾部添加,尾插法""" node = Node(item) # 处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了 if self.is_empty(): self.__head = node node.next = node else: cur = self.__head while cur.next != self.__head: cur = cur.next cur.next = node node.next = self.__head def insert(self, pos, item): ''' 指定位置添加,单项循环与单链表一样 :param pos: 从0开始 :param item: :return: ''' # 考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插 if pos<=0: self.add(item) elif pos > self.length()-1: self.append(item) else: pre = self.__head # 这时游标指向的是第一个节点,不是head count = 0 while count < (pos-1): count = count+1 pre = pre.next # pre指向的是插入位置的前一个节点,比如插入位置是2,这时指向的是位置1的节点 # 插入一般都是先设置插入节点的指针(游标),保证链表不断,然后再改变原有链表哪个节点的next node = Node(item) node.next = pre.next pre.next = node def remove(self,item): """删除指定元素的节点(多个相同删除第一个)""" if self.is_empty(): return cur = self.__head pre = None while cur.next != self.__head: if cur.elem == item: # 特殊情况,看是否是头节点 if cur == self.__head: # 头结点的情况 rear_cur = self.__head while rear_cur.next != self.__head: rear_cur = rear_cur.next self.__head = cur.next # 退出循环时,rear_cur指向的是尾节点 rear_cur.next = self.__head else: # 找到删除节点 pre.next = cur.next return else: # 同步移动两个游标,先移动前一个 pre = cur cur = cur.next # 退出循环,cur指向尾节点,需要判断一下当前的item if cur.elem == item: if cur == self.__head: # 链表只有一个节点,此时pre为None self.__head = None else: pre.next = cur.next def search(self, item): """查找节点是否存在""" cur = self.__head while cur.next != self.__head: if cur.elem == item: return True else: cur = cur.next # 退出循环cur指向尾节点 if cur.elem == item: return True return False if __name__ == '__main__': '''测试''' # 先创建单链表对象 ll = SingleCycleLinkList() print(ll.is_empty()) print(ll.length()) ll.append(1) # 尾插 ll.append(2) ll.append(3) ll.add(8) # 头插 ll.insert(1,0.5) # 位置插 ll.insert(0, 7.5) ll.insert(10, 4) ll.travel() # 假如是print(ll.travel()),输出会多一个None ll.remove(7.5) # 删除头 ll.remove(2) ll.remove(4) ll.travel() print(ll.search(1))
2.3 双向链表
“双向链表”又称为“双面链表”,每一个节点有两个链接:一个指向前一个,一个指向后一个。对于首节点,指向前一个的指空,同理尾节点。
#!/usr/bin/env python # -*-coding:utf-8 -*- class Node(object): # 双向节点 def __init__(self, elem): self.elem = elem self.prev = None # 前驱 self.next = None # 后驱 class DoubleLinkList(object): """双链表,其__init__,is_empty,length,travel,search,单链表一样,故可以利用多态的概念,直接传入单链表代替object""" def __init__(self, node=None): self.__head = node def is_empty(self): """链表是否为空""" return self.__head is None def length(self): """链表长度""" # 定义cur游标,用来移动遍历节点 cur = self.__head count = 0 while cur != None: count = count + 1 cur = cur.next # 退出循环时,cur指向的是尾节点的下一个(None) return count def travel(self): """遍历整个链表""" # 定义cur游标,用来移动遍历节点 cur = self.__head while cur != None: print(cur.elem, end=" ") cur = cur.next print("") def add(self, item): """双链表头部添加元素""" # 对于任意一种插入,一般都是先动插入那个节点的指针,最后再打破原来的链表 node = Node(item) if self.is_empty(): self.__head = node else: # 插入节点的后驱指向原首节点 node.next = self.__head # 原首节点的前驱指向插入节点 self.__head.prev = node # 头指向新的首节点 self.__head = node def append(self, item): """链表尾部添加,尾插法""" node = Node(item) # 处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了 if self.is_empty(): self.__head = node else: cur = self.__head while cur.next != None: cur = cur.next # 退出循环时,cur指向的是尾节点 cur.next = node node.prev = cur def insert(self, pos, item): ''' 指定位置添加,单项循环与单链表一样 :param pos: 从0开始 :param item: :return: ''' # 考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插 if pos<=0: self.add(item) elif pos > self.length()-1: self.append(item) else: cur = self.__head count = 0 while count < pos: count = count+1 cur = cur.next # 退出循环时cur指向pos位置 node = Node(item) node.next = cur node.prev = cur.prev cur.prev.next = node cur.prev = node def remove(self,item): """删除指定元素的节点(多个相同删除第一个)""" cur = self.__head while cur != None: if cur.elem == item: # 特殊情况,看是否是头节点 if cur == self.__head: # 头结点的情况 self.__head = cur.next if cur.next: # else的情况是只有一个节点,cur.next无prev cur.next.prev = Node else: # 找到删除节点,cur指向要删除的节点 cur.prev.next = cur.next if cur.next: # 尾节点 cur.next.prev = cur.prev return else: cur = cur.next def search(self, item): """查找节点是否存在""" cur = self.__head while cur != None: if cur.elem == item: return True else: cur = cur.next return False if __name__ == '__main__': '''测试''' # 先创建单链表对象 ll = DoubleLinkList() print(ll.is_empty()) print(ll.length()) ll.add(100) ll.append(1) # 尾插 ll.append(2) ll.append(3) ll.add(8) # 头插 ll.insert(1,0.5) # 位置插 ll.insert(0, 7.5) ll.insert(10, 4) ll.travel() # 假如是print(ll.travel()),输出会多一个None ll.remove(7.5) # 删除头 ll.remove(2) ll.remove(4) ll.travel() print(ll.search(1))