基本排序算法
一、冒泡排序1. 什么是冒泡排序?冒泡排序(Bubble Sort)是一种简单直观的排序算法。它得名于其工作方式,即较小(或较大)的元素会像水中的气泡一样,通过不断交换,慢慢“浮”到数列的顶端。 a. 核心思想它重复地遍历待排序的数列,一次比较两个相邻的元素,如果它们的顺序(如从大到小或从小到大)错误就把它们交换过来。遍历数列的工作会重复地进行,直到没有再需要交换的元素为止,这意味着整个数列已经排序完成。 b. 工作原理 比较相邻元素:从列表的第一个元素开始,比较它和下一个元素。 交换:如果第一个元素比第二个元素大(以升序为例),就交换它们的位置。 向后移动:继续比较下一对相邻的元素(即新的第二个和第三个元素),重复步骤2。 完成一轮:持续这个过程,直到列表的末尾。经过第一轮遍历后,最大的元素会被放置在列表的最后一个位置。 重复:对除了最后一个元素之外的所有元素,重复以上步骤。每一轮遍历都会将当前未排序部分的最大元素放到其最终位置。 终止:当某一轮遍历中没有发生任何交换时,说明列表已经完全排序,可以提前终止算法。 2. 图解示例让我们用一个简单的例子 [5, 1, 4, 2,...
通信方式对比—UART/SPI/IIC
一、UARTUART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种非常基础且广泛使用的串行通信协议。它不是像SPI或I²C那样的“总线”,而通常是一种点对点的通信方式。几乎所有的微控制器和计算机都包含UART硬件,它也是我们最常接触到的调试接口(例如通过USB转串口模块在电脑上查看单片机的打印信息)。 1. UART的核心概念 通用(Universal): 意味着它的通信参数(如速度、数据位、校验位等)是可配置的,可以与各种不同的设备进行通信。 异步(Asynchronous): 这是UART最核心的特点。与SPI和I²C不同,UART通信的双方没有共享的时钟线。发送方和接收方必须事先约定好相同的通信速率(波特率),接收方依靠数据信号本身的变化(起始位)来开始同步并按约定的速率接收数据。 收发器(Receiver/Transmitter): UART硬件模块通常包含一个独立的接收器和一个独立的发送器,因此可以同时进行数据的接收和发送,实现 全双工(Full-Duplex) 通信。 2....
时间复杂度与空间复杂度分析
在算法竞赛和面试中,根据题目给出的数据量大小来推断所需算法的时间复杂度和空间复杂度,是一项至关重要的核心技能。它能迅速排除掉不切实际的暴力解法,将思考方向聚焦在可行的算法上。 一、核心原理:计算机的运算速度这个推断方法的基石是对现代计算机运算速度的一个估算。在算法竞赛平台(如 LeetCode、Codeforces、牛客网等)上,评测机通常能在 1秒内执行大约 10^7 到 10^8 次计算操作。 为了保险起见,我们通常以 10^8 作为上限参考,但更稳妥的估算是 10^7。这意味着,如果你的算法总计算量超过了这个数量级,就很可能会“超时”(Time Limit Exceeded, TLE)。 基本公式:数据规模 N 带入 时间复杂度表达式 ≈ 总计算量 ≤ 10^8 二、时间复杂度 “速查表” (Cheat Sheet)这张表是解决算法问题的“第一直觉”,强烈建议记在心里。假设题目给出的主要数据规模是 N。 数据规模 N 的范围 可接受的时间复杂度 常见算法示例 N ≤ 10~12 O(N!)、O(N *...
Python中的装饰器
1. 开篇:为什么需要装饰器?装饰器的核心思想是在不修改被装饰对象(通常是函数或方法)的源代码和调用方式的前提下,为其增加额外的功能。 一个简单的问题场景假设我们有一个核心业务函数: 12def business_logic(): print("执行核心业务逻辑...") 现在,我们有一个新的需求:在每次执行这个函数前后,打印一条日志,记录函数的开始和结束。 不使用装饰器的解决方案方案一:直接修改函数代码(最差的方式) 1234def business_logic(): print("开始执行函数...") print("执行核心业务逻辑...") print("函数执行结束。") 弊端: 违反了开放/封闭原则:我们修改了函数的内部实现。 代码冗余:如果有很多函数都需要这个日志功能,我们就得在每个函数里重复添加这些代码,违反了 DRY (Don’t Repeat Yourself)...
Python中的全局变量与局部变量
核心概念:作用域(Scope)在理解变量之前,必须先理解作用域。作用域就是一个变量能够被访问(可被读取和写入)的区域。把它想象成一个房子的不同房间: 全局作用域(Global Scope):就像房子的客厅。所有房间的人都能看到客厅里的东西。在 Python 中,这是在所有函数之外的顶层代码区域。 局部作用域(Local Scope):就像房子的卧室。只有在卧室里的人才能看到和使用卧室里的东西。在 Python 中,这通常是指一个函数内部的区域。 1. 局部变量 (Local Variables)定义:在一个函数内部创建(首次赋值)的变量,就是局部变量。 特性: 生命周期:当函数被调用时,局部变量被创建;当函数执行完毕返回时,局部变量就被销毁。 访问范围:它只能在其被定义的函数内部被访问。在函数外部尝试访问它会导致 NameError。 示例: 123456789101112131415def my_function(): # 'message' 是一个局部变量,它在 my_function 内部被创建 message =...
CPP学习笔记—lambda表达式
Lambda 表达式是 C++11 引入的一项革命性特性,它极大地改变了 C++ 的编程风格,尤其是在与标准模板库(STL)配合使用时。它允许我们在代码中需要一个函数的地方,直接定义一个匿名的、临时的函数对象。 1. 什么是 Lambda 表达式?简单来说,Lambda 表达式就是一个可调用的代码单元,可以把它理解为一个匿名的内联函数。与普通函数不同,它可以在函数内部定义,并且可以“捕获”其所在作用域中的变量。 2. 为什么需要 Lambda 表达式?在 C++11 之前,如果想向一个算法(如 std::sort)传递一个简单的、一次性的比较逻辑,通常需要: 定义一个全局函数或静态成员函数:这会污染命名空间,并且逻辑与使用它的地方相隔甚远。 定义一个函数对象(Functor):需要编写一个完整的类,重载 operator()。这非常繁琐。 Lambda 的优势: 代码局部性:将逻辑直接写在使用它的地方,代码更紧凑,可读性更高。 简洁性:避免了编写独立的函数或函数对象类的样板代码。 状态捕获:可以方便地访问和使用其定义作用域内的变量,这是普通函数难以做到的。 3....
二分查找
包含了二分查找的两种写法(左闭右闭和左闭右开)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152"""题目描述:给定数组nums = [1, 3, 6, 8, 9, 12, 16, 19, 20]target = 6若数组中包含target,则返回数组下标,否则返回-1"""def binary_search_1(nums, target): """左闭右闭区间的二分搜索写法""" left, right = 0, len(nums) - 1 while left <= right: middle = left + (right - left) // 2 if nums[middle] > target: right = middle - 1 ...
CPP学习笔记—标准模板库(STL)中的容器
一、什么是容器?为什么它如此重要?简单来说,C++ 容器是用来存储和管理一组对象的类模板。它们就像是工具箱里不同种类的收纳盒:有的适合快速取放(像数组),有的适合保持物品有序(像分类文件柜),还有的适合快速查找(像带索引的卡片盒)。 使用 STL 容器的好处是巨大的: 代码更简洁:你无需从零开始实现链表、哈希表等复杂数据结构。 性能更优越:STL 容器经过了高度优化,性能通常比手写的要好。 代码更安全:它们处理了内存管理的细节,减少了内存泄漏和越界访问的风险。 互操作性强:所有容器都遵循一套统一的接口(如 begin(), end(), size()),可以与 STL 的迭代器和算法无缝协作,威力倍增。 二、容器的分类与概览C++ 的容器主要分为四大类,分别是序列容器、关联容器、无序关联容器和容器适配器。 类别 容器 特点 序列容器 vector, deque, list, array 元素按线性顺序排列,可通过位置访问。 关联容器 map, set, multimap, multiset 元素根据键值自动排序,查找速度快 (O(log...
CPP学习笔记—指针
一、指针到底是什么?—— 内存地址的“房卡”想象一下计算机的内存是一家长长的酒店,里面有无数的房间,每个房间都有一个独一无二的门牌号(内存地址)。 普通变量:就像一个房间,里面存放着具体的东西(值)。 1int age = 25; // 在一个叫 `age` 的房间里,放了数字 25。 指针:它本身也是一个变量,但它里面存放的不是普通的东西,而是一张房卡。这张房卡上写着另一个房间的门牌号。 所以,指针是一个存储了另一个变量内存地址的变量。通过这张“房卡”,我们就能找到并操作那个特定的“房间”。 二、两大核心操作符:& (取地址) 和 * (解引用)要玩转指针,必须掌握这两个“魔法”操作符。 1. & (取地址符) —— “制作房卡”& 符号的作用是获取一个变量的内存地址。可以把它读作“…的地址”。 12345int age = 25; // 一个存放 25 的房间 `age`int* p_age = &age; // 创建一张叫 `p_age` 的房卡, // 上面记录 `age`...
Python输入输出处理
核心知识点 input() vs sys.stdin.readline() input(): 内置函数,使用方便,但速度较慢。它会读取一行,去除末尾的换行符 \n,并返回一个字符串。 sys.stdin.readline(): 速度更快,因为它使用了缓冲区。它会读取一行,但保留末尾的换行符 \n。因此,通常需要配合 .strip() 或 .rstrip() 使用。 print() vs sys.stdout.write() print(): 内置函数,功能强大,可以自动在末尾添加换行符,但相对较慢。 sys.stdout.write(): 速度更快,但只接受字符串作为参数,且不会自动添加换行符,需要手动添加 '\n'。 对于追求极致性能的竞赛,推荐使用 sys 模块。 12import sys# 之后就可以使用 sys.stdin.readline() 和 sys.stdout.write() 一、 输入 (Input)场景1:读取单行数据1.1 读取一个字符串12345678910# 方法一:使用 input() (方便,但稍慢)s =...