一.树 1.1树的定义 树是一种非线性的数据结构,它是有n个有限结点组成的一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根在上,叶在下的。
在树中有一个特殊的结点,称为根结点,根结点没有前驱结点
除根结点外,其余的结点被分成了M个互不相交的集合T1、T2、......、Tm,其中每一个集合Ti又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱结点,但可以有0个或多个后继结点。因此:树是递归定义的。
注意:树的结点之间是不可以有交集的,有交集的话就不是树了,至于是什么我们后期再说。
如下图:以A为根节点的就不是树,以D为结点的才是树。
1.2树的基本概念 结点的度:一个结点含有的子树的个数称为该结点的度叶子结点:度为0的结点称为叶子结点分支结点:度不为0的结点双亲结点/父结点:若一个结点包含子节点,则这个结点称为其子结点的父节点孩子结点/子节点:一个结点含有的子树的根结点称为该结点的子节点兄弟结点:具有相同父节点的结点互称为兄弟结点树的度:一棵树中,度最大的结点的度称为树的度结点的层次:定义根为第一层,根的子节点为第二层,依次类推即可树的高度或深度:树中结点的最大层次堂兄弟结点:双亲在同一层的结点互为堂兄弟结点结点的祖先:从根到该结点所经分支上的所有结点。子孙:以某结点为根的子树的任一结点都称为该结点的子孙。森林:由m棵互不相交的树组成的集合称为森林。 1.3树的表示方法 我们在这里学习三种树的表示方法,分别为双亲表示法、树的孩子表示法、左孩子右兄弟表示法。
我们将用这三种方法来表示这一棵树:
1.3.1父节点表示法(双亲表示法) 树的父节点表示法,是利用顺序表来完成的,怎么做呢?
首先,我们创建顺序表结构体来存储结点的内容以及父节点的下标。我们将这个结构体称为结点结构体。
然后,我们创建树的结构体,其中一个是结点结构体数组,一个是数组内元素个数。
由此,我们可以写出如下代码:
//双亲表示法 //顺序表的方式存储 //顺序表存储结点的数据和双亲结点的下标 typedef int DataType; typedef struct Node { DataType data;//数据域 int parent;//双亲结点下标 }Node; //树-->结点组成的数组 typedef struct Tree { Node NodeArr[10];//保存结点的数组,也可以动态申请 int size;//结点个数 }; 在这里需要我们大家注意的是:由于根节点没有前驱结点,我们将其的父节点特别记为-1. 下面我们来表示一下这棵树。
1.3.2树的孩子表示法 树的孩子表示法是通过顺序表和链表结合的形式表示的
它的原理是:
先定义一个单链表结构体表示一个父节点的所有孩子结点的下标,一个孩子指向另外一个孩子然后用一个顺序表结构体存储当前结点的数值以及这个结点的第一个孩子结点的下标最后定义一个树结构体 那么,我们就可以写出如下代码:
typedef int DataType; //链表 struct ListNode { int child;//当前孩子结点下标 struct Listnode* next;//下一个孩子 }; //顺序表 struct Node { DataType data;//结点的数据 ListNode* FirstChild;//第一个孩子 }; struct Tree { Node NodeArr[10]; int size; }; 现在我们画图来理解一下这个方法
系列篇章💥 AI大模型探索之路-实战篇4:深入DB-GPT数据应用开发框架调研
AI大模型探索之路-实战篇5:探索Open Interpreter开放代码解释器调研
AI大模型探索之路-实战篇6:掌握Function Calling的详细流程
AI大模型探索之路-实战篇7:Function Calling技术实战自动生成函数
AI大模型探索之路-实战篇8:多轮对话与Function Calling技术应用
AI大模型探索之路-实战篇9:探究Agent智能数据分析平台的架构与功能
AI大模型探索之路-实战篇10:数据预处理的艺术:构建Agent智能数据分析平台的基础
AI大模型探索之路-实战篇11: Function Calling技术整合:强化Agent智能数据分析平台功能
AI大模型探索之路-实战篇12: 构建互动式Agent智能数据分析平台:实现多轮对话控制
AI大模型探索之路-实战篇13: 从对话到报告:打造能记录和分析的Agent智能数据分析平台
AI大模型探索之路-实战篇14: 集成本地Python代码解释器:强化Agent智能数据分析平台
目录 系列篇章💥一、前言二、Memory功能实现之在线云盘类封装1、创建OpenAi客户端2、定义本地云盘文件目录创建方法3、定义doc文档创建方法4、定义文件内容追加方法5、定义获取文件内容的方法6、定义清理文件内容的方法7、定义获取文件列表的方法8、定义文件重命名的方法9、定义删除文件的方法10、定义追加图片的方法11、定义一个云盘文件操作类 三、Memory功能实现之消息工具类封装1、定义消息管理类2、测试-查看消息管理器3、测试-追加消息4、测试-删除消息5、添加背景知识 四、Tools功能之函数封装1、获取表结构基本信息(工具函数)2、提取SQL数据到python变量(辅助函数)3、python代码解释器(工具函数)4、function函数信息生成器(辅助函数)5、function函数调用辅助类 三、结语 一、前言 在前面篇章中我们实现了Agent智能数据分析平台中的Tools和Memory相关代码落地实践,本文中我们将对这两大块功能代码进行整合封装。
二、Memory功能实现之在线云盘类封装 1、创建OpenAi客户端 ## 导入依赖 import openai import os import numpy as np import pandas as pd import json import io from openai import OpenAI import inspect import pymysql import tiktoken from docx import Document import matplotlib.pyplot as plt import seaborn as sns import tempfile import ast from IPython.
最近需要用IDEA完成课设,同时也需要用到SQL Server实现数据持久化,而刚开始连接数据库时,即使成功用SQL Server身份验证方式连接后,在IDEA里还是不能与SQL成功连接,这里就很可能是防火墙的问题,或者是SQL Server 配置等问题,这里分享idea连接SQL server数据库的一篇经验贴,希望有这方面需要或问题的小伙伴能少走一些弯路,同时也希望大家批评指正,相互学习~
:) 0、开发环境说明 SQL Server 16.0.1000.6 IntelliJ IDEA 2021.1.1 1、启用SQL sever身份验证 打开SQL server数据库,
首先先连接服务器实例,第一次连接可以选择Windows 身份验证 (后面可更改)
右键点击服务器,选择 属性
选择 “安全性”页签,选择 SQL Server 和 Windows 身份验证模式。
确认更改,重启 SQL Server 实例。
下一步就可以设置SQL server 的身份验证密码了
打开 SQL Server Management Studio (SSMS),展开 安全性 文件夹,展开 登录名 文件夹,
右键点击 'sa' 用户,选择 属性,选择SQL Server身份验证,输入密码,检查配置,然后点击确定
这时候重新登录,用SQL Server身份验证方式,输入用户名密码,如果成功登录就完成了
报错情况 (没报错可以跳过这部分)
有部分人到里这发现还是不能用SQL Server身份验证方式连接,比如说题主这时候。。。
这时就要检查是不是下面的地方没设置好:用户名或密码错误,
SQL Server 身份验证模式:确保 SQL Server 处于混合模式
用户权限:确认 'sa' 用户在 SQL Server 上具有足够的权限。
链表是一种常见的数据结构,由一系列节点(Node)组成,每个节点包含数据和指向下一个节点的指针。链表的头结点(Head Node)也称为哨兵位,是链表的起点,通常有以下几个重要作用:
1. 标识链表的起点 头结点是链表的入口点,指向链表的第一个有效节点或直接作为链表的第一个节点。通过头结点,我们可以访问链表中的所有节点。需要注意的是:头结点并不存储有效数据,所以它不是有效结点。
示例:
Head -> Node1 -> Node2 -> Node3 -> NULL 在这个例子中,Head 是头结点,通过它可以访问 Node1,再通过 Node1 访问 Node2,依此类推。
2. 提供统一的操作接口 头结点可以作为链表操作的统一接口,方便进行插入、删除、查找等操作。例如我们可以直接在头结点后插入我们需要插入在开头的结点,并不影响整个链表的正常使用。
示例:插入操作
Head -> Node1 -> Node2 -> Node3 -> NULL 插入一个新节点Node0到链表的开头: Head -> Node0 -> Node1 -> Node2 -> Node3 -> NULL 在这个例子中,通过操作 Head,我们可以轻松地在链表的开头插入 Node0。
3. 方便处理特殊情况 当链表为空时,有了头结点,可以避免对空指针的特殊处理,简化代码逻辑。也就是说当链表中没有有效节点也就是为空时,仍然会有一个头结点存在,也就不会出现野指针的情况。
示例:
没有头结点时的空链表: NULL 有头结点时的空链表: Head -> NULL 在有头结点的情况下,链表总是存在一个起点,即使没有任何有效节点,这使得链表操作更为简单和一致。
4. 帮助简化算法实现 在某些算法实现中,头结点的存在可以简化边界条件的处理,避免复杂的判空逻辑。就是说可以保证第一个结点的删除是和删除其他结点一样的操作,而不会有特殊的处理,从而简化整个代码。
示例:删除操作
Head -> Node1 -> Node2 -> Node3 -> NULL 删除Node1: Head -> Node2 -> Node3 -> NULL 在这个例子中,通过操作 Head,我们可以直接删除 Node1,而不需要考虑 Node1 是否存在或是链表的第一个节点。
目录 0.QT介绍1.下载QT2.创建并编写第一个Qt程序3.配置Visual Studio编写Qt程序的环境补充补充一 0.QT介绍 QT 是一个跨平台的应用程序开发框架,它提供了丰富的工具和类库,用于开发图形用户界面(GUI)程序。Qt 提供了 C++ 编程语言接口,同时也支持其他编程语言,如 Python和QML。
以下是 QT 的一些主要特点:
跨平台:Qt 支持在多个操作系统上进行开发,包括 Windows、macOS、Linux、Android 和 iOS 等。通过编写一次代码,可以在不同平台上进行部署和运行。高度集成的开发环境:Qt 提供了 Qt Creator,一个集成的开发环境,用于代码编辑、调试和界面设计。它提供了丰富的工具和可视化设计器,使开发过程更加高效。注意:Qt是一个应用程序开发框架,Qt Creator则是其提供的一个IDE,注意区分强大的图形用户界面库:Qt 的 GUI 框架提供了丰富的 UI 控件和布局管理器,使开发者可以轻松创建各种功能齐全的界面。同时,Qt 还支持自定义样式和主题,使界面能够与平台和用户需求相适应。响应式编程:Qt 引入了信号与槽机制,用于在对象之间进行通信和事件处理。这种机制使得编写响应式的代码更加简单和直观。数据库支持:Qt 提供了对多种数据库的支持,包括 MySQL、SQLite、PostgreSQL 等,使开发者能够方便地进行数据持久化和处理。多媒体功能:Qt 提供了大量的多媒体功能接口,包括音频、视频、图像处理等,使开发者可以轻松地实现音视频播放、图像处理等功能。 1.下载QT 进入官网
提醒:因为QT5.14.2之后的版本不再提供离线安装包,所以需要去官网下载在线安装器,我这儿也提供了下载好的网盘分享链接
官网地址为:QT官网
在线下载器的网盘分享链接:在线下载器的网盘分享链接点击Download.Try.
点击选择开源版
下拉,点击Download the Qt Online Installer
选择对应的版本并下载,我这儿下载Windows版本
下载完成后,进入在线下载器的目录,打开终端,执行以下命令打开在线下载器 .\qt-unified-windows-x64-4.6.1-online.exe --mirror https://mirrors.aliyun.com/qt/ 注意:第一段在线下载器的版本对应不要出错,后面是指使用镜像网站下载,这样下载速度更快,此处再提供两个镜像网站,下载时发现网速不行可以尝试更换镜像网站后再启动下载器重新下载
最推荐使用中科大镜像网站,个人感觉比较稳定且网速较快
清华大学:https://mirrors.tuna.tsinghua.edu.cn/qt/ 中国科学技术大学:https://mirrors.ustc.edu.cn/qtproject/ 输入Qt账号并登录,如果没有的话可以注册一个,并不麻烦
如图
如图
如图
选择合适的文件夹安装
整个路径一定不能包含中文字符、空格等
如图勾选,点击筛选,这样可以看到很多可以下载的版本
选择需要的版本进行下载,我的勾选如下
注意:一般只需从Qt栏中选择一个对应版本,其他默认即可
我这里选择了5.14.2下的所有库,但这样软件体积很大且对入门选手没什么用,大家可以参考《补充一》选择部分库进行安装
如图
如图
如图
等待安装完成
如图
目录 0.原理讲解1.回文子串1.题目链接2.算法原理详解3.代码实现 2.最长回文子串1.题目链接3.代码实现 3.分割回文串 IV1.题目链接2.算法原理详解3.代码实现 0.原理讲解 动态规划能够将所有的子串是否是回文的信息,保存在dp表里面状态表示一般经验:以[i, j]为区间,分析问题 1.回文子串 1.题目链接 回文子串 2.算法原理详解 思路: 确定状态表示 -> dp[i][j]的含义
s字符串[i, j]的子串,是否是回文串 推导状态转移方程
初始化:无需初始化
确定填表顺序:从下往上
确定返回值:dp表里true的个数
3.代码实现 int countSubstrings(string s) { int n = s.size(); vector<vector<bool>> dp(n, vector<bool>(n)); int ret = 0; for(int i = n - 1; i >= 0; i--) { for(int j = i; j < n; j++) { if(s[i] == s[j]) { dp[i][j] = i + 1 < j ?
RPG Maker MV 仿新仙剑 战斗场景UI (十) 前言角色站位人物站位人物影子 前言 上一期完成了几个功能,虽然没有进行进一步的优化,但基础的功能已经完成,现在记录下已完成及未完成的功能:
战斗菜单 一级战斗菜单 二级战斗菜单 角色状态显示 物品及法术窗口 进入状态及装备场景 角色战斗精灵 战斗背景图 投掷窗口 合击、防御、围攻 角色站位 战斗动画(法术及各操作动画) 经验及物品的消息显示 敌我选中 伤害显示 战斗移动 可以看到看到现在差的功能还有不少。
角色站位 人物站位 角色的站位,是开始战斗时角色进行战斗准备的位置,初期其实已经完成一部分了,通过之前的截图就可以看出来。
这里用了一条线段来作为标志,线段和底部呈现22.5度的夹角,而人物就在这个线段上,由于角色的不同,包括不同形态,对应的精灵的宽高也不同,因此不能通用,需要对应给不同的角色以不同的坐标来真正的保持一致。
//设置角色的初始位置 Sprite_Actor.prototype.setActorHome = function(index) { if($gameParty.members().length>1){ this.setHome(434 + index * 103, 399 - index * 44); if(this._actor._battlerName==="LingEr1"){ this.setHome(434 + index * 106, 399 - index * 65); } }else{ this.setHome(434 + 1 * 103, 399 - 1 * 44); } }; 这里针对只有一个人物时,坐标是初始坐标加上索引乘上位移的坐标,可以看到只有一个人物时索引是一个固定值,这是因为第一个主角站位正好是中间,因此需要提前写死,后期可能会考虑用多维数组或json的方式直接存储调用。
一、引言 上一个博客讲解并演示给字段加外键约束,以及通过外键来保证数据的一致性和完整性。我们一旦为子表 emp 字段 dept_id 添加外键关联之后,再去删除父表的数据之后,判断当前父表的这条数据是否在子表关联关系。如果存在,则不允许删除。
这里实际上涉及到外键约束当中的删除和更新行为。
二、外键约束(删除和更新行为) (1)主要的行为有以下情况。 注意:
1、NO ACTION:(no action) 和 RESTRICT (restrict:限制)
2、NO ACTION、RESTRICT 外键约束的默认行为
3、CASCADE:(cascade),这个操作称为级联。如果有,也删除/更新外键在子表中的记录,也就是跟着父表字段值一起修改或删除。
4、SET NULL:如果有,则设置子表中该外键值为 NULL 。也就是断开关联,保证数据的一致性和正确性。
(特别注意前4个)
5、SET DEFAULT:父表变更,如果有外键,则子表将外键设置成一个默认值
(2)接下来展示,如何在创建外键的时候来指定它们的删除以及更新的规则或者是行为。 语法:
注意:
1、前一部分是添加外键的语法。(之前学过)
2、我们只要在之前学的操作语法后面再加上一些东西:
ON UPDATE:(on update 在更新时),然后可以在后面加上要选择的行为:如 CASCADE
ON DELETE:(on delete 在删除时),如上
三、实操 还是用之前建立的两张表:员工表 emp 和 部门表:dept 。
(1)数据准备 CREATE TABLE dept ( id INT AUTO_INCREMENT COMMENT 'ID' PRIMARY KEY, name VARCHAR(50) NOT NULL COMMENT '部门名称' ) COMMENT '部门表'; INSERT INTO dept (id, name) VALUES (1,'研发部'),(2,'市场部'),(3,'财务部'),(4,'销售部'),(5,'总经办'); CREATE TABLE emp ( id INT AUTO_INCREMENT COMMENT 'ID' PRIMARY KEY, name VARCHAR(50) NOT NULL COMMENT '姓名', age INT COMMENT '年龄', job VARCHAR(20) COMMENT '职位', salary INT COMMENT '薪资', entrydate DATE COMMENT '入职时间', managerid INT COMMENT '直属领导ID', dept_id INT COMMENT '部门ID' ) COMMENT '员工表'; INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id) VALUES (1,'金庸',66,'总裁',20000,'2000-01-01',null,5), (2,'张无忌',20,'项目经理',12500,'2005-12-05',1,1), (3,'杨逍',33,'开发',8400,'2000-11-03',2,1), (4,'韦小笑',48,'开发',11000,'2002-02-05',2,1), (5,'常遇春',43,'项目经理',10500,'2004-09-07',3,1), (6,'小昭',19,'程序员鼓励师',6600,'2004-10-12',2,1); 部门表:dept
运行在VS2022,x86,Debug下。
30. 外观模式 为子系统定义一组统一的接口,这个高级接口会让子系统更容易被使用。应用:如在游戏开发中,游戏引擎包含多个子系统,如物理、渲染、粒子、UI、音频等。可以使用外观模式来封装这些复杂的子系统,提供一个简单的接口给游戏开发者,从而无需直接操作复杂的子系统,简化了开发流程。实现 子系统。外观,提供统一的接口。客户端。 代码如下。 游戏引擎外观(GameEngineFacade类)使用单例模式,确保在整个游戏中只有一个外观实例,从而统一管理子系统资源。 // 物理引擎子系统 class PhysicsSystem { public: void init() { cout << "Initializing physics engine..." << endl;} void update() { cout << "Updating physics..." << endl; } }; //渲染子系统 class GraphicsSystem { public: void init() { cout << "Initializing graphics..." <<endl; } void render() { cout << "Rendering graphics..." << endl;} }; //粒子子系统 class ParticleSystem { public: void init() { cout << "Initializing particle.
Redis容灾通常指的是数据备份和恢复机制,以确保在发生故障时可以尽快恢复服务。Redis提供了几种方法来保证数据的高可用性:
1. 使用RDB快照:通过配置文件设置定时快照,可以在指定的时间间隔保存数据集到磁盘。
2. 使用AOF日志:记录每个写操作,重启Redis时通过回放这些操作来恢复数据。
3. 主从复制:设置一个主Redis实例和一个或多个从Redis实例,数据会自动被复制到从实例。
4. 哨兵模式(Sentinel):监控主Redis服务器,当主服务器发生故障时,自动将一个从服务器升级为新的主服务器。
5. 集群模式(Cluster):创建一个Redis服务器网络,数据会在多个节点间分布。
Flutter 中的 TooltipTheme 小部件:全面指南 Flutter 是一个由 Google 开发的跨平台 UI 框架,它提供了丰富的组件来帮助开发者构建美观、响应式的用户界面。在 Flutter 的 Material 组件库中,Tooltip 是一个轻量级的组件,用于在用户将鼠标悬停在组件上时显示提示信息。TooltipTheme 组件则用于定义应用中所有 Tooltip 的统一样式。本文将为您提供一个全面的指南,介绍如何在 Flutter 应用中使用 TooltipTheme 小部件。
什么是 TooltipTheme? TooltipTheme 是一个 Flutter 小部件,它允许开发者统一设置应用中所有 Tooltip 组件的样式。通过 TooltipTheme,您可以自定义提示框的颜色、形状、阴影、文字样式等属性。
为什么使用 TooltipTheme? 统一样式:TooltipTheme 允许您统一设置应用中所有提示框的样式,保持 UI 的一致性。简化布局:它简化了布局的编写,特别是当您需要在多个地方使用统一的提示框样式时。自定义主题:TooltipTheme 可以响应主题变化,实现动态的样式更新。 如何使用 TooltipTheme? 使用 TooltipTheme 通常涉及以下几个步骤:
导入 Flutter 包:
import 'package:flutter/material.dart'; 创建 TooltipTheme:
在您的布局中添加 TooltipTheme 组件。
设置提示框样式:
通过 data 属性为 TooltipTheme 设置提示框的主题数据。
包裹布局组件:
使用 TooltipTheme 包裹需要应用样式的布局组件。
构建 UI:
构建包含 TooltipTheme 的 UI。
C语言栈:数据结构——栈(C语言版)-CSDN博客
C语言队列:数据结构——队列(C语言版)-CSDN博客
前言:
在之前学习C语言的时候,我们已经学习过栈与队列,并学习过如何使用C语言来实现栈与队列,今天,我们用C++来学习这些知识,让我们探索一下其中的新的知识点
目录
一、stack(栈)
1. 栈的概述
编辑
2. 栈的构造函数和成员函数
3. 栈的使用示例
4. 注意事项
二、queue(队列)
1. 队列的概述
编辑
2. 队列的构造函数和成员函数
3. 队列的使用示例
4. 注意事项
三、思考题
四、总结
一、stack(栈) C++中的stack是一种遵循后进先出原则的容器适配器。它提供了一系列标准的操作,使得用户可以方便地实现栈这种数据结构。
1. 栈的概述 在C++标准库中,stack并不直接暴露给用户,而是作为<stack>头文件中stack模板类的声明。这个类是std::deque的封装,因此默认情况下,栈是通过双端队列实现的。但是,用户也可以指定其他的容器作为栈的底层结构,比如std::vector或std::list。
2. 栈的构造函数和成员函数 栈提供了以下构造函数和成员函数,以便用户可以轻松地创建和使用栈:
空栈构造函数:创建一个空的栈。基于容器的构造函数:使用一个已存在的容器来初始化栈。拷贝构造函数:创建一个新栈,其内容是另一个栈的副本。 成员函数包括:
empty():检查栈是否为空。size():获取栈中的元素数量。top():返回栈顶元素的引用。push(const T&):在栈顶插入一个元素。pop():移除并返回栈顶元素。emplace(const T&):在栈顶位置构造并插入一个元素。swap(stack&):与另一个栈交换元素。 3. 栈的使用示例 以下是一个简单的使用C++栈的示例代码:
#include <iostream> #include <stack> int main() { std::stack<int> numbers; // 压入一些数字 numbers.push(1); numbers.push(2); numbers.push(3); // 打印栈顶元素 std::cout << "栈顶元素: " << numbers.top() << std::endl; // 弹出栈顶元素 numbers.
🔥 个人主页:空白诗 文章目录 引言一、深入理解并利用零值提升代码质量1.1 深入Go类型零值原理1.2 零值可用性的实践与优势1.2.1 切片(Slice)的零值与动态扩展1.2.2 Map的零值与安全访问1.2.3 函数参数与零值 二、使用复合字面值作为初值构造器2.1 结构体复合字面值2.2 数组/切片复合字面值2.3 map复合字面值 三、总结 引言 在Go语言的编程实践中,零值和复合字面值是两个非常重要的概念。零值作为Go语言类型系统的一部分,它为我们提供了一种默认初始化机制,使变量在声明后自动获得其类型的默认值。而复合字面值则提供了一种简洁、直观的方式来初始化复杂的数据结构,如结构体、数组、切片和映射。通过深入理解并有效利用这两个概念,我们可以提升代码质量,增强代码的健壮性和可读性。
一、深入理解并利用零值提升代码质量 在Go语言编程实践中,类型零值(Zero Value) 是一个核心概念,它对于代码质量、开发效率和程序的健壮性具有重要影响。零值是指当一个变量被声明后,如果没有显式地为其赋值,Go语言会自动赋予该变量对应类型的默认值。这种机制不仅简化了变量的初始化过程,还使得开发者在编写代码时能够更加专注于业务逻辑的实现,而无需过多关注变量的初始化细节。
1.1 深入Go类型零值原理 Go语言中的每一个类型都有一个默认的零值(zero value),它在变量声明但未被赋予明确值时自动赋予。零值的设定考虑到了类型特性和实际使用场景:
基础类型:如整型和浮点型的零值为0,布尔型为false,字符串为"",确保了数值和文本的默认安全起点。集合类型:数组、切片的元素自动初始化为对应类型的零值,为数据结构提供一致性和安全性。复合类型:结构体的每个字段自动初始化为它们各自类型的零值,便于统一处理和初始化。引用类型:指针、channel、map、slice、interface、函数等为nil,便于资源管理,预防空指针错误。自定义类型:根据其基础类型决定零值,允许开发者定义逻辑上合理的默认状态。 1.2 零值可用性的实践与优势 在Go语言中,零值可用的设计理念鼓励开发者编写出简洁且强大的代码,意味着许多类型在未显式初始化时即可直接安全地使用。这一原则体现在多种场景中,不仅减少了初始化负担,还提升了代码的清晰度和执行效率。让我们通过一些具体示例来深入理解这一点:
1.2.1 切片(Slice)的零值与动态扩展 Go语言中的切片类型是零值可用性的典型例子。未初始化的切片自动获得零值nil,但即使是nil切片也可以安全地调用某些方法,如append,这允许动态地创建和扩展切片,而无需预先分配空间。
var zeroSlice []int // 直接向nil切片追加元素,Go会自动转换为非nil切片 zeroSlice = append(zeroSlice, 1, 2, 3) // 继续追加 zeroSlice = append(zeroSlice, 4, 5, 6) fmt.Println(zeroSlice) // 输出: [1 2 3 4 5 6] 此例展示了即使切片最初为零值nil,通过append方法即可直接使用,无须显式分配内存,体现了Go语言对零值可用性的良好支持。
值得注意的是,并非所有类型都能像切片那样在零值状态下自由操作。尤其是涉及到直接访问或修改数据结构内部元素时,零值的限制尤为明显。例如,尝试直接通过下标访问或修改一个未初始化(nil)的切片,将导致运行时错误,如下代码所示:
var zeroSlice []int // 尝试访问或修改nil切片的元素会导致运行时错误 zeroSlice[0] = 1 fmt.
查询性能优化 慢查询基础:优化数据访问 查询性能低下最基本的原因是访问的数据太多。某些查询可能不可避免地需要筛选大量数据,但这并不场景。大部分性能低下的查询都可以通过减少访问的数据量的方式进行优化。对于低效的查询,我们发现通过下面两个步骤来分析总是很有效:
1.确认应用程序是否在检索大量超过需要的数据。这通常意味着访问了太多的行,但有时候也可能是访问了太多的列2.确认MySQL服务器是否在分析大量超过需要的数据行 是否向数据库请求了不需要的数据。 有些查询会请求超过实际需要的数据,然后这些多余的数据会被应用程序丢弃。这回给MySQL服务器带来额外的负担,并增加网络开销(如果应用服务器和数据库不在同一台主机上,网络开销就显得很明显了。即使在同一台服务器上仍然会有数据传输的开销)。另外也会消耗应用服务器的CPU和内存资源。下面是一些典型案例:
1.查询不需要的记录:一个常见的错误是常常误以为MySQL会只返回需要的数据,实际上MySQL却是先返回全部结果集再进行计算。我们经常会看到一些了解其他数据库系统的人会设计出这类应用程序。这些开发者习惯适用这样的使用,先使用SELECT语句查询大量的结果,然后获取前面的N行后关闭结果集(例如在新闻网站中取出100条记录,但是只是在页面上显示前面10条)。它们认为MySQL会执行查询,并只返回它们需要的10条数据,然后停止查询。实际情况是MySQL会查询出全部的结果集,客户端的应用程序会接收全部的结果集数据,然后抛弃其中大部分数据。最简单有效的解决方法就是在这样的查询后面加上LIMIT2.多表关联时返回全部列:如果你想查询所有在电影Academy Dinosaur中出现的演员,千万不要按下面的写法编写查询: mysql> SELECT * FROM actor -> INNER JOIN film_actor USING(actor_id) -> INNER JOIN film USING(film_id) -> WHERE film.title='Academy Dinosaur'; 这将返回这三个表的全部数据列。正确的方式应该时像下面这样只取需要的列:
mysql>SELECT actor.* FROM actor ..... 3.总是取出全部列:每次看到SELECT * 的时候都需要用怀疑的眼光审视,是不是真的需要返回全部的列?很可能不是必需的。取出全部列,会让优化器无法完成索引覆盖扫描这类优化,还会为服务器带来额外的IO、内存和CPU的消耗。因此,一些DBA是严格禁止SELECT * 的写法的,这样做有时候还能避免某些列被修改带来的问题。当然,查询返回超过需要的数据也不总是坏事。在许多案例中,人们会说这种有点浪费数据库资源的方式可以简化开发,因为能提高相同代码片段的复用性,如果清除这样做的性能影响,那么这种做法也是值得考虑的。如果应用程序使用了某种缓存机制,或者有其他考虑,获取超过需要的数据也可能有其他好处,但不要忘记这样做的代价是什么。获取并缓存所有的列的查询相比多个独立的只获取部分列的查询可能就更有好处。4.重复查询相同的数据:如果你不太小心,很容易出现这样的错误——不断地重复执行相同的查询,然后每次都返回完全相同的数据。例如,在用户评论的地方需要查询用户头像的URL,那么用户多次评论的时候,可能就会反复查询这个数据。比较好的方案是,当初次查询的时候将这个数据缓存起来,需要的时候从缓存中取出,这样性能显然会更好。 MySQL是否扫描额外的记录 在确定查询只返回需要的数据以后,接下来应该看看查询为了返回结果是否扫描了许多的数据。对于MySQL,最简单的衡量查询开销的三个指标如下:
1.响应时间
2.扫描的行数
3.返回的行数
没有哪个指标能够完美地衡量查询的开销,但它们大致反映了MySQL在内部执行查询时需要访问多少数据,并可以大概推算出查询运行的时间。这三个指标都会记录到MySQL的慢日志中,所以检查慢日志记录是找出扫描行数过多的查询的好办法
响应时间 要记住, 响应时间只是一个表面的值。这样说可能看起来和前面关于响应时间的说法有矛盾?其实并不矛盾,响应时间仍然是最重要的指标,这有一点复杂,后面细细道来。响应时间是两个部分之和:服务时间和排队时间。服务时间是指数据处理这个查询真正花了多长时间。排队时间是指服务器因为等待某些资源而没有真正执行查询的时间——可能是等待IO操作完成,也可能是等待行锁,等等。遗憾的是,我们无法把响应时间细分到上面这些部分,除非有什么办法能够逐个测量上面这些消耗,不过很难做到,一般最常见和重要的等待是IO和锁等待,但实际情况更加复杂。所以在不同类型的应用压力下,响应时间并没有什么一致的规律或者共识。诸如存储引擎的锁(表锁、行锁)、高并发资源竞争、硬件响应等诸多因素都会影响到响应时间。所以,响应时间既可能是一个问题的结果也可能是一个问题的原因,不同案例情况不同,除非我们能深入测量出每个环节。
当你看到一个查询的响应时间的时候,首先需要问问自己,这个响应时间是否是一个合理的值。实际上可以使用"快速上限估计"法来估算查询的响应时间,这是由Lahdenmaki和Mike Leach编写的Relational Database Index Design and the Optimizers一书中提到的技术。概括地说,了解这个查询需要哪些索引以及它的执行计划是什么,然后计算大概需要多少个顺序和随机IO,再用其乘以在具体硬件条件下一次IO的消耗。最后把这些消耗都加起来,就可以获得一个大概参考值来判断当前响应时间是不是一个合理得值
扫描的行数和返回的行数 分析查询时,查看该查询扫描的行数时非常有帮助的。这在一定程度上能够说明该查询找到需要的数据的效率高不高。对于找出哪些"糟糕"的查询,这个指标可能还不够完美,因为并不是所有的行的访问代价都是相同的。较短的行的访问速度更快,内存中的行也比磁盘中的行访问速度要快得多。理想情况下扫描得行数和返回的行数应该是相同的。但实际情况中这种"美事"并不多。例如在做一个管来奶查询时,服务器必须要扫描多行才能生成结果集中的一行。扫描的行数对返回的行数的比率通常很小,一般在1:1和10:1之间,不过有时候这个值也可能非常非常大
扫描的行数和访问类型 在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。MySQL有好几种访问方式可以查找并返回一行结果。有些访问方式可能需要扫描很多行才能返回一行结果,也有些访问方式可能无须扫描就能返回结果。在EXPLAIN语句中的type列反应了访问类型。访问类型有很多种,从全表扫描到索引扫描、范围扫描、唯一索引查询、常数引用等。这里列的这些,速度时从慢到快,扫描的行数也是从多到少。你不需要记住这些访问类型,但需要明白扫描表、扫描索引、范围访问和单值访问的概念.如果查询没有办法找到合适的访问类型,那么解决的最好办法通常就是增加一个合适的索引。现在应该明白为什么索引对于查询优化如此重要了。索引让MySQL以最高效、扫描行数最少的方式找到需要的记录。例如,我们看看示例库Sakila中的一个查询案例:
mysql> SELECT * FROM film_actor WHERE film_id =1; +----------+---------+---------------------+ | actor_id | film_id | last_update | +----------+---------+---------------------+ | 1 | 1 | 2006-02-15 05:05:03 | | 10 | 1 | 2006-02-15 05:05:03 | | 20 | 1 | 2006-02-15 05:05:03 | | 30 | 1 | 2006-02-15 05:05:03 | | 40 | 1 | 2006-02-15 05:05:03 | | 53 | 1 | 2006-02-15 05:05:03 | | 108 | 1 | 2006-02-15 05:05:03 | | 162 | 1 | 2006-02-15 05:05:03 | | 188 | 1 | 2006-02-15 05:05:03 | | 198 | 1 | 2006-02-15 05:05:03 | +----------+---------+---------------------+ 这个查询将返回10行数据,从EXPLAIN的结果可以看到,MySQL在索引idx_fk_film_id上使用了ref访问类型来执行查询:
文章目录 前言1.堆的相关概念1.1堆的概念1.2堆的分类1.2.1小根堆1.2.2大根堆 1.3堆的特点堆的实用场景 2.堆的实现2.1初始化2.2插入2.3堆的向上调整2.4删除2.5堆的向下调整2.6判空2.7获取堆顶元素2.8销毁 3.堆排序3.1实现3.2堆排序的时间复杂度问题 前言 在上一篇文章中,我们已经了解了树和二叉树的概念,而下面我们要学习的堆,在二叉树中非常重要;
如果的二叉树还不太了解的,大家可以参考作者的上一篇文章
详解二叉树
1.堆的相关概念 1.1堆的概念 现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,
一个是数据结构,一个是操作系统中管理内存的一块区域分段
1.2堆的分类 对于堆我们可以分成两种:
大堆和小堆;
1.2.1小根堆 1.小堆
在小堆中,堆中的某个节点的值总是不小于其父节点的值,换句话说就是父节点的值永远不大于其子节点,而如果有两个子节点时,子节点之间不存在大小限制关系;即指在逻辑上的二叉树结构中,根结点<=子结点,总是最大的,并且在堆的每一个局部都是如此。例如{1,2,3}可以看作为小根堆,而{1,3,2}亦可以看作为小根堆。小根堆的根结点在整个堆中是最小的元素。
1.2.2大根堆 2.大堆
在大堆中,堆中的某个节点的值总是不大于其父节点的值,换句话说就是父节点的值永远不小于其子节点,而如果有两个子节点时,子节点之间不存在大小限制关系;即指在逻辑上的二叉树结构中,根结点>=子结点,总是最大的,并且在堆的每一个局部都是如此。例如{3,1,2}可以看作为大根堆,而{3,2,1}亦可以看作为大根堆。大根堆的根结点在整个堆中是最大的元素。
详情请看下图:
1.3堆的特点 对于二叉树来说,我们用堆来实现,那为什么不用数组来实现呢?
因为对于普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树(也就是堆)更适合使用顺序结构存储。
并且堆还具有以下的特点,让它更加高效和适用:
1.维护有序性:
最大堆:每个节点的值都大于或等于其子节点的值,堆顶元素始终是最大值。
最小堆:每个节点的值都小于或等于其子节点的值,堆顶元素始终是最小值。
这种特性使得堆在需要频繁查找最大或最小元素的场景(如优先队列)中极为高效,无需遍历整个数组即可快速获得。
2.动态调整:
堆支持插入和删除元素的同时能够高效地(通常是O(log n)时间复杂度)重新调整结构以维持其特性,这一点在使用数组时难以直接高效实现。
3.内存利用率:
实际实现时,堆可以采用数组来存储,虽然逻辑上是树状结构,但实际上占用的是连续内存空间,因此内存使用相对高效。
4.排序应用:
堆可以作为实现堆排序的基础,这是一种不稳定的排序算法,其优势在于能够提供较好的最坏情况和平均时间复杂度(O(n log n)),并且不需要像快速排序那样依赖于数据的初始分布。
堆的实用场景 1、我们可以利用堆的性质来找出一个序列中最大/小的元素,尽管通过遍历来解决这一问题可能更好。
2、堆排序,堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1.建堆
升序:建大堆
降序:建小堆
2.利用堆删除思想来进行排序
建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。
3、建立优先级队列,根据上述的小结可知,若利用堆来建立优先级队列,可以快速的获取到队列中优先级最高/低的任务。
4、n个元素中排列出前k大的元素问题,对于此类问题,可以建立一个小根堆,依次读入n个元素并调整,并在堆的规模达到k+1时,剔除掉第1个元素,剩下k个较大元素,保持堆的规模不超过k,一直循环即可得到最终的结果。
2.堆的实现 实现堆,首先要知道堆在结构体中的结构是怎样的; //堆的结构 typedef int HeapTypeData; typedef struct heap { HeapTypeData* a; int size; int capacity; }HP; 还有我们要实现的一些接口: //初始化 void HPInit(HP* php); //插入 void HPPush(HP* php, HeapTypeData x); //删除 void HPPop(HP* php); //判空 bool HPEmpty(HP* php); //获取堆顶 HeapTypeData HPTop(HP* php); //销毁 void HPDestory(HP* php); //向上调整 void AdjustUp(HeapTypeData* a, int n, int parent); //向下调整 void AdjustDown(HeapTypeData* a, int child); //交换 void Swap(HeapTypeData* p1, HeapTypeData* p2); 下面我们就来一一实现;
文章目录 第二章:树无向树森林(树)的路分解树的度序列树的中心生成树割集生成树的计数最小生成树有序二元树 第二章:树 无向树 边加权属于P问题,点加权属于NP问题
等价定义:设 G = < V , E > G=<V,E> G=<V,E>是 n n n阶 m m m条边的简单无向图,则下面各命题是等价的:
G G G是树且连通无圈 G G G中任意两个顶点之间存在唯一的路径 G G G中无圈且 m = n − 1 m=n-1 m=n−1 G G G是连通的且 m = n − 1 m=n-1 m=n−1 G G G是连通的且 G G G中任何边均为桥 G G G中无圈,但在任何两个不相邻的顶点之间加一条新边,所得图中得到唯一的一个含新边的圈 推论:具有 k k k个分支的森林有 n − k n-k n−k条边,其中 n n n是 G G G的顶点数。
目录
动态内存有什么用呢
malloc函数
开辟失败示范
free函数
calloc函数
realloc函数
当然realooc也可以开辟空间
常⻅的动态内存的错误
对NULL指针的解引⽤操作
对动态内存开辟的空间越界访问
对⾮动态开辟内存使⽤free释放
使⽤free释放⼀块动态开辟内存的⼀部分
对同一块动态内存空间多次释放
动态内存开辟的空间忘记释放(内存泄露)
动态内存的笔试题分析
题目1
题目2
题目3
题目4
柔性数组
柔性数组的特点:
第一种代码
第二种代码
C/C++程序内存分配的⼏个区域:
动态内存有什么用呢 int main() { int a;//在栈空间开辟4个字节 int arr[10] = { 0 };//在栈空间开辟10个字节的连续空间 } 上面这种开辟空间有2个缺点
1.空间开辟的大小是固定的。
2.数组在申明的时候,必须指定数组的⻓度,数组空间一旦确定了大小就不能调整
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知
道,那数组的编译时开辟空间的⽅式就不能满⾜了。
C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。
malloc函数 开辟空间函数需要的头文件
#include<stdlib.h> 内存开辟的空间都是在堆区上的
C语⾔提供了⼀个动态内存开辟的函数:
void* malloc(size_t size); 这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。
如果开辟成功,则返回⼀个指向开辟好空间的指针。
如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃
⼰来决定。
如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。
mallo申请的空间和数组申请的空间有什么区别呢?
1.开辟的空间可以调整大小
2.开辟的位置不一样
int main() { //申请10个整行的空间 int* p = (int*)malloc(10*sizeof(int)); //判断p if (p == NULL) { //是NULL就申请失败 //打印报错信息 perror("
文章目录 网络编程介绍TCP网络编程服务器监听客户端连接服务器服务端获取连接向连接中写入数据从连接中读取数据关闭连接/监听器 简易的TCP回声服务器效果展示服务端处理逻辑客户端处理逻辑 网络编程介绍 网络编程介绍
网络编程是指通过计算机网络实现程序间通信的一种编程技术,涉及到在不同计算机之间建立连接、传输数据和协议解析等操作。套接字(Socket)编程是网络编程的一种实现方式,其提供了一种机制,使得应用程序能够通过网络进行数据传输和通信。Go中的net包是标准库中提供的网络编程包,是基于套接字编程的一种实现方式,提供了对TCP、UDP、IP、ICMP、Unix域套接字等常见网络协议的支持,通过net包可以完成创建套接字、建立连接、发送和接收数据等操作,实现网络通信。 TCP网络编程 服务器监听 服务器监听
在Go的net包中,Listen函数用于创建并返回一个网络监听器(Listener),以监听指定网络地址和端口上的连接请求。该函数的函数原型如下:
func Listen(network, address string) (Listener, error) 参数说明:
network:用于指定网络类型,其值必须是"tcp", “tcp4”, “tcp6”, “unix"或"unixpacket”。address:用于指定需要被监听的IP地址和端口号,格式为"host:port"。 返回值说明:
第一个返回值:表示创建的网络监听器。第二个返回值:如果创建网络监听器过程中出错,将返回非nil的错误值。 通过Listen函数创建得到的网络监听器是Listener类型的,该类型是一个接口类型,其定义如下:
type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (Conn, error) // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener's network address. Addr() Addr } Listener接口中各方法说明:
光子晶体概述 光子晶体定义和分类 [P4-5] 光子晶体是一种在一维、二维或三维空间内周期性排列的多层介质。这些结构通过在光子尺度上排列的重复单元,可以对光进行调控和控制。具体来说,光子晶体是指那些在空间上具有周期性排列的介质结构,它们通过这种周期性排列形成光子带隙,从而控制特定波长范围的光传播。
一维光子晶体 (1D) 一维光子晶体是指沿一个方向上具有周期性结构的介质,例如:
光栅:可以反射特定角度入射的光波。滤波器:选择性地反射某些频率的光波。 二维光子晶体 (2D) 二维光子晶体是在两个方向上具有周期性结构的介质,例如:
平行棒阵列圆柱形孔阵列:如改变光纤特性的孔状光纤(holey fibers)。 三维光子晶体 (3D) 三维光子晶体是指在三维空间内具有周期性结构的介质,例如:
立方体、球体或各种形状的孔,这些结构类似于天然晶体的晶格排列。 光子晶体的特征 尺度:
光波本质上是周期性的,当光波与具有相似尺度的周期性介质相互作用时,会产生特定的物理现象。光子晶体的周期性结构与光的波长处于相同量级,这种匹配是实现光子带隙效应的基础。 光子带隙 (Photonic Bandgaps):
光子晶体最显著的特征之一是光子带隙,即某些频率范围内的光在光子晶体中无法传播。这种现象类似于半导体中的电子带隙,对光的传播形成有效的控制和调节。光子带隙适用于所有方向,使得光子晶体可以用于制造各种光学器件。 光子晶体中电磁波的传播特性求解的基本方法框架[P6] 波动方程的本征值问题
在非均匀介电介质中,波动方程具有以下一般形式:
∇ × ( η ( r ) ∇ × E ) = ω 2 c 0 2 E \nabla \times (\eta(r) \nabla \times \mathbf{E}) = \frac{\omega^2}{c_0^2} \mathbf{E} ∇×(η(r)∇×E)=c02ω2E
∇ × [ η ( r ) ∇ × H ] = ω 2 c 0 2 H \nabla \times [\eta(r) \nabla \times \mathbf{H}] = \frac{\omega^2}{c_0^2} \mathbf{H} ∇×[η(r)∇×H]=c02ω2H
接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量。
一个简单的接口代码示例 interface IShape { void draw(); } class Cycle implements IShape { @Override public void draw() { System.out.println("○"); } } public class Data { public static void main(String[] args) { IShape shape = new Rect(); shape.draw(); } } 定义接口的注意事项:
使用 interface 定义一个接口接口中的方法一定是抽象方法, 因此可以省略 abstract接口中的方法一定是 public, 因此可以省略 publicCycle 使用 implements 继承接口. 此时表达的含义不再是 "扩展", 而是 "实现"在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.接口不能单独被实例化 定义一个完整的接口是这样的:
interface Ishape{
public static final int num = 10;