python依赖库的认识 python语言强大之处就是在于开源很多的库,别人已经帮你造好了轮子,你直接用就可以了。
这些库可以是自己封装的,当然可以用别人的,那这么好的库要去哪里找下载的地方呢。
python常用库下载地址:PyPI · The Python Package Index
这里离线包的下载地址,当然我们日常有网的正常情况下是可以直接需要什么包就去下载什么包的,所以分享几种安装包的方式:
python安装依赖包的方式: pip 安装: win+R打开命令窗口输入cmd,进入命令界面直接使用命令安装: pip install +包名
卸载包也可以直接利用pip 直接卸载:pip uninstall +包名
2.以上效率不高效:是因为下载地址的原因,可以引用国内的开源下载地址:
清华大学镜像源:https://pypi.tuna.tsinghua.edu.cn/simple/
阿里云镜像源:http://mirrors.aliyun.com/pypi/simple/
中国科技大学镜像源:https://pypi.mirrors.ustc.edu.cn/simple/
而后接上镜像源就可以下载的更加快速了,写法为:
pip install +包名 -i +镜像源
例如:
pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple/
以上是常规的安装方法,在线安装。
离线的python库安装方法: 首先认识一下,python库都装在什么地方:
1、找到安装的python环境路径
2、找到Lib文件夹:
3、找到site-packages文件夹:
这里面装的就是所有的目前你环境下的所有依赖包。
找包到此结束,接下来就是安装
安装思路:
1、我们可以在正常的python环境里面测试,你的项目依赖的这些包都没问题了,能够正常运行。
2、那么这些包我们就可以全部拷走,直接把整个site-packages文件夹,替换到你的新环境下面,新环境也是一样的路径直接替换。那么就可以用了,在正常环境里怎么跑的在内网或者无网环境里都可以跑起来。
单个安装python依赖包的方法: 1:使用源码安装
在官网PyPI · The Python Package Index搜索要下载的模块,找到Download Files,下载源码压缩包。
下载界面详解:
找个你想要的版本:
三个重要按钮的解释:
点击下载后:可以下载两种包一种压缩包一种是whl文件
重点:点击下载后左边往下滑动可以查看依赖包适配的python环境版本,一定要对应自己所装的python环境版本号
查看python环境版本的方法:
1、win+r cmd 然后输入 python -V 回归正题下载完后安装包,两种方式:
👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。
Python中的时间序列分析与预测技术 时间序列分析是数据科学中的重要领域,它涵盖了从数据收集到模型构建和预测的整个过程。Python作为一种强大的编程语言,在时间序列分析和预测方面有着丰富的工具和库。本文将介绍Python中常用的时间序列分析与预测技术,并通过代码实例演示其应用。
1. 数据准备 在进行时间序列分析之前,首先需要准备数据。我们将使用Python中的pandas库来读取和处理时间序列数据。
import pandas as pd # 读取时间序列数据 data = pd.read_csv('time_series_data.csv', parse_dates=['Date'], index_col='Date') # 查看数据的前几行 print(data.head()) 2. 可视化分析 可视化是理解时间序列数据的重要手段。Python中的matplotlib和seaborn库可以帮助我们进行数据可视化。
import matplotlib.pyplot as plt import seaborn as sns # 设置图形样式 sns.set_style('whitegrid') # 绘制时间序列图 plt.figure(figsize=(10, 6)) sns.lineplot(x=data.index, y='Value', data=data) plt.title('Time Series Data') plt.xlabel('Date') plt.ylabel('Value') plt.show() 3. 时间序列分解 时间序列通常包含趋势、季节性和随机性等成分。Python中的statsmodels库提供了用于时间序列分解的功能。
from statsmodels.tsa.seasonal import seasonal_decompose # 进行时间序列分解 result = seasonal_decompose(data['Value'], model='additive') # 绘制分解图 result.plot() plt.show() 4. 预测建模 时间序列预测是通过构建模型来预测未来数据点的值。常见的预测模型包括自回归移动平均模型(ARIMA)和长短期记忆网络(LSTM)等。下面以ARIMA模型为例进行预测建模。
from statsmodels.
文章目录 前言一、介绍二、详细分析1.核心组成2.实现步骤3.代码示例4.优缺点优点缺点 5.使用场景 总结 前言 装饰器模式属于结构型模式,又叫包装设计模式,动态的将责任添加到对象上。
一、介绍 装饰器模式又叫包装设计模式,为现有的类的一个包装,允许向一个现有的对象添加新的功能,同时又不改变其结构。给对象增加功能,一般两种方式 继承或关联组合,将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为来增强功能,这个就是装饰器模式,比继承更加灵活。
二、详细分析 1.核心组成 抽象组件(Component):定义装饰⽅方法的规范;被装饰者(ConcreteComponent):Component的具体实现,也就是我们要装饰的具体对象;装饰者组件(Decorator):定义具体装饰者的行为规范, 和Component角色有相同的接口,持有组件(Component)对象的实例例引用;具体装饰物(ConcreteDecorator):负责给构件对象装饰附加的功能。 2.实现步骤 创建抽象组件,定义公用的方法;创建被抽象者,实现抽象组件接口,实现基础方法;创建装饰者,实现抽象组件接口,可不做实现;创建具体抽象者,继承装饰者类,实现具体方法,添加抽象组件属性。 3.代码示例 抽象组件
/** * 抽象组件 */ public interface Computer { /** * 描述 */ String getDesc(); /** * 价格 * @return */ int getPrice(); } 被装饰者
/** * 被装饰者 */ public class DesktopComputer implements Computer { private String desc = "台式机"; @Override public String getDesc() { return desc; } @Override public int getPrice() { return 5000; } } /** * 被装饰者 */ public class LaptopComputer implements Computer { private String desc = "
算法的作用 我有很多外卖需求,我有很多店、用户、外卖员的坐标,如何科学分配给每一个外卖员合理的任务和路线,让整个外卖系统的用户等待时间较短,这个需求的大概设计思路是什么?主要用到传统算法还是AI算法?
为了科学分配每一个外卖员合理的任务和路线,以减少整个外卖系统的用户等待时间,可以采用如下设计思路:
需求理解:
确认系统需求,包括最大化配送效率、最小化用户等待时间以及保证配送质量等。
数据收集:
收集所有相关数据,包括店铺位置、用户位置、外卖员位置、历史配送数据等。
订单分配算法:
开发一个智能的订单分配系统,该系统需要根据当前的订单、外卖员位置、交通情况等动态分配订单给合适的外卖员。
路线规划:
对于已经分配好的订单,需要对外卖员的配送路线进行优化,以最短的时间完成配送。
实时调度:
根据实时情况,如交通变化、订单取消或新增、外卖员状态变化等,动态调整分配和路线。
在这个过程中,可能会用到的传统算法和AI算法包括:
传统算法: 图搜索算法:如Dijkstra或A*算法,用于计算最短路径。
贪心算法:在每一步选择当前看起来最优的配送任务。
最小生成树(MST):如Kruskal或Prim算法,可能用于连接多个配送点。
匹配算法:如匈牙利算法,用于解决外卖员和订单之间的匹配问题。
动态规划:处理配送路径的优化问题。
AI算法: 机器学习:基于历史数据预测需求、交通状况或配送时间等。
遗传算法:用于解决复杂的路线规划问题。
强化学习:可以训练模型以实时决策最佳的订单分配和路径规划。
神经网络:预测订单需求和交通模式。
综合使用: 在实践中,通常会将传统算法和AI算法结合起来使用。例如,可以使用AI算法预测订单需求和交通状况,然后使用传统算法来进行实际的订单分配和路线规划。这样结合两者的优点,既可以处理复杂的情况,又可以保证算法的实时性和可靠性。
具体选择哪种类型的算法取决于系统的需求、数据的可用性以及期望的准确度和响应时间。在设计系统时,需要考虑到算法的计算复杂度和响应时间,以确保能够快速有效地处理实时数据。此外,系统还应该具备学习和适应的能力,以不断优化配送策略,应对不断变化的环境和业务需求。
目录 scrcpy链接手机只能显示无法触控更新才刚发现scrcpy这个神级软件开启操作权限打开安全设置总结 scrcpy启动脚本/adb传输文件打包 scrcpy链接手机只能显示无法触控 更新 adb开启操作权限的这个对我自己失效了,因为没有更多的设备测试不知道是设备原因还是什么,再次使用会显示无权限,统一使用打开安全设置那个吧,打开后记得重启。如果打开安全设置那个重启还是无效可以尝试一下adb开启这个。
才刚发现scrcpy这个神级软件 使用matepadpro链接没有任何问题,也不需要进行额外的设置,系统是鸿蒙4
然后想链接手机(Mi 10s)时发现只能显示画面,无法进行触控操作
在网上搜索后发现有俩解决方式
开启操作权限 在github的问题中找到的,原因是因为手机安全设置屏蔽掉了usb传入的触控,
使用adb执行:
adb shell pm grant (pkgname) android.permission.WRITE_SECURE_SETTINGS 打开安全设置 这是看到别人帖子中的操作,在github中也有不少是利用这个弄成功的,更新:弄完后需要重启,这个也对我生效
将红框中的两开关打开即刻
总结 我先使用了打开安全设置,但是对我不起效,然后找到了adb命令,直接生效,然后为了确定是adb命令直接生效的,把安全设置的开关重新关上,依然可以触控,证明adb可以独立生效。
scrcpy启动脚本/adb传输文件打包 市面上有很多的scrcpy的GUI软件,但都在小50M,我一看scrcpy的本体都才15M
GUI是为了简化启动步骤,为了方便设置,编写了一个python脚本来直接启动scrcpy的主程序,并且对应着少许能用到的功能参数,将其打包成了exe,大小在6.8M,并且将adb的传输文件在这里进行了包装,弥补了无法scrcpy无法传输文件的遗憾,然后自己需要其他的一些adb命令也可以往里面添加。这里附上py全代码和打包的exe文件,模式只有这几个,需要其他的可以稍作改动即刻使用。
python脚本
import os from datetime import datetime import subprocess x = input("选择模式:\n1、普通\n2、普通息屏\n3、录屏\n4、录屏息屏\n") def sendcommod(cmd): subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) def getfile(): while(True): filename = input("拖入你传输的文件:") segments = filename.split('\\') desired_content = segments[-1] cod = r".\adb.exe push "+ filename +" /sdcard/Download/scrcpy/"+desired_content os.system(cod) def get1(): print("
目录
1. 定义
2. 诞生背景
3. 版本历史
4.优点
5.缺点
6. 基础用法
7. 十个应用场景
8. 其他示例
1. 定义 scikit-learn(也称为`sklearn`)是一个用于数据挖掘和数据分析的Python开源机器学习库,是基于NumPy、SciPy和matplotlib等科学计算库构建。
2. 诞生背景 scikit-learn由David Cournapeau在2007年发起,目的是提供一种易用、效率高的机器学习工具。最初作为Google Summer of Code项目之一,由一些志愿向着共同目标努力,逐步发展成为机器学习领域中的主流工具之一。
3. 版本历史 0.1 (2007): 初始版本,只包含很少的功能。
0.6 (2010): 增加了很多新的学习算法,并提升了之前算法的效率。
0.9 (2011):稳定版,增加了支持向量机、随机森林等流行算法。
0.18 (2016): 引入了更强大的模型评估和选择工具。
0.24 (2020): 增加了一些新的特性,如嵌套交叉验证、新的估计器等。
1.0 (2021): 标志性的版本,推出相较于以前的“契约”版本,大幅提升兼容性和性能。
可参考官方网站了解更多版本更新信息:https://scikit-learn.org/stable/whats_new.html
scikit-learn(通常简称为sklearn)是一个非常受欢迎的机器学习库,因其易用性和丰富的功能而广泛应用于数据科学和机器学习领域。下面列出了scikit-learn的一些主要优点和缺点。
4.优点 1. 易用性:
sklearn有着简洁清晰的API设计,使得入门和使用都非常方便。即便是新手,也能快速上手并实现复杂的机器学习模型。
2. 丰富的功能:
提供了多种机器学习算法,包括分类、回归、聚类、降维和数据预处理等。几乎覆盖了所有常用的机器学习功能。
3. 良好的文档:
官方提供了详细的文档和教程,帮助用户理解和使用不同的功能。同时,还有大量的社区资源和出版物支持。
4. 与其他Python工具兼容:
sklearn能够与其他科学计算和数据分析的库如NumPy、SciPy和matplotlib无缝对接,形成一个强大的生态系统。
5. 开源免费:
scikit-learn是一个开源项目,不仅可以免费使用,还可以通过贡献代码或提出问题参与到社区的建设中。
6. 性能较好:
在复杂度较低的数据集上,sklearn的运行性能表现优异,许多基础算法都经过高度优化。
5.缺点 1. 不能处理大规模数据:
sklearn主要设计用于中小规模数据集,对于大型数据集(如数十亿条记录),可能表现不佳,此时需要考虑使用更专门的大数据处理工具如Apache Spark的MLlib。
前面简单讲了机械臂的正解问题,即通过原位姿和控制各关节的角度得到终点位姿。而在实际应用的时候,我们通常都是知道起始点和末端终点的位姿,需要考虑如何达到,即运动学机械臂的运动学逆解问题。
求解操作臂运动学方程是一个非线性问题。我们需要考虑解的存在性、多重解性以及求解方法。
1.解的存在性 解是否存在的问题完全取决于操作臂的工作空间。工作空间是操作臂末端执行器所能达到的范围。如果解存在,则被指定的目标点必须在工作空间内。灵巧工作空间是指机器人的末端执行器能后从各个方向达到的空间区域,可达工作区间是机器人至少从一个方向上有一个方位可以达到的空间。灵巧工作空间是可达工作空间的子集。
2.多重解问题 在求解过程中可能遇到另一个问题就是多重解问题。
解的选择标准。最短形成?权重?干涉?
3.解法 代数解法 二幅角反切公式:
atan2_百度百科
几何解 4.MATLAB 实操 这真的是一件难过的事情,在求逆解的时候我曲曲折折烦躁不已,还是没有很好地求出来,还是参考了其他同学的代码。本来想通过控制几何关系来求解,但是应该是一堆bug,已经无力修改了,如果有人帮忙看看问题是什么就更好了
clear; clc; %% 参数 L1 = 100; L2 = 105; L3 = 98; L4 = 245; IN_theta = [0, -45, 30,10, 0]; % DH 参数 C_a = [0, 0, L2, L3, 0, 0, 0]; C_d = [0, L1, 0, 0, 0, 0, L4]; C_alpha = [0, -90, 0, 0, -90, 0, 0]; C_theta = [0, IN_theta(1), IN_theta(2), IN_theta(3), IN_theta(4)-90, IN_theta(5), 0]; T_target = eye(4); % 初始化结果为单位矩阵 for i = 1:6 T = [cosd(C_theta(i+1)), -sind(C_theta(i+1)), 0, C_a(i); sind(C_theta(i+1))*cosd(C_alpha(i)), cosd(C_theta(i+1))*cosd(C_alpha(i)), -sind(C_alpha(i)), -sind(C_alpha(i))*C_d(i+1); sind(C_theta(i+1))*sind(C_alpha(i)), cosd(C_theta(i+1))*sind(C_alpha(i)), cosd(C_alpha(i)), cosd(C_alpha(i))*C_d(i+1); 0, 0, 0, 1]; % 根据给定的公式计算 T[i] T_target = T_target * T; % 乘以每个 T[i] end % 提取目标位置 x_target = T_target(1, 4); y_target = T_target(2, 4); z_target = T_target(3, 4); % 初始化最优解 best_theta = []; min_error = inf; %% 逆运动学求解 for apha1 = -90:1:90 % L4与x轴的夹角,角度步长为1度 % 方程组定义 syms theta2 theta3 theta4 % 几何约束条件 eq1 = L4 * cosd(apha1) + L3 * cosd(apha1 + theta4) + L2 * cosd(theta2) == x_target; eq2 = L1 + L4 * sin(apha1) + L3 * sind(apha1 + theta4) + L2 * sind(theta2) == z_target; eq3 = theta2 == apha1 + theta3 + theta4; %eq4 = cos(2/pi + theta2 + theta4 + deg2rad(apha1)) == (L2^2 + L4^2 - (L3 * cosd(apha1 + theta4) + L2 * cosd(theta2))^2 + (L3 * sind(apha1 + theta4) + L2 * sind(theta2))^2) / (2 * L2 * L3); % 几何约束的非负性 %condition1 = L1 + L2 * cosd(theta2) > 0; %condition2 = L1 + L3 * cosd(apha1 + theta4) + L2 * cosd(theta2) > 0; % 组合所有方程和约束条件 equations = [eq1, eq2, eq3]; % 求解方程组 solutions = solve(equations, [theta2, theta3, theta4], 'Real', true); % 提取并验证解 theta2_sol = double(solutions.
👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。
优化Python中的数据结构与算法 Python是一种强大而灵活的编程语言,它提供了丰富的数据结构和算法库,但是在处理大规模数据或者需要高效运行的情况下,需要考虑一些优化技巧。本文将介绍一些Python中常用的数据结构与算法优化技巧,并附带代码实例,帮助你更好地理解和运用。
1. 使用内置数据结构 Python提供了许多内置的数据结构,如列表、字典、集合等,它们在大多数情况下都能满足需求,并且具有良好的性能。例如,使用字典来存储键值对,可以快速地进行查找操作:
# 使用字典来统计字符出现次数 text = "hello world" char_count = {} for char in text: if char in char_count: char_count[char] += 1 else: char_count[char] = 1 print(char_count) 2. 选择合适的数据结构 在选择数据结构时,要根据实际情况选择最适合的数据结构。例如,如果需要频繁地在序列中间插入或删除元素,应该选择链表而不是列表,因为链表的插入和删除操作复杂度更低:
# 使用链表实现队列 class Node: def __init__(self, value): self.value = value self.next = None class Queue: def __init__(self): self.head = None self.tail = None def enqueue(self, value): new_node = Node(value) if not self.head: self.head = new_node self.
1、插值的基本定义
设函数 y = f ( x ) y=f(x) y=f(x)在区间 [ a , b ] [a,b] [a,b]上有定义,且已知它在 n + 1 n+1 n+1个互异点 a ≤ x 0 < x 1 < . . . < x n ≤ b a\leq x_0<x_1<...<x_n\leq b a≤x0<x1<...<xn≤b上的函数值 y 0 , y 1 , . . . , y n y_0,y_1,...,y_n y0,y1,...,yn,若存在一个简单函数 p ( x ) p(x) p(x),使得
p ( x i ) = y i , i = 0 , 1 , 2 , .
本篇会加入个人的所谓鱼式疯言
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!
前言 在上篇中我们学习了 二叉树的基本概念 以及他们的特性结论,并运用到了 具体的题目 中去解决问题 。
而在本篇中,小编讲继续学习 二叉树 的基本操作, 主要围绕着我们 遍历二叉树 来讲解 , 人狠话不多,下面让我们切入主题吧 💥 💥 💥
目录 二叉树的遍历初识
前序遍历
中序遍历
4.后序遍历
层序遍历
二叉树遍历的应用
一. 二叉树的遍历初识 学习二叉树的结构,最简单的方式就是遍历,所谓遍历 是指 沿着某条搜索路线,依次树中的某个节点均做一次访问, 访问节点所做的操作 依赖于要解决的各种实际问题。
遍历是二叉树是最重要的操作之一,是 二叉树上进行其他运算 的基础
1. 二叉树的遍历简介 在遍历二叉树时, 如果没有进行某种约定,每个人都按照自己的方式来遍历, 得到的结果就比较乱, 如果我们按照某个规则 来遍历, 则每个人对于遍历结果都是相同的 , 如果 N 代表 根节点,L 代表左节点, R 代表 右节点, 那根据遍历的的节点有以下的遍历方式。
NLR: 前序遍历 (先序遍历) 根据 根——》 左 ——》 右 的顺序对二叉树进行遍历
LNR : ==中序遍历 ==: 根据 左——》 根——》 右 的顺序 对二叉树进行遍历
一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1148C - Codeforces
二、解题报告 1、思路分析 题目提示O(5n)的解法了,事实上我们O(3n)就能解决,关键在于1,n的处理
我们读入数据a[],代表初始数组,p[i]代表 i 的下标
如果p[i] != i
说明需要交换
a[p[i]] 一定能跟a[1]或者a[n]交换, a[i]也一定能跟1或n交换
假设 a[i] 的可交换位置为x,a[p[i]] 的可交换位置为y(x、y只可能为1、n)
那么我们使得元素i从p[i] -> y -> x -> i 就在3步之内让i到达了下标i
此时a[1] 和 a[n]可能不满足a[1] = 1, a[n] = n
事实上我们将每个元素调整完后再调整1和n即可
这也是为什么能从O(5n)优化到O(3n)
2、复杂度 时间复杂度: O(3n)空间复杂度:O(n)
3、代码详解 #include <bits/stdc++.h> using PII = std::pair<int, int>; const int N = 3e5 + 10; int p[N], a[N], n, s; std::vector<PII> path; void swap(int x, int y) { std::swap(p[a[x]], p[a[y]]); std::swap(a[x], a[y]); path.
背景 因为mac上的brew很久没用了,版本非常旧,随着mac os的更新,本机的homebrew大部分的功能都无法使用,幸好过去通过brew安装的工具比较少,于是决定重新安装一遍brew。
卸载旧版brew 法一:通过使用线上的uninstall.sh卸载brew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" 报错:
curl: (7) Failed to connect to raw.githubusercontent.com port 443 after 19 ms: Couldn't connect to server
所以采用法二。
法二:手动删除本地路径下的homebrew文件夹
rm -rf /opt/homebrew 因为本人主机是mac m1,homebrew相关软路径也保存在/opt/homebrew/bin下,已经一并删除了。
接着可以继续删除环境变量里的homebrew配置,因为本人打算安装新的homebrew,所以环境变量暂时保留。
安装新版brew 为了保证brew使用过程中的网络畅通,本次通过清华大学镜像安装。
打开网站:mirrors.tuna.tsinghua.edu.cn
搜索"homebrew"
网站有详细的使用镜像安装流程,总结操作步骤如下:
- 1、安装 CLT for Xcode (若未安装)
xcode-select --install - 2、在环境变量中设置镜像链接
vim ~/.bash_profile # 或 vim ~/.bashrc # 或 vim ~/.zshrc 在环境变量文件末尾添加下面几行
export HOMEBREW_INSTALL_FROM_API=1 export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api" export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles" export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git" export HOMEBREW_CORE_GIT_REMOTE="
🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
文章目录
1.0 会话技术
2.0 会话跟踪
2.1 会话跟踪 - Cookie
2.1.1 客户端获取 Cookie 的流程
2.1.2 Cookie 会话跟踪的特点
2.2 会话跟踪 - Session
2.2.1 客户端获取 SESSIONID 的过程
2.2.2 Session 会话跟踪的特点
2.2.3 Session 会话跟踪技术与 Cookie 会话跟踪技术的区别
2.3 会话跟踪 - JWT 令牌技术
2.3.1 客户端获取到令牌的过程
2.3.2 生成 JWT 令牌与校验 JWT令牌
1.0 会话技术 用户打开浏览器,访问 web 服务器的资源,会话建立,直到有一方断开,会话结束。在一次会话中可以包含多次请求和响应。
2.0 会话跟踪 一种维护浏览器状态的方法,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
1)客户端会话跟踪技术:Cookie
2)服务端会话跟踪技术:Session
3)JWT 令牌技术
2.1 会话跟踪 - Cookie Cookie 包含有关用户访问过的网站或应用程序的信息,以及用户在该网站或应用程序上的活动和偏好设置。通过使用 Cookie ,网站或应用程序可以识别用户并跟踪他们的活动,为用户提供个性化的服务和体验。
Cookie 可以存储各种信息,例如用户登录凭据、购物车内容、偏好设置等。网站或应用程序可以根据 Cookie 中的信息,实现记住用户登录状态、保存用户偏好设置等功能。
02 Rendering with JSX Your first JSX content In this section, we’ll implement the obligatory " Hello, World " JSX application. At this point, we’re just dipping our toes in the water; more in-depth examples will follow. We’ll also discuss what makes this syntax work well for declarative UI structures.
在本节中,我们将实现必需的“Hello, World”JSX应用程序。在这一点上,我们只是把脚趾伸进水里;后面会有更深入的例子。我们还将讨论是什么使这种语法能够很好地用于声明性UI结构。
Hello JSX 先创建一个基于ts的vite项目:
npm create vite 修改src/main.tsx:
import React from "react"; import ReactDOM from "react-dom/client"; import App from "
专栏介绍: 哈喽大家好,我是野生的编程萌新,首先感谢大家的观看。数据结构的学习者大多有这样的想法:数据结构很重要,一定要学好,但数据结构比较抽象,有些算法理解起来很困难,学的很累。我想让大家知道的是:数据结构非常有趣,很多算法是智慧的结晶,我希望大家在学习数据结构的过程是一种愉悦的心情感受。因此我开创了《数据结构》专栏,在这里我将把数据结构内容以有趣易懂的方式展现给大家。
1.树 1.1树的定义 之前我们一直谈的都是一对一的线性结构,可现实中,还是有很多一对多的情况需要处理,所以我们需要研究这种一对多的数据结构—“树”,考虑它的各种特性,来解决我们在编程中遇到的相关问题。树是一种非线性的数据结构,它是由n(n>=0)个节点组成的一个具有层次关系的集合,把它叫做树是因为它看起来像是一棵倒挂的树,也就是说它根是向上的,叶子是向下的。如下图:
n=0时被称为空树,在任意一棵非空树中:1.有且仅有一个特定的根节点 2.当n>1时,其余节点可以分为m个互不相交的有限集,其中每一个集合本身又都是一个树,被称为子树。如下图:
上面就是两个子树的简单例子,4、5组成的树是以2为根节点的子树,6、7组成的树是以3为结点的子树,对于树的定义还需要强调两点:
n>0时根节点是唯一的,不可能存在多个根节点,千万不要和现实中的大树混在一起,现实中的树有很多的根须,那时真实的树,数据结构中的树只有一个节点!m>0时,子树的个数没有限制,但是他们一定是不相交互的,即同一层次的各个节点之间不相互。像下面展示的结构就不符合树的定义,因为他们之间有交互。 1.2树的相关概念 我们一会就用下面这张图来展开介绍树的相关概念。
根节点:根节点是树结构中的第一个节点,也是整个树的起点。它是树的顶部节点。在上面的图中A是这棵树的根节点。一棵树只能有一个根节点。其他节点可以通过根节点进行访问和遍历。根节点是树的分支点,它可以有多个子节点。子节点通过边或链接与根节点相连,形成了树的层次结构。双亲结点/父节点:父节点是树结构中一个节点的上一级节点。每个节点都可以有一个父节点,除了根节点,因为根节点没有父节点。在上图中C就是H的父节点。父节点直接连接到其子节点,形成了树的层次结构。父节点是子节点的直接访问入口。通过父节点,可以找到子节点,进而访问和操作子节点。父节点可以有多个子节点。这使得树结构能够表示复杂的分支关系,每个父节点可以连接到不同的子节点。在上图中F就有3个子节点节点。兄弟节点:兄弟节点是树结构中同一层级的节点之间的关系。它们的父节点是相同的,即它们有相同的父节点。兄弟节点在树的结构中是相邻的,它们在同一层级的位置是相邻的。在上图中K、L、M就互为兄弟节点。祖先节点:从根到该节点所经分支上的所有节点,在上图中A是所有结点的祖先节点。子孙:以某节点为根的子树中任一节点都称为该节点的子孙,在上图中所有节点都是A节点的子孙。节点的度:节点的度是指该节点拥有的子节点的数量。在上图中A节点的度为6.叶节点/终端节点:叶节点是指没有子节点的节点,即度为0的节点,在上图中B就是一个叶节点。分支节点:度不为0的节点,除根节点之外,分支节点也叫做内部节点,在上图中C就是一个分支节点。树的度:一棵树中,最大的节点的度称为树的度,上面这张图中树的度为6. 1.3树的存储结构 说到存储结构,我们就会想到之前提到的顺序存储结构和链式存储结构,之前我们都是一对一的结构,现在变成树这样一对多的结构该怎么办呢?在这里我们要充分利用顺序存储和链式存储的特点,来实现对树的存储结构的表示。我们这里要介绍三种不同的表示方法:双亲表示法、孩子表示法、孩子兄弟表示法。
1.3.1双亲表示法 我们有的人可能因为种种原因没有孩子,但无论谁都不可能是从石头缝里蹦出来的。树这种结构也不例外,除了根节点之外,其余每个节点,不一定有子节点,但一定有且仅有一个双亲结点。我们假设以一组连续的空间存储树的节点,同时每个节点中,还要设置一个指针指向双亲结点的位置。也就是说,每个节点除了要知道自己是谁之外,还要知道双亲在哪里,它的结构如下:
其中,data是数据域,存放节点的数据信息,parent是指针域,存储该节点对应的双亲在数组中的下标 。以下是双亲表示法的节点结构的定义代码:
#define MAX_TREE_SIZE 100 typedef int TNDataType //树结点的数据类型,暂定为整型 typedef struct TreeNode { TNDataType data; int parent; }TNode; typedef struct Tree { TNode nodes[MAX_TREE_SIZE]; //节点数组 int r,n; //根节点的位置和节点数 }Tree; 有了上面的结构定义我们就可以来实现双亲表示法了。由于根节点是没有双亲的,所以我们约定根节点的位置域为-1,这就意味着,我们所有的节点都存在它的双亲的位置。下面图中的树结构可以用双亲表示法来表示:
因为按照数组那种连续存储结构画出来的话,图片横向太长不方便看,所以这里我用一个表格来表示方便观看,又简单明了:
下标dataparent0A-11B02C03D04E15F26G27H38I59J510K5 这样的存储结构,我们可以根据节点的parent指针很容易的找到它的双亲结点,所用的时间复杂度为O(1),直到parent为-1时,表示找到了树结点的根。可如果我们要知道节点的孩子是什么的话,需要遍历整个数组才行,那我们能否改进一下呢?why not?我们增加一个指针域存放第一个孩子(一般取最左边的子节点)在数组中的下标,这样就很容易得到节点的孩子,如果没有子节点的话,我们就把这个指针域设为-1。如下表表示:
下标dataparentfirstchild0A-111B042C053D074E1-15F286G2-17H3-18I5-19J5-110K5-1 这样对于有0或1个子节点的的双亲结点来说,这样的结构是为了解决要找子节点的问题,甚至是有多个子节点也能解决,知道了第一个子节点是谁,剩下的子节点也就一目了然了。就像上面表格中A的第一个子节点下标是1,即B节点的位置,而B第一个子节点的下标为4,所以在[1,4)区间中的所有整数在数组中所对应的下标元素就是A的子节点。
这时候又有一个新问题了,我们很关注兄节点之间的关系,双亲表示法无法体验出这种关系,那我们该怎么办呢?这时候我们只需要在双亲表示法的基础上增加一个指针用来指向子结点中最右侧的节点,即右兄弟节点,也就是说每一个节点如果它存在右兄弟,就存放右兄弟的下标,如果右兄弟不在就存放-1,如下表表示:
下标dataparentrightbrother0A-1-11B022C033D0-14E1-15F266G2-17H3-18I599J51010K5-1 存储结构的设计是一个非常灵活的过程,一个存储结构设计的是否合理,取决于基于该存储结构的运算是否合适、是否方便,时间复杂度好不好等。不是越多越好,有需要时再设计相应的结构,复杂的结构意味着更多的时间和空间的开销,简单的设计对应着快速的查找与删除,我们要根据实际情况进行取舍。
1.3.2孩子表示法 我们换一种思路:由于树中每个节点可能有多个子树,可以考虑使用多重链表,即每个节点有多个指针域,其中每个指针域指向一棵子树的根节点,我们把这种方法叫做多重链表表示法。不过树的每个节点度都是不一样的,所以可以设计两种解决方案。
1.3.2.1方案一 一种方案就是指针域的个数等于树的度,前面我们提到了,树的度是各个节点度的最大值。其结构如下表示:
其中,data就是数据域,child1~childn是指针域,用来指向该节点的孩子节点。在双亲表示法我们提到的那个树用这种方法实现如下图:
这种方法对于树中各个节点的度相差很大,显然是浪费空间的,因为有很多节点,它的指针域是空的。如果树的各个节点的度相差很小的话,那就意味着开辟的空间被充分利用了,这是存储结构的缺点反而成了优点。既然很多指针域为空,为什么不按需求分配空间呢?于是我们有了第二种方案。
1.3.2.2方案二 第二种方案每个结点的指针域等于该节点的度,我们专门取一个位置来存储节点指针域的个数,其结构如下:
其中,data为数据域,degree为节点的度,也就是存储该节点的子节点的个数,child1~childn为指针域,指向该节点的各个子结点。这种方法实现如下图:
这种方法克服了浪费空间的缺点,对空间的利用率很高了,但是由于各个节点的链表是不相同的结构,加上要维护节点的度的数值,在运算上就会带来时间上的损耗,能否有更好的方法,既可以减少空指针的浪费又能使结点的结构相同。仔细观察,我们为了要遍历整棵树,把每个节点放在一个顺序存储结构的数组中是合理的,但每个结点的孩子有多少是不确定的,所以我们在对每个结点的孩子建立一个单链表体现他们的关系。
这就是我们要讲的孩子表示法。具体办法是:把每个节点的子节点排列起来,以单链表作为存储结构,则n个节点有n个子链表,如果是叶子节点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放在一个一维数组中。如下图:
为此我们设计两个节点结构,一个是子链表的子节点,如下表表示:
其中,child是数据域,用于存储某个节点在表头数组中的下标;next是指针域,用来存储指向某节点的下一个子节点的指针。另一个是表头数组的表头结点,如下表表示:
其中,data是数据域,存储某节点的数据信息;firstchild是头指针域,存放该节点的子链表的头指针。以下是我们的孩子表示法的结构定义代码:
文章目录 客户端缓存与服务器缓存的区别客户端缓存浏览器缓存应用程序缓存优点缺点 服务器缓存优点缺点 HTTP缓存控制头字段Cache-ControlExpiresLast-ModifiedETag 缓存策略的优化与实践经验分享1. 使用合适的缓存头字段2. 结合使用Last-Modified和ETag3. 利用CDN进行缓存4. 实现缓存失效机制5. 缓存预热6. 监控与调优7. 避免缓存雪崩 客户端缓存与服务器缓存的区别 客户端缓存 客户端缓存是指将数据存储在用户的设备上,以减少网络请求的频率和提高应用性能。常见的客户端缓存包括浏览器缓存和应用程序缓存。
浏览器缓存 浏览器缓存是指浏览器将网站的数据(如HTML、CSS、JavaScript、图片等)存储在本地磁盘或内存中,以便在用户再次访问时能快速加载这些资源,而不需要重新从服务器获取。
应用程序缓存 应用程序缓存是指将数据存储在客户端应用程序的本地存储中,以减少网络请求的频率和提高应用性能。常见的应用程序缓存技术包括本地存储(如HTML5的LocalStorage和SessionStorage)、IndexDB,以及移动应用中的本地数据库(如SQLite)。
优点 减少服务器负载:减少了对服务器的请求次数,从而减轻了服务器的压力。提高加载速度:从本地缓存加载资源要比从服务器获取快得多,从而提升用户体验。 缺点 数据可能过时:客户端缓存的数据可能与服务器上的最新数据不一致,导致用户看到的内容不是最新的。存储空间有限:客户端设备的存储空间是有限的,尤其是在移动设备上。安全性风险:本地存储的数据可能面临安全风险,如果没有适当的加密和保护措施,敏感数据可能会被恶意软件或用户窃取。 通过合理利用客户端缓存,可以显著提升应用的性能和用户体验,但同时需要注意数据一致性和安全性问题。结合具体应用场景,选择合适的缓存技术和策略,才能实现最佳效果。
服务器缓存 服务器缓存是指将数据存储在服务器端的缓存系统中,以减少对数据库或其他后端服务的访问频率,从而提高系统性能。常见的服务器缓存包括内存缓存(如Redis、Memcached)和文件缓存。
优点 减少数据库负载:通过缓存数据库查询结果,减少了对数据库的访问频率,从而减轻数据库的压力。提高响应速度:从缓存中读取数据要比从数据库中读取快得多,从而提高了服务器的响应速度。 缺点 缓存一致性问题:缓存中的数据可能与数据库中的数据不一致,需要采取适当的缓存失效机制来确保数据一致性。额外的维护成本:需要设计和维护缓存系统,增加了系统的复杂度。 HTTP缓存控制头字段 HTTP协议提供了一些头字段来控制缓存行为,常见的包括Cache-Control、Expires、Last-Modified和ETag。
Cache-Control Cache-Control头字段用于指定请求和响应的缓存机制。它可以包含多个指令,常见的指令包括:
public:表示响应可以被任何缓存(包括浏览器、CDN等)存储。private:表示响应只能被单个用户的浏览器缓存存储,不能被共享缓存存储。no-cache:强制缓存进行重新验证,即使缓存副本是新鲜的。no-store:禁止任何缓存存储响应数据,每次请求都必须从服务器获取。max-age=:指定响应可以被缓存的最大时间,以秒为单位。 例如:
Cache-Control: public, max-age=3600 表示响应可以被任何缓存存储,并且缓存的有效期为3600秒(1小时)。
Expires Expires头字段指定响应过期的日期和时间,格式为HTTP日期。它用于指示缓存何时认为响应是陈旧的。
例如:
Expires: Wed, 21 Oct 2024 07:28:00 GMT 表示响应在2024年10月21日7点28分后过期。
需要注意的是,如果同时存在Cache-Control和Expires头字段,Cache-Control优先级更高。
Last-Modified Last-Modified头字段指示资源的最后修改时间。服务器可以在响应中包含这个头字段,客户端在后续请求中可以使用If-Modified-Since头字段来询问服务器资源是否在某个时间点之后修改过。
例如:
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT 客户端请求时可以包含:
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT 如果资源自该时间点之后未修改,服务器可以返回304 Not Modified响应,指示客户端使用缓存数据。
按照官网说明,引入 springdoc-openapi-starter-webmvc-ui后应该就可以直接访问swagger-ui.html或者swagger-ui/index.html就可以出现swagger页面了,但是我引入后,访问提示报错404.
在我的项目中,有其他依赖间接引入了org.webjars:swagger-ui,但是,这个jar包的版本必须和springdoc-openapi-starter-webmvc-ui声明的一致,否则默认配置就会无法找到静态资源。可以在springdoc-openapi-starter-common这个依赖的根目录找到springdoc.config.properties配置文件,如果里面写的和实际项目引入的不一致,就会出问题。
可以通过调整依赖,让他们一致,或者在application.yaml中设置变量springdoc.swagger-ui.version=引入的版本。
🎁个人主页:我们的五年
🔍系列专栏:数据结构课程学习
🎉欢迎大家点赞👍评论📝收藏⭐文章
目录
🍩1.大堆和小堆
🍩2.向上调整算法建堆和向下调整算法建堆:
🍟向上调整算法:
🍟向下调整算法:
🍟用这两种方法建堆的时间复杂度:
🍩3.堆排序:
🍩1.大堆和小堆 要想弄明白堆排序,我们先来看看大堆和小堆的概念和区别: 注意:堆是完全二叉树。
大堆:父节点都大于孩子节点。
小堆:父节点都小于孩子节点。
🍩2.向上调整算法建堆和向下调整算法建堆: 注:根节点我定为0
🍟向上调整算法: 向下调整算法调整该节点的前提是该节点以上的树已经是堆(大堆或者小堆),但是开始的时候,树里面的元素是随便放置的,但是可以把根元素以上看成一个堆,然后向上调整从2*0+1(第二层左边的元素)的位置开始就可以了。
以向上调整建立大堆为例:
从已经建好的堆的下一层开始向上调整,这里可以把根看成小堆,要想把整个二叉树调整为小堆形式,我们就要从根的下一层,把每个元素都进行一次向上调整。
向上调整的实现:
根该节点开始,我们把该节点与它的父节点进行比较,因为该节点以上的节点已经是大堆,此时的根是该树最大元素,所以只要和根比较谁大,如果比根大就交换位置,这样增加一个元素以后,该树还是大堆。
从上面图来看,向上调整结束的条件为该节点到达根节点,上面没有元素了。
由孩子节点的下标找到父节点的下标是:parent=(child-1)/2。
实现代码:
void AdjustUp(int* a,int child) { //该节点开始比较 int parent = (child - 1 - 1) / 2; while (child > 0) //当节点到达根节点,就没有父亲节点了,就停止 { if (a[parent] < a[child]) { int tmp = a[parent]; a[parent] = a[child]; a[child] = a[parent]; child = parent; parent = (child - 1 - 1) / 2; } else { break; } } } 🍟向下调整算法: 向下调整算法的要求就是左右子树已经是堆(大堆或者小堆)。结束的条件是孩子节点为NULL。
题目 n(3<=n<=100)个点的有向图,
图的边的关系未知,但保证以下两点:
1. 只存在j->i(i<j)的边
2. 对于任意三个点i、j、k(i<j<k),要么k可以到达i,要么k可以到达j,要么j可以到达i
每次你可以询问两个点,? i j(i<j),如果j可以到达i,返回YES,否则返回NO
你需要将图染成黑白两色,
使得黑色的任意两个点x、y(x<y),都满足y可到达x,
且白色的任意两个点x、y(x<y),都满足y可到达x
你有最多2n次询问机会,可以证明答案一定存在
最终输出n个点染色的情况,输出0表示染黑色,为1表示染白色
图不是交互式的,也就是说图一开始是固定下来的,不会随询问的改变而动态变更
实际t(t<=100)组样例,但保证sumn不超过1000
思路来源 乱搞ac
题解 其实也不完全是算乱搞,一开始wa了,后来看了下数据的反例修了下就过了
首先原图肯定可以拆分成两条链,这样对于三个点,一定存在两点共链,就满足题意了
然后考虑这个图的形状可能是怎样的,一开始是想的双螺旋结构的
维护两条链,开始只有点n,然后点n-1如果在点n下游就接上去,否则就新开一条链,
然后这两条链可以汇集在一点,后续在这点之后继续扩,类似Y型,
然后Y型后续还能拆成两个分支,再成两条链,后续两条链再能合并成一个点,重复若干次
然后输出的话,就沿着点n,往下找一个下游,直接找齐一条链,这样另一条链就自然另一种颜色
但是,遇到了一个反例
可以发现,在点5形成Y字型,后接点4之后,新来的点3并没有续到点4上,
也没有续到点5上,而是续到了点6上,
此时我找的链是10->9->6->5->4->1,就使得另一条链8->7和3->2不连通
而此时应该是10->9->6->3->2,另一条链8->7->5->4->1
这表明,当出现Y形状的交点时(也就是图中的5点)后,代表两条链合成了一条链(合并)
这时候这条链往下接了点4,
新来的点3,可以接在点4后,也可以接在点5后,也可以接在点6后,也可以接在点7后,
这四种情况,都能使原图被划分成两条链,
对于5->4链上有多个点的情况,不妨只视为有Y字形交点(点5)、Y字形尾部点(点4)两个点
因为在链中间的点后面续新的点,都可以看成是在交点后面续新的点,不影响两条链的性质
此外注意到,点3的出现,使得一条链又变成了两条链(拆分)
这两条链后续仍然可能再形成新的Y字形,也就是分分合合的过程可能出现若干次
所以,做法是:
1. 初始时,点n当第一条链的链尾
2. 当前如果有两条链,记他们的链尾分别为f和s,当前要续的点是i,
(1)i如果能同时往f和s后面续,代表两条链合成了一条
(2)否则,如果能往第一条链后面续,就往第一条后面续
(3)否则,如果能往第二条链后面续,就往第二条后面续
(4)否则,两条链都续不了,记两条链上一次合并成Y字形交点(也就是上图的点5)为las,如果能往las后面续,就往las后面续
(5)否则,记las的两个父亲为f1、f2(也就是上图的点6、点7),如果能往f1后面续,就往f1后面续
(6)否则往f2后面续
3. 如果当前只有一条链,能续则续,不能续的时候,新开一条链即可
代码里加了如果不存在则为-1的情况,以及加了询问的记忆化,统一了部分情况的分类讨论
询问次数想了一下,是不超过2n的,因为最极端的情况是上图点3不能续到点4后面的情况
此时有4种情况,需要最多询问3次才能确认是哪种情况,
而这种情况的出现,说明前面有一个只询问了1次的点,也就是在点5下面的点4,
这两个点均摊一下,平均次数就不超过2了
代码 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);++i) #define per(i,a,b) for(int i=(a);i>=(b);--i) typedef long long ll; typedef double db; typedef pair<int,int> P; #define fi first #define se second #define pb push_back #define dbg(x) cerr<<(#x)<<"
[猫头虎分享21天微信小程序基础入门教程]第21天:小程序的社交分享与消息推送
第21天:小程序的社交分享与消息推送 📲 自我介绍 大家好,我是猫头虎,一名全栈软件工程师。今天我们继续微信小程序的学习,重点了解如何实现社交分享与消息推送功能。这些功能可以帮助你提高小程序的用户互动和活跃度。🚀
社交分享 微信小程序提供了丰富的分享功能,可以让用户将内容分享到微信好友和朋友圈。
一、实现分享功能 1. 配置分享菜单 在小程序的 app.json 文件中配置分享菜单:
{ "window": { "navigationBarTitleText": "小程序", "navigationStyle": "custom" }, "tabBar": { "list": [{ "pagePath": "pages/index/index", "text": "首页" }] } } 2. 使用 onShareAppMessage 实现分享 在页面的 js 文件中实现分享逻辑:
Page({ onShareAppMessage() { return { title: '分享标题', path: '/pages/index/index', imageUrl: '/images/share-image.png', // 分享图片 success(res) { console.log('分享成功:', res); }, fail(err) { console.error('分享失败:', err); } }; } }); 二、自定义分享内容 1. 动态生成分享内容 根据页面内容动态生成分享标题和路径: