文章目录
- 线性表
- 顺序表
- 链表
- python实现单链表
- python实现双链表
- 顺序表与链表选择
- 顺序表的有序合并
- 链表的有序合并
- python中引用
- 栈
- 栈的应用
- 队列
线性表
- 相同数据类型的数据元素,组成的有限集合;
- 包括简单数据类型、复杂数据类型;
- 只有一个‘头’节点,一个‘尾’节点;
- 每个节点至多有一个前驱节点,一个后继节点;
- 基本操作增删改查。
顺序表
线性表的顺序存储(内存地址连续),即顺序表
- 通过索引(偏移量)可随机存取,时间复杂度O(1)
- 查找,逐一遍历每个元素进行对比,O(n)
- 删除效率低 O(n),要移动元素 =
- 插入,要移动元素 = ,i从最后一个索引开始
- 相关操作
- arr.append(x) 末尾追加 O(1)
- arr.insert(idx, x) 插入 O(n)
- arr.pop() 末尾删除一个元素并返回该元素,O(1)
- arr.pop(idx) 删除指定索引的元素并返回 O(n)
- arr.remove(x) 删除一个元素 O(n)
- arr[idx] 获取或者赋值 O(1)
- arr[-idx] 获取末尾的第idx个元素
- arr.sort() 默认从小到大排序 O(nlogn)
顺序表的结构:
- 表头,存储当前内存块的大小及已占用的大小;
- 数据区,存放数据元素。
分离式便于动态扩容。
可以动态扩容的顺序表为动态顺序表,如python列表list
各高级语言通过数组
实现顺序表。
顺序表优点:
- 物理相邻,逻辑相邻,不需要其他内存开销表示节点间的逻辑关系;
- 随机访问,时间复杂度O(1)
缺点:
- 插入、删除要移动元素,时间复杂度O(n),数据量大时效率低。
链表
线性表的链式存储(内存地址不连续),即为链表
- 每个节点除了存储数据,还要存储前驱或者后继节点的地址信息(指针);
- 单(向)链表
- 双(向)链表
- 循环链表
- 逐一遍历查询,时间复杂读O(n);
- 插入、删除 O(1)
- 相关操作
- is_empty() 是否为空;
- len() 长度;
- prepend(data) 头部插入;
- append(data) 尾部插入;
- insert(idx, data);
- remove_first();
- remove_last();
- remove(idx);
- search(data) 搜索数据;
- python实现的类
- 节点类
- 链表类
python实现单链表
# 节点类
class Node(object):
def __init__(self, data=None):
self.data = data
self.next = None
# 创建单链表类
class SingleLink(object):
def __init__(self):
self.__head = None
# 判空
def is_empty(self):
if self.__head is None:
return True
return False
# 头插法 O(1)
def prepend(self, data):
# 实例化节点对象
node = Node(data)
node.next = self.__head
self.__head = node
# 遍历 打印数据
def travel(self, count=False):
p = self.__head
if count:
num = 0
while p:
num += 1
p = p.next
return num
while p:
print(p.data)
p = p.next
# 求表长
def len(self):
return self.travel(count=True)
# 尾插法 ,先找到尾部节点,再插入O(n)
def append(self, data):
p = self.__head
while p.next is not None:
p = p.next
node = Node(data)
p.next = node
# 按索引查找
def search(self, idx):
# 索引从0开始
if idx < 0:
return -1
i = 0
p = self.__head
while p:
if i == idx:
return p
p = p.next
i += 1
else:
return -1
# 按值查找,依次遍历判断是否与目标值相等
def search_by_value(self, data):
pass
# 指定位置插入
def insert(self, idx, data):
pre = None
p = self.__head
i = 0
while p and i != idx:
pre = p
p = p.next
i += 1
node = Node(data)
if p is None:
pre.next = node
elif i == 0:
node.next = p
self.__head = node
else:
node.next = p
pre.next = node
# 删除元素
def remove(self, data):
if self.len() == 0:
return -1
pre = None
p = self.__head
while p:
if p.data == data and pre is None:
self.__head = p.next
return
elif p.data == data:
pre.next = p.next
return
pre = p
p = p.next
python实现双链表
- 节点类,(数据区,链接区)
- 双链表类
- 节点p前插入,先处理其前驱节点
p.prior.next = s;
s.prior = p.prior
s.next = p
p.prior = s - 节点p的删除
# 定义节点类
class Node(object):
def __init__(self, data):
self.data = data
self.prior = None
self.next = None
# 双链表类
class DoubleLink(object):
def __init__(self):
self.__head = None
# 判空
def is_empty(self):
if self.__head is None:
return True
return False
顺序表与链表选择
- 空间角度
- 存储空间不确定,采用链表,可动态扩容;
- 存储空间基本固定,为提高数据存储密度,充分利用存储空间,采用顺序表;
- 时间角度
- 频繁按照序号访问,用顺序表;
- 频繁插入、删除,用链表;
顺序表的有序合并
arr1 = [1, 3, 5, 7]
arr2 = [2, 4, 6, 8, 10, 11]
合并两个有序数组(列表);
# python 列表是顺序表
l1 = [1, 3, 5, 7]
l2 = [2, 4, 6, 8, 10]
# 有序合并(归并的基础)
def merge(l1, l2):
result = []
i = j = 0
while i < len(l1) and j < len(l2):
if l1[i] <= l2[j]:
result.append(l1[i])
i += 1
else:
result.append(l2[j])
j += 1
if i == len(l1):
result.extend(l2[j:])
else:
result.extend(l1[i:])
return result
链表的有序合并
- 方法1,从La Lb两个链表中取出较小值,尾插入Lc中(新创建节点)。
- 方法2,采摘节点法(不额外创建节点)。
# 采摘节点法
class Node(object):
def __init__(self, data, next=None):
self.data = data
self.next = next
l1 = None
for i in [1, 3, 5, 7]:
temp = Node(i)
if l1 is None:
l1 = temp
cur = temp
else:
cur.next = temp
cur = temp
l2 = None
for i in [2, 4, 6, 8, 10, 11]:
temp = Node(i)
if l2 is None:
l2 = temp
cur = temp
else:
cur.next = temp
cur = temp
def travel(link):
cur = link
while cur:
print(cur.data, end=" ")
cur = cur.next
print("")
travel(l1)
travel(l2)
l3 = None
l3_ptr = None
l1_ptr = l1
l2_ptr = l2
while l1_ptr and l2_ptr:
if l1_ptr.data <= l2_ptr.data:
if l3 is None:
l3 = l1_ptr
l3_ptr = l1_ptr
else:
l3_ptr.next = l1_ptr
l3_ptr = l1_ptr
l1_ptr = l1_ptr.next
else:
if l3 is None:
l3 = l2_ptr
l3_ptr = l2_ptr
else:
l3_ptr.next = l2_ptr
l3_ptr = l2_ptr
l2_ptr = l2_ptr.next
if l1_ptr:
l3_ptr.next = l1_ptr
else:
l3_ptr.next = l2_ptr
travel(l3)
python中引用
python中的赋值就是对一个对象的引用,也就是存储对象的地址信息。
# a 引用列表对象,分配内存空间(a)存储列表对象的地址,也可以说a是一个指针
a = [1, 2, 3]
栈
- 操作受限的线性表;
- 后进先出LIFO特性;
- 允许插入、删除的一端称为栈顶,另一端为栈底;没有元素为空栈;
- 解决具有后进先出特性的问题
- 进制转换;
- 括号匹配;
- 表达式求值;
- 函数调用;递归调用;
- 八皇后问题;
- 迷宫问题;
- 线性表任意位置插入、删除;栈只能在栈顶插入删除;
- 抽象数据类型ADT
- 初始化、push入栈、pop出栈、peek返回栈顶元素、is_empty、length
- 顺序栈,使用python列表实现;
- 链式栈,头指针处为栈顶;
# 链式栈的实现
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
def __str__(self):
return str(self.data)
class LinkStack:
def __init__(self):
# 头指针表示栈顶
self.__top = None
def is_empty(self):
return self.__top is None
def push_stack(self, data):
# 入栈
node = Node(data)
node.next = self.__top
self.__top = node
def pop_stack(self):
# 出栈
node = self.__top
self.__top = node.next
return node
def peek_stack(self):
# 返回栈顶元素的值
return self.__top.data
if __name__ == '__main__':
alist = [2,3,4,7]
link_stack = LinkStack()
for e in alist:
link_stack.push_stack(e)
while not link_stack.is_empty():
print(link_stack.pop_stack())
栈的应用
- 进制转换
- 十进制数 n 转为r (二、八、十六)进制,除r 取余数倒排列;
- n % r 结果入栈;
- n // r 结果赋值给n;
- 例子,将十进制 36926 转为八进制,输出字符串的结果;
if __name__ == '__main__':
n = 36926
r = 8
# 顺序栈
stack = []
while n:
stack.append(n % r)
n = n // r
result = ""
while stack:
result += str(stack.pop())
print(result)
- 函数调用时,入栈,返回结果时,出栈
- 如阶乘 factorial(n)
队列
- 先进先出的线性表;
- 队尾插入,队头删除;
- 按照先后顺序、公平地处理问题;
- 基本操作,入队、出队、判空、长度、遍历;
- 顺序队列(循环队列,内存重复利用)
队头指针, front,出队 front + 1;
队尾指针, rear,入队 rear + 1;
循环队列,front = (front + 1) % length;
rear = (rear + 1) % length
循环队列判空,front == rear
循环队列判满,front == (rear +1)% length 表示队列满,需要预留一个数据空间,长度为n的空间,只能存储n-1的数据,每次入队需要检查是否队满
# 顺序 循环队里
class Queue:
def __init__(self, max_size=5):
self.__list = [None] * max_size
self.max_size = max_size
# 队头
self.front = 0
# 队尾
self.rear = 0
def is_empty(self):
return self.front == self.rear
def is_full(self):
return (self.rear + 1) % self.max_size == self.front
def in_queue(self, data):
# 每次判断是否队满
if (self.rear + 1) % self.max_size == self.front:
# full
print("队列已满")
return
self.__list[self.rear] = data
self.rear = (self.rear + 1) % self.max_size
def out_queue(self):
# 判断是否为空
if self.front == self.rear:
print("队列为空")
return
data = self.__list[self.front]
self.front = (self.front + 1) % self.max_size
return data
def length(self):
if self.is_empty():
return 0
elif self.is_full():
return self.max_size - 1
return (self.rear - self.front + self.max_size) % self.max_size
def travel(self):
cur = self.front
while cur != self.rear:
print(self.__list[cur])
cur = (cur + 1) % self.max_size
if __name__ == '__main__':
queue = Queue()
alist = [1,5,9,2,0]
print("is empty:", queue.is_empty())
print("is full:", queue.is_full())
for i in alist:
queue.in_queue(i)
queue.travel()
- 链式队列
- front 指针指向头节点;
- rear 指针指向尾节点;
- 队头出队;队尾入队
# 数据节点
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
# 链表头
class LinkHead:
def __init__(self):
self.front = None
self.rear = None
# 链 队列
class LinkQueue:
def __init__(self, max_size=5):
self.__head = LinkHead()
self.max_size = max_size
def is_empty(self):
return self.__head.front is None and self.__head.rear is None
def is_full(self):
return self.length() == self.max_size
def length(self):
if self.is_empty():
return 0
front = self.__head.front
num = 1
while front != self.__head.rear:
num += 1
front = front.next
return num
def put(self, data):
# 入队 判满
if self.is_full():
print("queue is full.")
return
node = Node(data)
if self.is_empty():
self.__head.front = node
self.__head.rear = node
else:
self.__head.rear.next = node
self.__head.rear = node
def get(self):
if self.is_empty():
print("queue is empty.")
return
if self.__head.front == self.__head.rear:
node = self.__head.front
self.__head.front = self.__head.rear = None
else:
node = self.__head.front
self.__head.front = node.next
return node.data
if __name__ == '__main__':
link_queue = LinkQueue(max_size=10)
for i in range(15):
link_queue.put(i)
while not link_queue.is_empty():
print(link_queue.get())
下一篇: 树结构