记录大佬面经的第六周

                                                    第一天

vivo Java提前批面试

礼拜天笔试AC了1.3道,没想到进面试了,昨天预约的今天5.30面试,面试官准时来了。我的麦克风出了点问题,面试官很nice的提醒我不要着急,3分钟后调整好了麦克风,进入正式面试。

1.自我介绍,中间到项目时候直接打断

2.深挖项目,问项目中细节

3.你为什么是项目负责人?怎么分的?

4.项目中遇到的问题是什么?怎么解决的?

5.H264编码中的问题

6.视频动态更新的问题

7.你对自己的学习能力怎么评价?

9.本科学的最好的一门课?为什么?

10.对软件工程师后端的理解?

11.java学的怎么样?平常如何学习?

12.java异常?

13.还有什么要问我的吗?

全程面试官很nice,许愿一个HR面

 

                                                      第二天

 

vivo提前批 一面
 

面试小哥人很好,问的问题比较灵活,不按套路来,感觉不是太妙

1,反射能破坏private,那还要private干嘛?

解:简单来说,private并不是解决“安全”问题的,而是用户对Java使用的一种约束。private的意义是OOP(面向对象编程)的封装概念。对于setAccessible(true)破坏类的访问规则,带来安全隐患问题。

2,局部变量不赋值不能通过编译,为啥?

解:(1)、成员变量(全局变量)是可以不经初始化的,在类加载过程的准备阶段即可给它赋予默认值,但局部变量使用前需要显示赋予初始值,javac不是推断不出不可以这样做,而是没有这么做,对于成员变量而言,其赋值和取值访问的先后顺序具有不确定性,对于成员变量可以在一个方法调用前赋值,也可以在方法调用后进行,这是运行时发生的,编译器确定不了,交给jvm去做比较合适。
(2)、而对于局部变量而言,其赋值和取值访问顺序是确定的。这样设计是一种约束,尽最大程度减少使用者犯错的可能。假使局部变量可以使用默认值,可能总会无意间忘记赋值,进而导致不可预期的情况出现。

3,遍历一个arraylist用啥方式?

4,单例模式(我说了双重检验锁单例),追问为什么是线程安全的?为什么要双重检验? 你还能优化这个双重检验锁单例吗?(这个真不会了,我答也许把sync锁换成轻量级的更好)。

解:

//双重校验部分代码 

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
    }
    return instance;
  }

去掉第一个判断为空:即懒汉式(线程安全),这会导致所有线程在调用getInstance()方法的时候,直接排队等待同步锁,然后等到排到自己的时候进入同步处理时,才去校验实例是否为空,这样子做会耗费很多时间(即线程安全,但效率低下)。 

去掉第二个判断为空:即懒汉式(线程不安全),这会出现 线程A先执行了getInstance()方法,同时线程B在因为同步锁而在外面等待,等到A线程已经创建出来一个实例出来并且执行完同步处理后,B线程将获得锁并进入同步代码,如果这时B线程不去判断是否已经有一个实例了,然后直接再new一个。这时就会有两个实例对象,即破坏了设计的初衷。(即线程不安全,效率高)
 

5,b树 b+树

6, 两数之和(我用了hsshmap),追问有没有更好的方法?(不会 )

 

 

                                                   第三天

整合了几个面经

1、MyIsum和InnodB引擎的区别(从索引、事务扯到了B+树,分析了文件结构.indb .MyI .Myd)

解:

每个MyISAM在磁盘上存储成三个文件:

第一个文件的名字以表的名字开始,扩展名指出文件类型,.frm文件存储表定义。与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等。各种存储引擎都需要frm文件,并且存放于数据库名目录下。
第二个文件是数据文件,其扩展名为.MYD (MYData)。用于存储myisam表的数据
第三个文件是索引文件,其扩展名是.MYI (MYIndex)。用于存储myisam表的索引相关信息

https://www.cnblogs.com/qiumingcheng/p/5237785.html

innodB的数据库物理文件结构:

   .frm文件:存放表额元数据信息如表结构

   .ibd和.ibdata:这两个文件都是存放数据的文件,之所以两个因为innodb的数据存方式能够通过配置来决定使用共享表空间存放数据,还是独享表空间存放数据。

独享表是 .ibd文件。共享表:.ibdata文件

彩蛋:ib_logfiles文件及作用?

这个是innodb的redolog,其作用是系统崩溃后,做事务重做的

innodb事务日志:

innodb事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作。

1.redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
2.undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。

https://blog.csdn.net/u010002184/article/details/88526708

1.1、 为什么MyISAM会比Innodb 的查询速度快?

INNODB在做SELECT的时候,要维护的东西比MYISAM引擎多很多;
1)数据块,INNODB要缓存,MYISAM只缓存索引块,  这中间还有换进换出的减少; 
2)innodb寻址要映射到块,再到行,MYISAM 记录的直接是文件的OFFSET,定位比INNODB要快
3)INNODB还需要维护MVCC一致;虽然你的场景没有,但他还是需要去检查和维护

https://blog.csdn.net/xmtblog/article/details/87941698

聚簇索引是物理索引,数据表就是按顺序存储的,物理上是连续的。我的理解,所有的记录行都根据聚簇索引顺序存储,如按照主键Id递增方式依次物理顺序存储

2、线程ThreadLocal

3、建索引时注意哪些问题?(我讲了常用字段建索引、联合索引和最左匹配原则、离散性低的字段尽量不建索引)

解:表记录少的不建,经常修改的字段不建,Where条件里用不到的字段不创建索引,数据重复且分布平均的表字段不建,

在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好,全值匹配我最爱,最佳左前缀原则,不在索引列上做任何的计算,函数,类型转换否则会全表扫描,mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描,is not null 也无法使用索引,但是is null是可以使用索引,字符串不加单引号索引失效,少用or,用它来连接时会索引失效等

 

4、场景题:公司要为一个历史日志文件做一个数据库表格,用Innodb还是MyIsum好?
如果联合索引a属性有100情况,b属性有10000情况,主键索引应该建在那个上面?(引导了我一下,建在
b上好)

小表驱动大表,小表作为驱动表。因为 驱动表无论如何都会被全表扫描?。所以扫描次数越少越好。

 

5、I/O了解吗(这块我盲区,没往下答了,面试官提到了BIO,NIO,AIO等)

解:BIO:同步阻塞(一请求一应答)

采用 BIO 通信模型 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。我们一般通过在 while(true) 循环中服务端会调用 accept() 方法等待接收客户端的连接的方式监听请求,请求一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成, 不过可以通过多线程来支持多个客户端的连接,如上图所示。

如果要让 BIO 通信模型 能够同时处理多个客户端请求,就必须使用多线程(主要原因是 socket.accept()socket.read()socket.write() 涉及的三个主要函数都是同步阻塞的),也就是说它在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 一请求一应答通信模型 。我们可以设想一下如果这个连接不做任何事情的话就会造成不必要的线程开销,不过可以通过 线程池机制 改善,线程池还可以让线程的创建和回收成本相对较低。使用FixedThreadPool 可以有效的控制了线程的最大数量,保证了系统有限的资源的控制,实现了N(客户端请求数量):M(处理客户端请求的线程数量)的伪异步I/O模型(N 可以远远大于 M)

//文件上传案列
//服务端代码
public class FileUpload_Server {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器 启动.....  ");
        // 1. 创建服务端ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        // 2. 循环接收,建立连接
        while (true) {
            Socket accept = serverSocket.accept();
          	/*
          	3. socket对象交给子线程处理,进行读写操作
               Runnable接口中,只有一个run方法,使用lambda表达式简化格式
            */
            new Thread(() -> {
                try (
                    //3.1 获取输入流对象
                    BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
                    //3.2 创建输出流对象, 保存到本地 .
                    FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg");
                    BufferedOutputStream bos = new BufferedOutputStream(fis);
                ) {
                    // 3.3 读写数据
                    byte[] b = new byte[1024 * 8];
                    int len;
                    while ((len = bis.read(b)) != -1) {
                        bos.write(b, 0, len);
                    }

                    // 4.=======信息回写===========================
                    System.out.println("back ........");
                    OutputStream out = accept.getOutputStream();
                    out.write("上传成功".getBytes());
                    out.close();
                    //================================

                    //5. 关闭 资源
                    bos.close();
                    bis.close();
                    accept.close();
                    System.out.println("文件上传已保存");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

//客户端
public class FileUpload_Client {
    public static void main(String[] args) throws IOException {
        // 1.创建流对象
        // 1.1 创建输入流,读取本地文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.jpg"));
        // 1.2 创建输出流,写到服务端
        Socket socket = new Socket("localhost", 6666);
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        //2.写出数据.
        byte[] b  = new byte[1024 * 8 ];
        int len ;
        while (( len  = bis.read(b))!=-1) {
            bos.write(b, 0, len);
        }
      	// 关闭输出流,通知服务端,写出数据完毕
        socket.shutdownOutput();
        System.out.println("文件发送完毕");
        // 3. =====解析回写============
        InputStream in = socket.getInputStream();
        byte[] back = new byte[20];
        in.read(back);
        System.out.println(new String(back));
        in.close();
        // ============================

        // 4.释放资源
        socket.close();
        bis.close();
    }
}

NIO:同步非阻塞,提供了 Channel , Selector,Buffer等抽象

如果是在面试中回答这个问题,我觉得首先肯定要从 NIO 流是非阻塞 IO 而 IO 流是阻塞 IO 说起。然后,可以从 NIO 的3个核心组件/特性为 NIO 带来的一些改进来分析。如果,你把这些都回答上了我觉得你对于 NIO 就有了更为深入一点的认识,面试官问到你这个问题,你也能很轻松的回答上来了。

IO流是阻塞的,NIO流是不阻塞的。

Java NIO使我们可以进行非阻塞IO操作。比如说,单线程中从通道读取数据到buffer,同时可以继续做别的事情,当数据读取到buffer中后,线程再继续处理数据。写数据也是一样的。另外,非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

Java IO的各种流是阻塞的。这意味着,当一个线程调用 read()write() 时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了

2)Buffer(缓冲区)

IO 面向流(Stream oriented),而 NIO 面向缓冲区(Buffer oriented)。

Buffer是一个对象,它包含一些要写入或者要读出的数据。在NIO类库中加入Buffer对象,体现了新库与原I/O的一个重要区别。在面向流的I/O中·可以将数据直接写入或者将数据直接读到 Stream 对象中。虽然 Stream 中也有 Buffer 开头的扩展类,但只是流的包装类,还是从流读到缓冲区,而 NIO 却是直接读到 Buffer 中进行操作。

在NIO厍中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,写入到缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作。

最常用的缓冲区是 ByteBuffer,一个 ByteBuffer 提供了一组功能用于操作 byte 数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean类型)都对应有一种缓冲区。

3)Channel (通道)

NIO 通过Channel(通道) 进行读写。

通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。因为 Buffer,通道可以异步地读写。

4)Selectors(选择器)

NIO有选择器,而IO没有。

选择器用于使用单个线程处理多个通道。因此,它需要较少的线程来处理这些通道。线程之间的切换对于操作系统来说是昂贵的。 因此,为了提高系统效率选择器是有用的。

 基本代码,不加选择器和非阻塞

    @Test
    public void client() throws IOException {

        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        FileChannel inChannel = FileChannel.open(Paths.get("C:\\Users\\adminstors\\Desktop\\uv.plt"), StandardOpenOption.READ);

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while (inChannel.read(buffer)!=-1){
            buffer.flip();
            socketChannel.write(buffer);
            buffer.clear();
        }
        inChannel.close();
        socketChannel.close();
    }


    @Test
    public void server() throws IOException {

        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        FileChannel fileChannel = FileChannel.open(Paths.get("C:\\Users\\adminstors\\Desktop\\uv2.plt"), StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW);
        socketChannel.bind(new InetSocketAddress(9898));
        SocketChannel socktClent = socketChannel.accept();

        while (socktClent.read(buffer)!=-1){
            buffer.flip();
            fileChannel.write(buffer);
            buffer.clear();
        }
        socktClent.close();
        fileChannel.close();
        socketChannel.close();
    }

 

加选择器和非阻塞

  @Test
    public void client() throws IOException {


        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9888));

        FileChannel fileChannel = FileChannel.open(Paths.get("C:\\Users\\adminstors\\Desktop\\uv.plt"), StandardOpenOption.READ);

        //转换为非阻塞流
        socketChannel.configureBlocking(false);

        ByteBuffer buffer = ByteBuffer.allocate(1024);
/*
        while (fileChannel.read(buffer)!=-1){
            buffer.flip();
            socketChannel.write(buffer);
            buffer.clear();
        }*/
        buffer.put(LocalDate.now().toString().getBytes());
        buffer.flip();
        socketChannel.write(buffer);
        buffer.clear();
        fileChannel.close();
        socketChannel.close();
    }


    @Test
    public void server() throws IOException {

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        serverSocketChannel.configureBlocking(false);

        serverSocketChannel.bind(new InetSocketAddress(9888));

        //获取选择器
        Selector selector = Selector.open();

        //将通道注册到选择器上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //轮询选择器
        while (selector.select() > 0) {
            //获取选择器中的事件
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey next = it.next();
                //取消选择键
                it.remove();
                //判断是什么事件准备就绪
                if (next.isAcceptable()) {
                    //若接收就绪,获取客户端连接
                    SocketChannel clientChannel = serverSocketChannel.accept();
                    //切换到非阻塞模式
                    clientChannel.configureBlocking(false);
                    //将通道注册到选择器中
                    clientChannel.register(selector,SelectionKey.OP_READ);
                }else if(next.isReadable()){
                    SocketChannel sc =(SocketChannel)next.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int len=0;
                    while ((len=sc.read(buffer))!=-1){
                        buffer.flip();
                        System.out.println(new String(buffer.array(),0,len));
                        buffer.clear();
                    }
                }

            }
        }
    }

为什么大家都不愿意用 JDK 原生 NIO 进行开发呢?从上面的代码中大家都可以看出来,是真的难用!除了编程复杂、编程模型难之外,它还有以下让人诟病的问题:

  • JDK 的 NIO 底层由 epoll 实现,该实现饱受诟病的空轮询 bug 会导致 cpu 飙升 100%

  • 项目庞大之后,自行实现的 NIO 很容易出现各类 bug,维护成本较高,上面这一坨代码我都不能保证没有 bug

AIO:异步非阻塞

                                             第四天

vivo提前批

一面

vivo 21届提前批,面试时间 2020-06-10 17:50 - 18:13,共23分钟。涉及内容包括项目、开源组件、Java 内存模型、HashMap 底层原理等。全程录音,现在开始记录问题。

1.自我介绍

2.讲一下最近的那个项目

研究生系统,有啥模块

3.简历上开源工具非常多,挑一个掌握的比较好的讲一下。

这边讲了 Spring-boot。
现在很多公司都在用,主要特性就是 IoC 和 AOP。
IoC 就是控制反转,其实就是用 IoC 容器来控制各个单例,比如说 Service、Controller、Bean。(这边忘记说 IoC 容器的实质其实就是一个单例的 Map)
AOP 就是面向切面,Spring 的面向切面就是利用动态代理来实现的,实现方式有使用 CGlib 的动态代理和 JDK 提供的 Proxy 包的动态代理。

4.那我为什么要用 AOP 呢?

因为传统的面向对象在一些场景下不是很灵活,比如说有一个类 A,实现了一个 speak 方法,然后有很多继承的子类。如果我们要在 speak 方法前再添加一个喝水的动作,那么此时我们需要一个个去改子类的代码,这样是非常麻烦的。AOP 就能解决这种困难,只需要实现一个切面就能实现这个功能。

5.有没有其他方式解决你说的这个问题吗?

也有,比如说拦截器,也可以做到。

6.遇到类似的场景我们怎么决定用哪一个方案更好?

(这边有点迷糊了)这就要看具体场景分析了。比如说我们是前端调用一些方法,需要传到后端,如果我们要统一对这些请求先进行处理再实现具体业务的话,就可以使用拦截器,如果说是刚才这种场景的话,就使用 AOP 就行了

7.在项目中有遇到什么技术难点嘛?

研究生系统选课的高并发的问题

8.用异步方案能完全解决这个问题吗?

9.数据库做了主从一致,是数据库之前做过分库分表还是怎么样?

用了 galare 来保证数据库一致

10.为什么会有两个数据库呢?

没有说读写分离这样子,两个数据库主要是为了提高高可用性,一台服务器挂了另一台服务器的数据库还能顶上,单纯的 backup

11.那能讲一下主从一致的方案

用的是 galare 的主从一致

12.数据的同步是实时的?

对,galare 保证了主从数据库的一致性,实时的

13.还用 ELK 做了一个日志系统,怎么做的?

这个就是简单实用 ELK 搭建了一个系统来收集日常 API 实用时日志的收集,我们还做了定制化的轮盘在 Kibana 上,来显示日志情况,比如说某个时间点,某个 API 的请求数比较多导致服务器性能降低,我们就可以针对这个 API 进行优化

14.扯皮问题

15.我看你这边做的都是后台的,这边职位是安卓的,你有什么想法吗?

(必须有想法)说了自己做过的三个安卓项目,一个百度 SDK 的定位和***软件,一个飞机大战游戏,一个弹球游戏,都是小项目

16.做后台和做安卓都能接受是吧?

(必须能)

17.聊一下一些基础知识,看你写了 Git、maven、gradle,看过哪些资料呢?

就是日常使用嘛,(有自己写过脚本嘛?)没有,就是日常使用,比如说项目管理,maven 一下子就搞定了。

18.你们现在编译部署都是自动化了么?

是的,使用 gitlab 托管代码,git 用来版本控制,jenkins 构建,docker 构建好后使用 k8s 进行自动调度

19.整套的脚本控制在哪呢,在 Jenkins 里面?

gitlab 和 Jenkins 都有,都有触发器

20.下面讲一个 Java,Java 内存模型还熟悉吗?

还行,怎么说呢,有线程独占的和线程非独占的,独占的虚拟机栈、本地方法栈和程序计数器,非独占的堆和方法区,现在也叫元空间了。

21.像 GC 这块的话,怎么去识别一个内存对象可以被回收呢?

有两种方法,一种是引用计数法,通过指向该对象引用的数量来判断,如果为 0 就可以被回收。但是这个方法不能解决互相指向的问题,比如说一个对象 A 和一个对象 B,他们相互指向,但是没有引用指向他们,但是垃圾回收期无法回收。
还有一种就是可达性分析,JVM 会使用 GC Roots 列表,从他们触发遍历对象,如果可达就不会被收集,不可达就会被标记放到一个队列中,等待执行 finalize方法

22.那些对象可以作为 roots 节点呢?

(这边记不清了。。。气)局部变量中的对象,static 修饰的,还有 JNI 中的(其实还有,比如说常量、锁对象)

23.刚刚讲了内存对象的回收,类信息(Class 信息)可以被回收嘛?

(这边不太懂)不能,因为 Class 不在堆中,在方法区中,而 GC 用来回收堆中(其实是会的,只是垃圾回收器觉得方法区中的效率不高。只有满足三种情况才会回收类对象:(1)没有该类的对象存在;(2)没有对该类对象Class的引用;(3)加载该类的 classLoader 已被卸载)

24.链表还熟悉吗?

还可以吧,挺熟悉的(可以感觉到)???怎么就感觉到了?

25.怎么判断有环?

(这边没说麻烦的方法,也可以用快慢指针找入口的方法)用 set,遍历的时候看是否遍历到了 set 中的元素

26.像你平时的话,哪些数据结构用的比较多一点?

HashMap 吧,这个用的是最多的

27.可以讲一下嘛?

底层的话其实是用 bucket 数组加链表或者红黑树的存储方法。添加节点的时候先对 key 进行 hash 计算,除了使用 key 的 hashcode 方法之外还需要对 hashcode 的值进行位移操作,然后和 bucket 数组长度进行与计算,得到在 bucket 中的下标,找到这个 bucket 的头结点进行遍历,如果有该值的话就不加而是更新,否则的话就添加。添加操作结束后容量 size ++,如果 size 超过一个阈值,就会将原本的链表结构转化成红黑树,当然如果删除的话低于某个阈值的话,也会从树变成链表。

28.那如果不停地向一个 hashmap 中放元素,会触发它扩容嘛?

会啊,超出一个阈值就会。扩容的时候使用容量和装载因子进行新数组长度的计算,然后创建新数组。然后将原本的数组中的节点全部放到新数组中,当然,这个转移的操作并不是简单的转移,而是每次都要重新计算节点 key 的 hash 值的操作然后添加节点。

29.你这边有什么想要了解的嘛?

 

                                                                    第五天

一面

输入URL以后的过程

实现两个服务器上数据库同步的方法
MySQL数据库索引

各种锁

线程池

框架都用过什么?
Redis了解多少?
Redis的数据类型
问了更深的Redis问题,不太会了

网络安全了解多少?
CSRF攻击是什么?

二面

详细讲项目

问MySQL学习了多少,谈到了数据库性能优化手段,问了一下
分表的方式(垂直分割、水平分割)垂直分库可以避免跨页的问题。
水平分表的标准
解:垂直分表:垂直分表在日常开发和设计中比较常见,通俗的说法叫做“大表拆小表”,拆分是基于关系型数据库中的“列”(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张“扩展表”,将不经常使用或者长度较大的字段拆分出去放到“扩展表”中。

水平分表:水平分表也称为横向分表,比较容易理解,就是将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。

 

 


MySQL的Explain关键字

B树、B+树
谈到用过霍夫曼编码,问了相关知识

常用的排序算法
常用的查找算法,顺序查找

OSI七层模型

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值