数据结构与算法—线性表
线性结构的特点
- 存在唯一一个被称为”第一个“的数据元素
- 存在唯一一个被称为”最后一个“的数据元素
- 除第一个之外,集合中每一个元素均只有一个前驱
- 除最后一个之外,集合中每一个元素均只有一个后继
线性表的定义
用数学语言定义线性表可以表示为 ( a 1 , a 2 . . . a i − 1 , a i , a i + 1 . . . a n ) a i − 1 称 为 a i 的 前 驱 元 素 , a i + 1 称 为 a i 的 后 继 元 素 。 (a_1,a_2...a_{i-1},a_i,a_{i+1}...a_n)a_{i-1}称为a_i的前驱元素,a_{i+1}称为a_i的后继元素。 (a1,a2...ai−1,ai,ai+1...an)ai−1称为ai的前驱元素,ai+1称为ai的后继元素。
顺序表
顺序表是以数组的形式保存的线性表,具有地址连续的存储单元。
顺序表的基本API
python实现的顺序表
- 初始化
- 清空顺序表
- 判断是否为空
- 获取顺序表的长度
- 获取指定位置的元素
- 添加元素
- 在指定位置插入元素
- 删除并返回指定位置的元素
- 第一次元素相等的下标位置
class Sequence:
def __init__(self, n: int):
self.__elems = [None for i in range(n)]
self.__length = 0
def clear(self):
self.__length = 0
def is_empty(self):
if self.__length == 0:
return True
else:
return False
def length(self):
return self.__length
# 获取指定位置的元素
def get(self, index: int):
if index > self.__length:
return None
return self.__elems[index]
# 添加元素
def add(self, val):
self.__elems[self.__length] = val
self.__length += 1
# 在指定位置插入元素
def insert(self, n: int, val):
if n > self.__length:
return
self.__length += 1
for i in range(self.__length, n, -1):
self.__elems[i] = self.__elems[i-1]
self.__elems[n] = val
# 删除并返回指定位置的元素
def remove(self, n: int):
if n > self.__length:
return None
if n == self.__length:
self.__length -= 1
return self.__elems[self.__length+1]
else:
res = self.__elems[n]
for i in range(n, self.__length):
self.__elems[i] = self.__elems[i+1]
self.__length -= 1
return res
# 返回第一次元素相等的下标位置, 如果不存在则返回-1
def index_of(self, val):
for index,v in enumerate(self.__elems):
if v == val:
return index
return -1
顺序表常见的算法
顺序表的合并
两个原本有序的顺序表合并为新的顺序表,新的顺序表仍然保持有序。
顺序表的合并时间复杂度O(la.length + lb.length)
# 合并两个有序的顺序表,合并后新的顺序表仍然有序
def merge(la: Sequence, lb: Sequence) -> Sequence:
lc = Sequence(la.length()+lb.length())
i, j = 0, 0
while i < la.length() and j < lb.length():
if la.get(i) <= lb.get(j):
lc.add(la.get(i))
i += 1
else:
lc.add(lb.get(j))
j += 1
while i < la.length():
lc.add(la.get(i))
i += 1
while j < lb.length():
lc.add(lb.get(j))
j += 1
return lc
链表
链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能只管的表示数据元素的逻辑顺序,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成, 结点可以在运行时动态生成。
链表的基本API
python实现的链表
-
初始化
-
清空l链表
-
判断是否为空
-
获取链表的长度
-
获取指定位置的元素
-
添加元素
-
在指定位置插入元素
-
删除并返回指定位置的元素
-
第一次元素相等的下标位置
class Node:
def __init__(self, Ldata=None, Lnext=None):
self.data = Ldata
self.next = Lnext
# 带头结点的单向链表,头节点为空节点
class LinkList:
def __init__(self):
self.head = Node()
self.__N = 0
def clear(self):
self.head.next = None
self.__N = 0
def length(self):
return self.__N
def is_empty(self):
if self.__N == 0:
return True
else:
return False
# 获取指定位置元素的值
def get(self, index: int):
n = self.head
for i in range(index+1):
n = n.next
return n.data
def add(self, val):
n = self.head
while n.next != None:
n = n.next
node = Node(Ldata=val)
n.next = node
self.__N += 1
# 在指定位置插入节点
def insert(self, index: int, val):
n = self.head
node = Node(Ldata=val)
for i in range(index):
n = n.next
node.next = n.next
n.next = node
self.__N += 1
# 删除指定位置的节点,并返回删除节点
def remove(self, index: int):
n = self.head
for i in range(index):
n = n.next
res = n.next
n.next = n.next.next
self.__N -= 1
return res
# 返回第一次元素相等的下标位置, 如果不存在则返回-1
def index_of(self, val):
n = self.head
index = -1
while n != None:
if n.data == val:
return index
else:
n = n.next
index += 1
return -1
链表常见的算法
链表的合并
两个原本有序的链表合并为新的链表,新的链表仍然保持有序。
链表的合并时间复杂度O(la.length + lb.length)
def merge(la: LinkList, lb: LinkList):
p, q = la.head.next, lb.head.next
res = l = la.head
while p is not None and q is not None:
if p.data < q.data:
l.next = p
l = p
p = p.next
else:
l.next = q
l = q
q = q.next
if p is not None:
l.next = p
else:
l.next = q
return res
一元多项式的表示及相加
class Node:
def __init__(self, c=0, e=0):
self.coef = c # 系数
self.expn = e # 指数
self.next = None
class Polynomial:
def __init__(self):
self.head = Node()
def add(self, c, e):
n = self.head
while n.next != None:
n = n.next
node = Node(c, e)
n.next = node
# 一元多项式的加法
@staticmethod
def add_polynomial(p1, p2) -> Node:
h1 = p1.head.next
h2 = p2.head.next
res = p = p1.head
while h1 is not None and h2 is not None:
if h1.expn < h2.expn:
p.next = h1
p = h1
h1 = h1.next
elif h1.expn > h2.expn:
p.next = h2
p = h2
h2 = h2.next
else:
h1.coef += h2.coef
p.next = h1
p = h1
h1 = h1.next
h2 = h2.next
if h1:
p.next = h1
else:
p.next = h2
return res
判断链表是否有环
实现思路:采用快慢指针
- 如何判断是否有环?如果有两个头结点指针,一个走的快,一个走的慢,那么若干步以后,快的指针总会超过慢的指针一圈。
- 如何计算环的长度?第一次相遇(超一圈)时开始计数,第二次相遇时停止计数。
- 如何判断环的连接点?碰撞点p到连接点的距离=头指针到连接点的距离,所以分别让两个指针从碰撞点和头节点开始走,他们相遇的结点就是连接点。
- 如何计算整个链表的长度?根据2和3,链表的长度等于头结点到碰撞点的长度+环的长度。
判断是否有环,慢指针每次走一步,快指针每次走两步
# 检测链表是否有环
def judge_hoop(l: LinkList) -> bool:
slow = l.head.next
fast = l.head.next.next
while fast and fast.next:
if slow is fast:
return True
else:
slow = slow.next
fast = fast.next.next
return False
计算环的长度
# 获取环的长度
def hoop_length(l: LinkList) -> int:
slow = l.head.next
fast = l.head.next.next
flag = 0
num = 0
while fast and fast.next:
if slow is fast and flag < 2:
flag += 1
else:
if flag == 1:
num += 1
if flag == 2:
num += 1
return num
slow = slow.next
fast = fast.next.next
return 0
获取连接点
# 获取有环链表的连接点
def link_point(l: LinkList):
slow = l.head.next
fast = l.head.next.next
while fast and fast.next:
if slow is fast:
break
else:
slow = slow.next
fast = fast.next.next
fast = l.head
while slow is not fast:
slow = slow.next
fast = fast.next
return slow
计算链表的长度
# 计算链表长度
def link_length(l: LinkList) -> int:
hoop = hoop_length(l)
n = l.head
num = 0
while n.next is not link_point(l):
num += 1
n = n.next
return num + hoop
单链表的反转
使用递归的方式实现单链表的反转
def to_do(n, l):
if n.next == None:
l.head.next = n
return n
pre = to_do(n.next, l)
if n is l.head:
return l.head
else:
pre.next = n
n.next = None
return n
# 单链表的反转
def reverse(l: LinkList):
if l.is_empty():
return
n = to_do(l.head, l)
return l.head