数组
数据在内存中顺序存储,可以通过下标索引。
特点:支持随机访问,索引元素效率高,插入元素和删除元素效率低。
数组存放空间连续,插入元素时需要将插入位置及后面的元素一次往后移动一位,然后将元素插入到空出的插入位置。
python中的list是一个动态数组,支持动态扩容(2^n),list中封装了数组常用的方法:
>>> list = [3, 5, 7]
>>> list.append(9) # 往数组尾部添加元素
>>> list
[3, 5, 7, 9]
>>> list.insert(2,5) # 在指定位置插入元素
>>> list
[3, 5, 5, 7, 9]
>>> list.pop() # 取数组最后一个元素,会从数组中删除该元素
9
>>> list
[3, 5, 5, 7]
>>> list.remove(5) # 删除数组中的指定元素,有多个时只删除第一个
>>> list
[3, 5, 7]
>>> list.index(5) # 查找元素在列表中的位置
1
>>> list.reverse() # 列表反转
>>> list
[7, 5, 3]
在python中通过id()函数来输出列表元素的第地址时,可以看到id的值并不是连续的,这是为什么呢?
>>> arr = ['a','b','c']
>>> for char in arr:
print(id(char))
2204167613216
2204167611536
2204167474288
因为python的list存的是元素对象的引用而不是元素本身,从下面代码中可以看出来,所以输出的id值是元素对象的引用值,在底层的c语言中是使用数组来实现的。
>>> a = 1
>>> id(a)
1598830848
>>> b = [a]
>>> id(b)
9390640
>>> id(b[0])
1598830848
链表
链表数据在内存中的存储不连续,链表有一系列的节点通过指针依次连接起来,每个节点包含数据域和指针域,数据域存储数据,指针域存储下一个节点的指针。
单向链表
单向链表也叫单链表,是链表中最简单的形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
因为链表中的数据不是顺序存储的,不能随机访问,因此链表的查询效率低,但是链表在任意位置插入和删除节点时不用移动节点位置,因此插入和删除效率高。
链表实现
通常有以下步骤:
- 定义节点
- 定义链表
- 使用链表
class Node(object): '''1.节点定义''' def __init__(self,data): # 数据域 self.data = data # 指针域,创建时为None self.next = None class SingleList(object): '''2.链表定义''' def __init__(self): # 链表头 self.head = None if __name__ == '__main__': #3.链表使用 # 创建链表 singlelist = SingleList() # 创建节点 node1 = Node(3) # 将链表头指向节点 singlelist.head = node1 # 创建节点往链表上添加 node2 = Node(5) node1.next = node2 node3 = Node(7) node2.next = node3 # 创建临时变量指向头节点 temp = singlelist.head # 取头节点的数据 print(temp.data) # 取下一个节点的数据 temp = temp.next print(temp.data)
输出结果:
3
5
上面定义并使用了一个单项链表,但是链表的使用很不方便,获取元素添加和获取需要定位到节点,长度也没法知道,所以需要定义一些常用的方法,方便链表的使用。
链表中的常用方法:
- is_empty() 链表是否为空
- length() 链表长度
- items() 获取链表数据迭代器
- add(item) 链表头部添加元素
- append(item) 链表尾部添加元素
- insert(pos, item) 指定位置添加元素
- remove(item) 删除节点
- find(item) 查找节点是否存在
class Node(object):
'''节点定义'''
def __init__(self,data):
# 数据域
self.data = data
# 指针域,创建时为None
self.next = None
class SingleList(object):
'''链表定义'''
def __init__(self):
# 链表头
self.head = None
def is_empty(self):
'''链表判空'''
return self.head is None
def length(self):
'''链表长度'''
# 获取初始的头指针
cur = self.head
count = 0
while cur is not None:
count += 1
# 指针后移1位
cur = cur.next
return count
def datas(self):
'''链表遍历'''
cur = self.head
while cur is not None:
# 返回生成器
yield cur.data
# 指针后移1位
cur = cur.next
def add(self,data):
'''在头部插入节点'''
# 创建新节点
node = Node(data)
# 新节点指向原头节点
node.next = self.head
# 头节点指向新节点
self.head = node
def append(self,data):
'''向尾部插入节点'''
node = Node(data)
if self.is_empty():
# 空链表则直接将head指向node
self.head = node
else:
cur = self.head
while cur.next is not None:
# 将指针移到最后一个节点
cur = cur.next
# 将最后一个节点的指针指向node
cur.next = node
def insert(self,index,data):
'''任意位置插入节点'''
if index <= 0:
# 在头部插入
self.add(data)
elif index >= self.length():
# 在尾部插入
self.append(data)
else:
cur = self.head
for i in range(index - 1):
cur = cur.next
# 创建新节点
node = Node(data)
# 将新节点指向就节点
node.next = cur.next
# 将旧节点前一个节点指向新节点
cur.next = node
def remove(self, item):
""" 删除一个结点 """
if self.is_empty():
return
cur = self.head
pre = None
# 第一个元素为需要删除的元素
if cur.data == item:
if cur.next != None:
cur.next = self.head.next
# 调整头部结点
self.head = self.head.next
else:
# 只有一个元素
self.head = None
else:
# 不是第一个元素
pre = self.head
while cur.next != self.head:
if cur.data == item:
# 删除
pre.next = cur.next
return True
else:
pre = cur # 记录前一个指针
cur = cur.next # 调整指针位置
# 当删除元素在末尾
if cur.data == item:
pre.next = self.head
return True
def find(self, data):
""" 查找元素是否存在"""
return data in self.datas()
def printlist(self):
'''打印链表元素'''
print('当前链表:')
for i in self.datas():
print(i)
if __name__ == '__main__':
# 创建链表
singlelist = SingleList()
#添加节点
singlelist.add(3)
singlelist.append(5)
singlelist.append(7)
# 链表长度
length = singlelist.length()
print('链表长度:',length)
singlelist.printlist()
# 删除元素
singlelist.remove(5)
print('--删除元素--')
singlelist.printlist()
# 任意位置插入元素
singlelist.insert(2,666)
print('--任意位置插入元素--')
singlelist.printlist()
双向链表
双向链表与与单向链表的区别在于,双向链表的节点包含了两个指针域,如下图,prev指针域指向前一个节点,next指向后一个节点,head指向头节点,第一个节点的prev指针为None,最后一个节点的next为空。
为了方便操作双向链表,也会在链表中定义一些常用的方法。实例如下:
class Node(object):
'''双链表的节点'''
def __init__(self,data):
self.data = data
self.prev = None
self.next = None
class DoubleLinkedList(object):
'''双向链表'''
def __init__(self):
self.head = None
self.tail = None
def is_empty(self):
return self.head is None
def length(self):
cur = self.head
count = 0
while cur is not None:
cur = cur.next
count += 1
return count
def items(self):
'''顺序遍历'''
cur = self.head
while cur is not None:
yield cur.data
cur = cur.next
def add(self,data):
'''向链表头部添加节点'''
node = Node(data)
if self.head is None:
self.head = node
self.tail = node
else:
node.next = self.head
self.head.prev = node
self.head = node
def append(self,data):
'''向链表尾部添加节点'''
node = Node(data)
if self.tail is None:
self.head = node
self.tail = node
else:
node.prev = self.tail
self.tail.next = node
self.tail = node
def insert(self,index,data):
'''向指定位置添加节点'''
if index <= 0:
self.add(data)
elif index >= self.length():
self.append(data)
else:
node = Node(data)
cur = self.head
for i in range(index):
cur = cur.next
# 循环完后cur指向节点index了
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
def remove(self, item):
""" 删除一个结点 """
if self.is_empty():
return
# 当删除的节点在尾部
if item == self.tail.data:
self.tail = self.tail.prev
self.tail.next = None
return True
cur = self.head
pre = None
# 第一个节点为需要删除的节点
if cur.data == item:
# 有多个节点
if cur.next != None:
self.head = cur.next
return True
# 只有一个节点
else:
self.head = None
return True
else:
# 首尾之外的任意节点
while cur.next != self.tail:
if cur.data == item:
pre.next = cur.next
cur.next.prev = pre
return True
else:
pre = cur
cur = cur.next
def find(self, data):
""" 查找节点是否存在"""
return data in self.items()
def print_list(self):
'''打印链表数据'''
print('链表当前节点:')
for node in self.items():
print(node)
if __name__ == '__main__':
# 实例化双向链表
doublelist = DoubleLinkedList()
# 向头部添加节点
doublelist.add(10)
doublelist.add(20)
doublelist.print_list()
# 向尾部添加节点
doublelist.append(30)
doublelist.append(40)
doublelist.print_list()
# 任意位置添加节点
doublelist.insert(2,666)
doublelist.print_list()
# 删除首尾和任意节点
doublelist.remove(666)
doublelist.remove(20)
doublelist.remove(40)
doublelist.print_list()
输出结果:
链表当前节点:
20
10
链表当前节点:
20
10
30
40
链表当前节点:
20
10
666
30
40
链表当前节点:
10
30
其他数据结构:栈、队列、树等的实现可以看:python-data-structure