认识Hash表+Hash函数的设计+Hash冲突的处理+Hash表的实现+Java中的equals与hashCode

news2024/9/28 13:04:05

一、Hash表

1、定义:Hash表是一种特殊的数组

2、Hash函数

(1)设计原则

(2)作用

(3)应用

(4)Hash冲突:

二、Hash函数的设计

1、解决Hash索引分布不均匀的方法-模一个素数

2、操作对象是整数

3、寻找最优的素数

4、浮点数在内存中的存储

5、浮点数在hash数组中的hash索引

6、字符串在Hash数组中的Hash索引

7、日期对象在Hash数组中的Hash索引(将日期对象转为整型进行处理)

8、复杂对象在Hash数组中的Hash索引(将复杂对象转为整型进行处理)

三、Java中的hashCode

4、实现原理

四、Java中的equals和hashCode

1、Java中的equals方法和hashCode方法

2、总结

五、Hash冲突的处理方法-链地址法

2、Hash冲突

3、链地址法

4、哈希表中的缩容与扩容

5、使用AVL树作为数组元素实现哈希表

六、哈希表的复杂度分析

七、总结


一、Hash表

1、定义:Hash表是一种特殊的数组

2、Hash函数

(1)设计原则

  • 必须满足Hash索引大的一致性,即如果 a==b,则 Hash(a) = Hash(b)

(2)作用

操作对象转换为数组中对应的索引

如:index = ch - 'a';

将字符串中的字符与Hash表中的索引进行一一对应

(3)应用

可将字符串、日期对象、浮点数、复杂对象转换为索引

(4)Hash冲突:

不同的操作对象通过Hash函数进行计算有可能得到相同的索引(Hash索引)

二、Hash函数的设计

“键”通过哈希函数得到的“索引”分布越均匀越好

1、解决Hash索引分布不均匀的方法-模一个素数

(1)选取4作为素数-分布不均匀

(2)选取7作为素数-分布均匀

2、操作对象是整数

(1)小范围正整数直接使用(正整数--->索引)

举例:统计0到120每个数字出现的次数,创建一个大小为121的int[]类型的数组,直接使用每个数字作为数组中的索引

(2)小范围负整数偏移后使用(将小范围负整数转换为小范围正整数)(偏移后数字-->索引)

举例:统计-100到100每个数字出现的次数,创建一个大小为201的int[]类型的数组,将每个数字加100后作为数组的索引

(3)大整数取模,模一个素数(大于1的自然数中,只有1和他本身两个因数的数)

举例:为身份证号123456546126688888创建索引

  • 直接将身份证号作为索引需要在内存中创建一个很大的数组
  • 直接将身份证号后四位作为索引,可能会出现索引重复
  • 身份证号中的信息没有被完全利用

3、寻找最优的素数

根据下表进行选择,可得到最优素数,比如在2^5和2^6之间的数的最优素数是53

good hash table primes

4、浮点数在内存中的存储

(一)根据国际标准IEEE754,任意一个二进制浮点数V以表示成如下形式

(1)(-1)^S*M*2^E

(2)(-1)S表示符号位,当S=0时,V是整数,当S=1时,V是负数

(3)M表示有效数字,且1<=M<2

(4)举例:

  • 十进制的5.0对应的2进制是101.0,等价于1.01*2^2,与V的格式相对应,可得到S=0,M=1.01,E=2,标准写法为(-1)^0*1.01*2^2
  • 十进制的-5.0对应的2进制是-101.0,等价于-1.01*2^2,与V的格式相对应,可得到S=1,M=1.01,E=2,标准写法为(-1)^0*1.01*2^2

 (二)IEEE754规定

(1)对于32位的浮点数,最高大的一位是符号位S,接着的8位是指数位E,剩下的23位是有效数字M

(2)在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的部分,比如在保存1.01的时候只保存01,等到读取的时候,第一位的1加上去 ,这样做的目的是节省1位有效数字,以32位浮点数为例,留给M只有32位,将第一位的1舍去以后,等于可以保存24位有效数字

5、浮点数在hash数组中的hash索引

将浮点数在内存中的二进制存储表示为十进制整数,再将得到的十进制整数对素数取余得到数组的hash索引

举例:求3.2的hash索引

(1)32位的浮点数

(2)64位的浮点数

6、字符串在Hash数组中的Hash索引

以字符串“persist”为例

code    = p*26^6 + e*26^5 + r*26^4 + s*26^3 + i*26^2 + s*26^1 + t

            = p*B^6 + e*B^5 + r*B^4 + s*B^3 + i*B^2 + s*B^1 + t

B代表随机的整数,一般字符串都是小写字母时取26,字符串都是大写字母时取52

code是一个大整数,要对code进行取模

(1)hash(code) = (p*B^6 + e*B^5 + r*B^4 + s*B^3 + i*B^2 + s*B^1 + t)%M(M是素数,B^K,K越大,计算机越慢)

(2)hash(code) = ((p*B^5 + e*B^4 + r*B^3 + s*B^2 + i*B^1 + s) * B + t)%M

                   = (((p*B^4 + e*B^3 + r*B^2 + s*B^1 + i) * B + s) * B + t)%M

                   = ((((p*B^3 + e*B^2 + r*B^1 + s) * B + i)) * B + s) * B + t)%M

                   = (((((p*B^2 + e*B^1 + r) * B + s) * B + i)) * B + s) * B + t)%M

                   = ((((((p*B + e) * B + r) * B + s) * B + i)) * B + s) * B + t)%M(加快了计算机的速度,但如果字符串较长同时B较大时,计算机计算出来的整数计算机无法表示,可能会出现溢出)

(3)hash(code) = ((((((p%M*B + e) %M* B + r)%M * B + s)%M * B + i)) %M* B + s) %M* B + t)%M (对于整数a,b,M,存在公式 (a + b) % M = ( a%M + b%M)% M

(4)代码实现:求字符串“persist”的hash索引

/**
     * 获取字符串对象的Hash索引
     * @param s
     * @return
     */
    public int stringHashIndex(String s){
        int b = 26;// 取位权
        int m = 97;// 取素数
        int hashIndex = 0;// 字符串s的hash索引

        for(int i = 0;i < s.length(); i++){
            hashIndex = (hashIndex * b + s.charAt(i))%m;
        }

        return hashIndex;
    }

    public static void main(String[] args) {
        System.out.println(new HashIndex().stringHashIndex("persist"));
    }

7、日期对象在Hash数组中的Hash索引(将日期对象转为整型进行处理)

/**
     * 获取程序运行时系统时间的Hash索引
     * @return
     */
    public int DateHashIndex(){
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR) ;
        int month = calendar.get(Calendar.MONTH) + 1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int hashIndex = (year + month + day) % 97;

        return hashIndex;
    }

    public static void main(String[] args) {
        System.out.println(new HashIndex().DateHashIndex());
    }

8、复杂对象在Hash数组中的Hash索引(将复杂对象转为整型进行处理)

以学生类为例


public class Student {
    private String name;
    private char gender;
    private int age;
    private float weight;

    public Student(String name, char gender, int age, float weight) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.weight = weight;
    }

    /**
     * 获取学生对象的Hash索引
     * @return
     */
    public int hashIndex() {
        int hashIndex = 0;// Hash索引
        int b = 26;// 位权
        int m = 97;// 素数
        // 姓名
        for(int i = 0;i < this.name.length(); i++){
            hashIndex = (hashIndex * b + this.name.charAt(i))%m;
        }
        // 性别
        hashIndex = (hashIndex % m + this.gender % m)%m;
        // 年龄
        hashIndex = (hashIndex % m + this.age % m)%m;
        // 体重
        hashIndex = (hashIndex % m + Float.hashCode(this.weight) % m)%m;

        return hashIndex;
    }

    public static void main(String[] args) {
        Student student = new Student("小李", '男',27, 23.1f);
        System.out.println(student.hashIndex());
    }
}

三、Java中的hashCode

1、返回值为int类型,hashCode有正有负

2、hashCode是Java中对象的Hash索引,如果还是比较大,可以再模一个素数变为较小的Hash索引

3、举例:

public static void main(String[] args) {
        // int类型的hashCode
        int a = 10;
        System.out.println(Integer.hashCode(a));
        a = -10;
        System.out.println(Integer.hashCode(a));
        // double类型的hashCode
        double gdp = 6598462581.23;
        System.out.println(Double.hashCode(gdp));
        // String类型的hashCode
        String s = "comma";
        System.out.println(s.hashCode());
    }

4、实现原理

通过将对象的地址转换为一个整数,然后对该整数通过Hash函数算法就得到了该对象在Hash表中对应的Hash索引

四、Java中的equals和hashCode

1、Java中的equals方法和hashCode方法

(1)调用Object类中的equals方法是通过两个对象的地址进行比较来判断这两个对象是否是相等的

Student student1 = new Student("小李", '男',27, 23.1f);
Student student2 = new Student("小李", '男',27, 23.1f);
System.out.println(student1.equals(student2));

(2)重写Object类中的equals方法,使得equals方法是通过两个对象中的内容来判断这两个对象是否是相等的

注意:浮点数在内存中存储时会损失精度且不可直接用==比较是否相等

@Override
    public boolean equals(Object obj) {
        if(obj == null){// obj是空对象
            return false;
        }
        if(this == obj){// 两个对象地址相同
            return true;
        }
        if(obj instanceof Student){// 比较对象内容
            if(this.name.equals(((Student) obj).name) && this.gender == ((Student) obj).gender
                    && Math.abs(this.weight - ((Student) obj).weight)<=1.0E-10){
                return true;
            }else{
                return false;
            }
        }else{
            return false;// 不是同一类型的对象
        }
    }
Student student1 = new Student("小李", '男',27, 23.1f);
Student student2 = new Student("小李", '男',27, 23.1f);
System.out.println(student1.equals(student2));

(3)调用Object类中的hashCode方法,会使得相等对象的hashCode是不同的,违背了Hash函数的设计原则,即相等对象的Hash索引必须是相等的

Student student1 = new Student("小李", '男',27, 23.1f);
        Student student2 = new Student("小李", '男',27, 23.1f);
        System.out.println(student1.equals(student2));
        System.out.println(student1.hashCode() == student2.hashCode());

(4)重写Object类中的hashCode方法,使得两个相等对象的hashCode是相等的

@Override
    public int hashCode() {
        int hashIndex = 0;// Hash索引
        int b = 26;// 位权
        int m = 97;// 素数
        // 姓名
        for(int i = 0;i < this.name.length(); i++){
            hashIndex = (hashIndex * b + this.name.charAt(i))%m;
        }
        // 性别
        hashIndex = (hashIndex % m + this.gender % m)%m;
        // 年龄
        hashIndex = (hashIndex % m + this.age % m)%m;
        // 体重
        hashIndex = (hashIndex % m + Float.hashCode(this.weight) % m)%m;

        return hashIndex;
    }

2、总结

(1)如果a.equals(b)返回true,那么a和b的hashCode()必须要相等

(2)如果a.equals(b)返回false,那么a和b的hashCode()有可能相等也有可能不相等

(3)两个对象相等的必要条件是两个对象的hashCode()相等

(4)使用hashCode和equals来共同判断两个对象是否相等

五、Hash冲突的处理方法-链地址法

1、hashCode(k1)可能为负数,需要通过多按位与运算对hashCode(k1)进行处理,即hashCode(k1)&0x7fffffff,可将hashCode(k1)变为正数

举例:-10

public static void main(String[] args) {
        int a = -10;
        System.out.println(Integer.hashCode(a));// 求-10的hashCode 为-10
        // 将-10的hashCode的-10变为10
        System.out.println(Integer.hashCode(a) & 0x7fffffff);
        /*
        -10 : 原码:10000000 00000000 00000000 00001010
              反码:11111111 11111111 11111111 11110101
              补码:11111111 11111111 11111111 11110110
              &    01111111 11111111 11111111 11111111
                   11111111 11111111 11111111 11110110
         */
    }

2、Hash冲突

3、链地址法

在数组的每一个位置都存放一个链表,本质上就是一个查找表,查找表的实现也可以使用平衡树的结构

4、哈希表中的缩容与扩容

对于Java中哈希表的实现,在Java8之前,哈希表中的每个位置对应一个链表,从Java8开始,当哈希冲突达到8时,每个位置自动从链表转为红黑树红黑树的链化阈值是6,即哈希表在进行扩容时,单个Node节点下的红黑树节点个数小于6时,会将红黑树转为链表(用实际元素数目除以Hash表的容量的结果与6进行比较)

HashMap原理详解(含面试题):https://zhuanlan.zhihu.com/p/127147909

5、使用AVL树作为数组元素实现哈希表

/**
 * 哈希表
 */
public class MyHashTable <K extends Comparable<K>,V>{
    private AVLTree<K,V>[] hash;// hash数组
    private int size;// 实际元素数量
    private int M;// 哈希表的容量
    private final int[] capacity = {53,97,193,389,769,1543,3079,6151,12289,24593,49157,98317,196613,393241,786433,1572869};
    private int capacityIndex;
    private static final int upperTol = 8;// 缩容的阈值
    private static final int lowerTol = 6;// 扩容的阈值

    public MyHashTable(int capacityIndex){
        this.capacityIndex = capacityIndex;
        this.M = this.capacity[this.capacityIndex];// 模的素数
        this.size = 0;
        this.hash = new AVLTree[this.M];
        // 初始化hash数组
        for(int i = 0;i < this.M;i ++){
            hash[i]= new AVLTree<>();
        }
    }

    public MyHashTable(){
        this(0);
    }
    /**
     * 获取实际存放元素的数目
     * @return
     */
    public int getSize(){
        return this.size;
    }

    /**
     * 删除元素
     * @param pair
     */
    public void remove(Pair<K,V> pair){
        // 1、获取pair对应的hash索引
        int hashIndex = (pair.key.hashCode() & 0x7fffffff) % this.M;
        // 2、从AVL树中删除pair
        if(hash[hashIndex].find(hash[hashIndex].root,pair.key) == null){
            System.out.println("找不到");
        }
        hash[hashIndex].root = hash[hashIndex].remove(hash[hashIndex].root, pair.key);

        this.size--;
        // 在删除元素后判断hash数组是否需要缩容
        if(this.size/this.M <= this.lowerTol && this.capacityIndex >= 1){
            System.out.println("执行缩容");
            reSize(--this.capacityIndex);
        }
    }

    /**
     * 对hash数组扩容
     * @param newCapacityIndex
     */
    public void reSize(int newCapacityIndex){
        System.out.println("新容积: "+ this.capacity[newCapacityIndex]);
       // 保存旧的M
       int oldM = this.M;
       // 更新M
       this.M = this.capacity[newCapacityIndex];
       // 创建新的hash数组
       AVLTree<K,V>[] newHash = new AVLTree[this.M];
       // 初始化newHash
        for(int i = 0;i < this.M;i ++){
            newHash[i]= new AVLTree<>();
        }
       // 将原来hash数组中的数据全部存入新的hash数组中
       for(int i =0;i < oldM;i++){
           // 拿到AVL树
           AVLTree<K,V> tmp = this.hash[i];
           while (tmp.getSize()!=0){
               // 获取节点值
               Pair<K,V> pair = tmp.removeMinNode(this.hash[i].root);
               // 计算节点的新hash索引
               int newHashIndex = (pair.key.hashCode() & 0x7fffffff) % this.M;
               // 将节点加到新的hash数组中
               newHash[newHashIndex].root = newHash[newHashIndex].insert(newHash[newHashIndex].root,pair);
           }
       }
        this.hash = newHash;
    }

    /**
     * 添加元素
     * @param pair
     */
    public void add(Pair<K,V> pair){
        // 在添加元素之前判断是否要对hash数组进行扩容
        if(this.size/this.M >= this.upperTol){
            System.out.println("执行扩容");
            reSize(++this.capacityIndex);
        }
        // 1、获取pair对应的hash索引
        int hashIndex = (pair.key.hashCode() & 0x7fffffff) % this.M;
        // 2、将pair添加到hash表中对应的位置
        hash[hashIndex].root = hash[hashIndex].insert(hash[hashIndex].root,pair);
        // 3、更新size
        this.size++;
    }

    public static void main(String[] args) {
        MyHashTable<Integer,Integer> hashTable = new MyHashTable<>();
        int[] nums = new int[10000];
        for(int i = 0;i < 10000;i++){
            nums[i] = i;
        }
        for(int i = 0;i < nums.length;i++){
            hashTable.add(new Pair<>(nums[i],nums[i] + 1));
        }
        for(int i = 0;i < nums.length;i++){
            hashTable.remove(new Pair<>(nums[i],nums[i] + 1));
        }
    }

}

六、哈希表的复杂度分析

假设哈希表有M个地址,当数据量达到upperTol*M是要对哈希表进行扩容操作,哈希表的时间复杂度为O(Mlog_{2}^{n}),则哈希表的均摊时间复杂度为O(log_{2}^{n})

七、总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2173577.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

tomcat安装与部署

一、基础准备 1. 节点规划 IP 主机名 节点 192.168.200.70 tomcat Tomcat 2. 环境准备 准备一台虚拟机&#xff0c;镜像为CentOS-7-x86_64&#xff0c;下载两个软件包&#xff0c;apache-tomcat-9.0.95.tar.gz&#xff1b;zrlog WAR包。 二、安装Tomcat 1.基础环境配…

跳表的理解以及使用

文章目录 背景数组-链表优化链表随机访问的方法 介绍跳表的理解层数随机为什么随机可以保证效率实现细节 跳表与二分查找跳表与红黑数跳表与HASH 使用实现随机层数的实现跳表实现以及测试 背景 数组-链表 数组优点 随机访问速度较快&#xff08;基于下标访问&#xff09;。 实…

OpenCV视频I/O(5)视频采集类VideoCapture之从视频流中获取下一帧的函数grab()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 从视频文件或捕获设备中抓取下一帧。 grab() 函数是 OpenCV 中 VideoCapture 类的一个成员函数&#xff0c;用于从视频流中获取下一帧而不立即检…

基于SpringBoot的学生宿舍管理系统【附源码】

基于SpringBoot的高校社团管理系统&#xff08;源码L文说明文档&#xff09; 4 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xf…

相关数据库类型介绍

数据库类型可以根据不同的维度进行分类&#xff0c;但最常见的分类方式是将其分为关系型数据库&#xff08;Relational Databases&#xff09;和非关系型数据库&#xff08;Non-Relational Databases&#xff09;&#xff0c;也称为NoSQL数据库。下面我将详细介绍这两种类型的数…

[Linux] Linux操作系统 进程的优先级 环境变量

标题&#xff1a;[Linux] Linux操作系统 进程的优先级 个人主页水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、进程优先级 1.PRI and NI 2.PRI vs NI 的补充理解 二、命令行参数和环境变量 1. 命令行参数 2.环境变量 I&#xff0c;环境变量是内…

AI大模型算法工程师就业宝典—— 高薪入职攻略与转行秘籍!

从ChatGPT到新近的GPT-4&#xff0c;GPT模型的发展表明&#xff0c;AI正在向着“类⼈化”⽅向迅速发展。 GPT-4具备深度阅读和识图能⼒&#xff0c;能够出⾊地通过专业考试并完成复杂指令&#xff0c;向⼈类引以为傲的“创造⼒”发起挑战。 现有的就业结构即将发⽣重⼤变化&a…

【CSS Tricks】深入聊聊前端编写css的方法论

目录 引言BEM 规范OOCSS 规范结构与样式分离容器与内容分离 SMACSS 规范ITCSS 规范设置层工具层通用层元素层对象层组件层微调层由此分层后的项目代码结构也会相应做修改&#xff0c;主要有两种形式&#xff1a;文件夹形式文件名形式引用方式按照层级顺序引用 ACSS 规范总结 引…

U盘打开提示要格式化:深度剖析、恢复策略与预防指南

U盘打开提示要格式化现象阐述 在日常的数字生活中&#xff0c;U盘作为便携式存储设备的代表&#xff0c;扮演着不可或缺的角色。然而&#xff0c;不少用户都曾遭遇过这样一个令人头疼的问题&#xff1a;当满怀期待地插入U盘&#xff0c;准备访问其中存储的数据时&#xff0c;系…

21天全面掌握:小白如何高效学习AI绘画SD和MJ,StableDiffusion零基础入门到精通教程!快速学习AI绘画指南!

‍‍大家好&#xff0c;我是画画的小强。 今天给大家分享一些我长期以来总结的AI绘画教程和各种AI绘画工具、模型插件&#xff0c;还包含有视频教程 AI工具&#xff0c;免费送&#x1f447;&#x1f447;‍‍ 这份完整版的AI绘画全套学习资料已经上传CSDN&#xff0c;朋友们如…

怎么通过AI大模型开发一个网站?

目录 一、提示词与AI输出 二、网站效果 以前不会代码开发&#xff0c;写网站是不可能的事情&#xff0c;现在有了AI&#xff0c;一切都有了可能。以下是我通过通义千问大模型开发的简单网站。 一、提示词与AI输出 提示词1 你是python程序员&#xff0c;我有一个大的需求&am…

使用代理IP数据采集都需要注意那些?

“在当今大数据时代&#xff0c;数据采集成为了企业决策和个人研究的重要依据。然而频繁访问目标网站往往会引发IP被封锁的风险&#xff0c;这时使用代理IP就显得尤为重要。但代理IP的使用并非毫无风险&#xff0c;以下是使用代理IP进行数据采集时需要注意的几个关键事项。” 一…

Spring Boot助力:小徐影院管理系统

第二章开发技术介绍 2.1相关技术 小徐影城管理系统是在Java MySQL开发环境的基础上开发的。Java是一种服务器端脚本语言&#xff0c;易于学习&#xff0c;实用且面向用户。全球超过35&#xff05;的Java驱动的互联网站点使用Java。MySQL是一个数据库管理系统&#xff0c;因为它…

一些硬件知识(二十四)

如何测量芯片电源的上电时序&#xff1a; FPGA和很多的CPU上电都有一个上电时序&#xff1a;也就是先那部分上电&#xff0c;后那部分上电&#xff0c;这样主板才能正常开机&#xff0c;否则会开机异常&#xff1a; 设置触发参数&#xff0c;选择单次触发&#xff1a; 小米手环…

赋值运算符重载

背景&#xff1a; 在EHR模块进行调试时&#xff0c;发现QVector3D对象进行赋值时&#xff0c;出现变量未赋值成功问题。 问题描述&#xff1a; 在进行代码调试时&#xff0c;发现赋值操作未成功&#xff0c;导致代码逻辑异常&#xff0c;经过分析&#xff0c;发现QVector3D 赋…

数据结构——二叉树的性质和存储结构

二叉树的抽象类型定义 基本操作&#xff1a; CreateBiTree(&T&#xff0c;definition) 初始条件&#xff1a;definition给出二叉树T的定义。 操作结果:按definition构造二叉树T。 PreOrderTraverse(T) 初始条件:二叉树T存在。 操作结果:先序遍历T&#xff0c;对每个结…

springboot农产品销售信息微信小程序—计算机毕业设计源码35557

摘 要 在信息飞速发展的今天&#xff0c;网络已成为人们重要的信息交流平台。每天都有大量的农产品需要通过网络发布&#xff0c;为此&#xff0c;本人开发了一个基于springboot农产品销售信息微信小程序。 对于本农产品销售信息系统的设计来说&#xff0c;它主要是采用后台采…

D21【python接口自动化学习】-python基础之内置数据类型

day21 内置数据类型文档使用 学习日期&#xff1a;20240928 学习目标&#xff1a;内置数据类型--30 内置数据类型参考&#xff1a;如何使用官方文档与帮助&#xff1f; 学习笔记&#xff1a; 使用官方文档 可通过终端查看数据类型的方法 使用帮助 总结 官方文档是体系化的…

端侧Agent系列 | 端侧AI Agent任务拆解大师如何助力AI手机?(详解版)

引言 简介 Octo-planner 规划和执行Agent框架 规划数据集 基准设计 实验设计 结果 全量微调与LoRA 多LoRA训练与合并 不同基础模型的全量微调 不同数据集大小的全量微调 总结 实战 英文 中文示例1&#xff1a; 中文示例2&#xff1a; 0. 引言 人生到处知何似…

简单理解C++在C的基础上的改变

1.C语言的一些不足 我们首先看下面用C语言实现栈 #include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int StackDateType; typedef struct Stack {StackDateType* _ps;size_t _size;size_t _capacity; }Stack; void StackInit(Stack* ps) {…