数据结构与算法(python版)-- 02 线性表

线性表

  • 相同数据类型的数据元素,组成的有限集合;
  • 包括简单数据类型、复杂数据类型;
  • 只有一个‘头’节点,一个‘尾’节点;
  • 每个节点至多有一个前驱节点,一个后继节点;
  • 基本操作增删改查。

顺序表

线性表的顺序存储(内存地址连续),即顺序表

  • 通过索引(偏移量)可随机存取,时间复杂度O(1)
  • 查找,逐一遍历每个元素进行对比,O(n)
  • 删除效率低 O(n),要移动元素 a i a_i ai = a i + 1 a_{i+1} ai+1
  • 插入,要移动元素 a i a_i ai = a i − 1 a_{i-1} ai1,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前插入,先处理其前驱节点 a i − 1 a_{i-1} ai1
    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())

	

 
 
下一篇: 树结构

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

laufing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值