Python笔记(七)

1. 数据结构与算法

1.1 什么是数据结构

数据结构是存储、组织数据的方式

1.2 什么是算法

算法是为了实现业务目的的各种方法和思路

1.3 算法的五大特性

  1. 输入:0个输入或多个输入
  2. 输出:1个或多个输出
  3. 有穷性:在有限的步骤之后会自动结束,而不会无限的循环,且每一个步骤可以在可接受的实践内完成
  4. 确定性:每一步都有确定的含义不会出现二义性
  5. 可行性:每一步都是可行的,即每一步都能够执行有限的次数完成。

1.4 时间复杂度

时间效率一般是看操作步骤的数量。时间复杂度是随着问题规模不断变化的一个趋势。
对于一些基本的操作,例如赋值、输入、输出等,时间复杂度为O(1)
对于顺序操作,时间复杂度也是O(1)
循环操作,时间复杂度为O(n)
对于分支结构,时间复杂度一般取分支的时间复杂度的最大值。

1.5 时间复杂度排序

通常情况下,我们只关注算法的最坏时间复杂度。一般只关注最高次项。
时间复杂度越低,效率越高。
O(n^3) > O(n^2) > O(nlogn) > O(n) > O(logn) > O(1)

1.6 空间复杂度

空间复杂度是指算法所耗费的存储空间
内存是以字节为基本单位的,1024b = 1kb,每个基本存储空间都有自己的地址。

2. 数据结构的分类

数据结构按照逻辑结构的不同,可以分为线性结构和非线性结构。

2.1 线性结构

线性结构:所有的结点最多只有一个直接前驱结点和一个直接后继结点。
线性结构按照存储方式的不同,又分为顺序表和链表。
常见的线性结构有栈、队列。

2.2 非线性结构

非线性结构:一个结点可以有多个直接前驱和多个直接后继,常见的非线性结构有树结构、图结构。

2.3 顺序表

2.3.1 什么是顺序表

将元素顺序地存放在一块连续的存储区里,存储是连续的。
顺序表的完整信息包括两部分:数据区和信息区(即,容量和已有元素个数)
顺序表存储数据有两种情况,分别是一体式结构、分离式结构。且时间复杂度都是O(1)

2.3.2 一体式结构

对于相同的数据类型,变量会记住首元素地址,然后按照偏移公式m*(n-1)个字节去查找。其中m代表是数据类型的大小,例如int类型占4个字节,m=4
n代表的是元素个数。
如下图:
在这里插入图片描述
存储类型为整型,即占4个字节。利用便宜公式:4*(n-1)可得:
第一个元素1,通过0x11偏移0个字节找到
第二个元素2,通过0x11偏移4个字节找到
第三个元素3,通过0x11偏移8个字节找到

2.3.3 分离式结构

对于不同的数据类型,变量会记住首元素地址所在的地址,然后按照偏移公式找到对应的地址,再根据地址找对应的元素。
如下图:
在这里插入图片描述
存储的数据为地址,地址也占4个字节。利用便宜公式:4*(n-1)可得:
第一个元素A,通过0x11偏移0个字节找到所在地址是0x21,然后再根据0x21去找元素所在位置。
第二个元素2,通过0x11偏移4个字节找到所在地址是0x31,然后再根据0x31去找元素所在位置。
第三个元素3,通过0x11偏移8个字节找到所在地址是0x41,然后再根据0x41去找元素所在位置。

2.3.4 顺序表的时间复杂度

顺序表的增加和删除有两种方式。
无论是增加还是删除,在头部操作时间复杂度都是O(n),在尾部操作时间复杂度都是O(1)
Python中的列表就是一种顺序表。

2.4 链表

2.4.1 定义

元素放在通过链接构造起来的一系列存储块中,存储是非连续的。

2.4.2 单链表

单链表是链表的一种形式,每个节点包含两个域,一个元素域,一个链接域,最后一个节点的链接域则指向一个空值。
在这里插入图片描述
上图中,head是头结点,指向第一个节点,第一个节点的next指向第二个节点。

2.4.3 python实现单链表

  1. 首先要创建一个节点,节点包含两个两部分,一个是属性值,一个是next域
  2. 然后创建一个链表,包含头指针和链表方法
  3. 实现链表的添加元素、删除元素、查找元素等方法。
  4. 单链表可以为空表,所以初始化时,可以不传入节点参数,
  5. 遍历节点的时候,要用临时指针,不能使用头指针,因为头指针永远指向第一个节点

2.4.4 单链表代码实现

"""
@Time : 2024/1/16 21:21
@Author :Ceyyen_Koo
@File :my_node.py
@IDE :PyCharm
@coding: utf-8
@DESC:单链表的单个节点
"""


class MyNode(object):
    def __init__(self, value):
        self.value = value
        self.next = None

"""
@Time : 2024/1/16 21:23
@Author :Ceyyen_Koo
@File :single_linked_list.py
@IDE :PyCharm
@coding: utf-8
@DESC:单链表
注意点:
遍历、添加的时候不能用头指针,要新增一个临时的指针
"""
from my_node import MyNode


class SingleLinkedList:
    def __init__(self, node=None):
        # 设置头指针,使其指向节点
        self.head = node

    # 判断单链表是否为空
    def is_empty(self):
        if self.head is None:
            return True
        else:
            return False

    # 获取单链表的长度
    def length(self):
        size = 0
        cur_pointer = self.head
        while cur_pointer is not None:
            size += 1
            cur_pointer = cur_pointer.next
        return size

    # 遍历单链表所有的结点,以列表的形式返回
    def loop_list(self):
        cur_pointer = self.head
        new_list = list()
        while cur_pointer is not None:
            new_list.append(cur_pointer.value)
            cur_pointer = cur_pointer.next
        return new_list

    # 头部添加元素方法
    def append_start(self, val):
        print("单链表头部添加元素开始")
        head_node = MyNode(val)
        cur_pointer = self.head
        self.head = head_node
        self.head.next = cur_pointer
        print("单链表头部添加元素结束")

    # 指定位置添加元素
    def append_middle(self, val, pos):
        """
        :param val: 新增的元素值
        :param pos: 新增的位置,例如第0位
        :return: None
        """
        print("单链表指定位置添加元素开始")
        if pos == 0:
            print(f"开始执行头部添加元素方法")
            self.append_start(val)
            print(f"头部添加元素方法执行完毕")
        elif pos >= self.length():
            print(f"开始执行尾部添加元素方法")
            self.append_end(val)
            print(f"头部添加元素方法执行完毕")
        else:
            cur_pointer = self.head
            for i in range(pos - 1):
                cur_pointer = cur_pointer.next
            new_node = MyNode(val)
            new_node.next = cur_pointer.next
            cur_pointer.next = new_node
        print("单链表指定位置添加元素结束")

    # 尾部添加元素方法
    def append_end(self, val):
        print("单链表尾部添加元素开始")
        new_child_node = MyNode(val)
        cur_pointer = self.head
        # 遍历所有结点,寻找最后一个结点
        if cur_pointer is None:
            self.head = new_child_node
        else:
            while cur_pointer.next is not None:
                cur_pointer = cur_pointer.next
            cur_pointer.next = new_child_node
        print("单链表尾部添加元素结束")

    # 删除尾结点
    def del_tail(self):
        if self.head is None:
            return
        if self.length() == 1:
            self.head = None
            return
        if self.length() == 2:
            self.head = self.head.next
            return
        cur_pointer = self.head
        for i in range(self.length() - 2):
            cur_pointer = cur_pointer.next
        cur_pointer.next = None

    # 删除节点
    def del_element(self, val=None, pos=None):
        """
        删除元素节点,可传递两个参数。要么只传pos,要么只传val,pos优先级高于val
        :param val: 删除元素的值
        :param pos: 指定删除元素的位置,例如第0位,第1位
        :return: None
        """
        if pos is not None:
            # 执行按位置删除
            # 判断传入pos是否合法
            if pos == 0:
                # 删除头结点
                self.head = self.head.next
                return
            else:
                cur_pointer = self.head
                for i in range(pos - 1):
                    cur_pointer = cur_pointer.next
                if pos >= self.length() - 1:
                    cur_pointer.next = None
                else:
                    cur_pointer.next = cur_pointer.next.next
                return
        elif val is not None:
            pre_pointer = None  # 添加临时辅助指针
            # 按值删除
            cur_pointer = self.head
            while cur_pointer is not None:
                if cur_pointer.value == val:
                    # 要删除的元素在头结点
                    if cur_pointer == self.head:
                        self.head = self.head.next
                    else:
                        pre_pointer.next = cur_pointer.next
                    return
                else:
                    pre_pointer = cur_pointer
                    cur_pointer = cur_pointer.next

    # 查找结点是否存在
    def find_element(self, val):
        cur_pointer = self.head
        while cur_pointer is not None:
            if cur_pointer.value == val:
                return True
            else:
                cur_pointer = cur_pointer.next
        return False


if __name__ == '__main__':
    # 创建一个结点
    my_node = MyNode(10)
    # 创建一个单链表
    sing_list = SingleLinkedList(my_node)
    # 查看单链表头指针指向
    print(sing_list.head)
    # 查看单链表头指针指向的结点元素
    print(sing_list.head.value)
    print(sing_list.head.next)
    print(sing_list.length())
    print(sing_list.loop_list())
    sing_list.append_end(20)
    sing_list.append_end(30)
    sing_list.append_end(40)
    print(f"头结点指向元素的值是{sing_list.head.value}")
    # 循环遍历单链表
    print(sing_list.head.value)
    print(sing_list.head.next.value)
    print(sing_list.head.next.next)

    print(sing_list.is_empty())
    print(sing_list.length())
    print(sing_list.loop_list())

    sing_list2 = SingleLinkedList()
    print(sing_list2.is_empty())
    print(sing_list2.length())
    print(sing_list2.loop_list())
    print(sing_list2.append_start(20))
    print(sing_list2.append_start(30))
    print(sing_list2.append_start(40))
    print(sing_list2.length())
    print(sing_list2.loop_list())
    sing_list2.append_middle(66, 2)
    print(sing_list2.loop_list())
    sing_list2.append_middle(77, 0)
    print(sing_list2.loop_list())
    sing_list2.append_middle(88, 4)
    print(sing_list2.loop_list())
    sing_list2.append_middle(99, 6)
    print(sing_list2.loop_list())
    sing_list2.del_element(pos=2)  # 从0位开始,删除第2位的元素
    print(sing_list2.loop_list())
    sing_list2.del_element(pos=0)  # 删除头结点
    print(sing_list2.loop_list())
    sing_list2.del_element(pos=4)  # 删除尾节点
    print(sing_list2.loop_list())
    sing_list2.del_element(val=20)  # 删除元素值为20的结点
    print(sing_list2.loop_list())
    sing_list2.del_element(val=66)  # 删除元素值为66的结点
    print(sing_list2.loop_list())
    sing_list2.del_element(val=40)  # 删除元素值为40的结点
    print(sing_list2.loop_list())
    print(sing_list2.find_element(88))
    sing_list2.append_start(36)
    print(sing_list2.find_element(36))
    print(sing_list2.loop_list())

2.5 栈

栈是一种运算受限的线性表,因为只允许在表的一端进行插入和删除运算,而这一端被称为栈顶。

2.5.1 特点

栈的特点是先进后出。进出栈的时间复杂度为O(1)

2.5.2 应用

函数中存在局部变量和全局变量,函数的局部变量会随着函数完毕而销毁。即全局变量先入栈,然后局部变量入栈,函数完毕后,局部变量出栈。

2.5.3 代码实现

class MyStack(object):
    """
    先进后出
    """

    def __init__(self):
        self.__items = []

    # 进栈
    def push(self, item):
        self.__items.append(item)

    # 出栈
    def pop(self):
        return self.__items.pop()

    def loop_list(self):
        new_list = list()
        num = 0
        for item in self.__items:
            new_list.insert(-num, item)
            num += 1
        return f"栈顶{new_list}栈底"


if __name__ == '__main__':
    my_stack = MyStack()
    my_stack.push(1)
    my_stack.push(2)
    my_stack.push(3)
    print(my_stack.loop_list())
    print(my_stack.pop())
    print(my_stack.loop_list())
    print(my_stack.pop())
    print(my_stack.loop_list())

2.6 队列

队列是一种特殊的线性表,只允许在头部进行删除操作,在尾部进行插入操作,进行插入操作的端称为队尾,进行删除操作的端称为队头。

2.6.1 作用及应用

例如:用户发起多个请求任务,这些请求会进入队列,后端开启多个应用程序从队列中获取任务。
起到了缓冲压力的作用。

2.6.2 代码实现队列

class MyQueue(object):
    def __init__(self):
        self.__queue = []

    # 尾部插入
    def append_end(self, val):
        self.__queue.append(val)

    # 头部删除
    def del_head(self):
        return self.__queue.pop(0)

    # 判断队列是否为空
    def is_empty(self):
        return len(self.__queue) == 0

    # 返回队列的大小
    def size(self):
        return len(self.__queue)

    # 查看当前队列
    def loop_list(self):
        print("队头:", end=" ")
        for i in range(self.size()):
            print(self.__queue[i], end=" ")
        print("队尾")


if __name__ == '__main__':
    my_queue = MyQueue()
    my_queue.append_end(20)
    my_queue.append_end(30)
    my_queue.append_end(40)
    my_queue.loop_list()

2.7 双端队列

2.7.1 什么是双端队列

双端队列是一种具有队列和栈的性质的数据结构,双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行,双端队列可以在队列任意一端入队和出队。

2.7.2 双端队列代码实现

class MyDeque(object):
    def __init__(self):
        self.__deque = []

    # 队头加入元素
    def append_head(self, val):
        self.__deque.insert(0, val)

    # 队尾加入元素
    def append_tail(self, val):
        self.__deque.append(val)

    # 队头删除元素
    def del_head(self):
        self.__deque.pop(0)

    # 队尾删除元素
    def del_tail(self):
        self.__deque.pop(-1)

    # 返回队列的大小
    def size(self):
        return len(self.__deque)

    # 判断双端队列是否为空
    def is_empty(self):
        return self.size() == 0

    # 查看当前队列
    def loop_list(self):
        print("队头:", end=" ")
        for i in self.__deque:
            print(i, end=" ")
        print("队尾")


if __name__ == '__main__':
    my_deque = MyDeque()
    print(my_deque.is_empty())
    my_deque.append_head(10)
    my_deque.append_head(20)
    my_deque.append_head(30)
    my_deque.loop_list()
    print("队头删除")
    my_deque.del_head()
    my_deque.loop_list()
    print("队尾删除")
    my_deque.del_tail()
    my_deque.loop_list()
    my_deque.append_tail(40)
    my_deque.append_tail(50)
    my_deque.append_tail(60)
    my_deque.loop_list()
    print("队尾删除")
    my_deque.del_tail()
    my_deque.loop_list()


  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值