• 吉比特面试准备


    吉比特面试准备

    算法知识

    1、快速排序

    快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
    (1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
    (2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
    (3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
    (4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

    2、堆排序

    堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

    hash冲突解决办法

    1.开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
    2.再哈希法
    3.链地址法(Java hashmap就是这么做的,为了好的查询,可以用红黑树)

    c++知识

    引用

    一般例子

        int a = 1;
        int &b = a;//引用
        b = 2;
        cout << a;
    

    引用的一些错误

        int &a;//引用是已经存在对象的别名,所以右边必须有一个对象
        int &b = 1;//同上,引用的初始值必须是一个对象
        double c = 1.1;
        int &d = c;//类型应该一致
    

    指针

    指针的一般用法

        int ival = 42;
        int *p = &ival;//
        cout << *p;
    

    引用与指针的区别

    1:指针本书就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象.

    2:指针无须在定义时赋初值.和其他内置类型一样,在块作用域内定义的指针如果没有被初始化.也将拥有一个不确定的值

    3:指针和引用都能提供对其他对象的间接访问,然而在具体实现细节是二者有很大的不同,其中最重要的一点就是引用本身并非一个对象.一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问他最初绑定的对象.指针和存放的地址之间就没有这种限制.给指针赋值就是令它存放一个新的地址,从而指向一个新的对象

    4:指针需要申请一段内存空间,引用不需要.

    const 限定符

    const其值定义好就不能改变

    const 函数

    const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值,这种措施主要还是为了保护数据而设置的。const 成员函数也称为常成员函数。(放在函数的后面)

    int getage() const;
    

    修饰的是返回值,表示返回的是指针所指向值是常量。(放在函数前面)

    const int * GetPosition();
    

    static 局部静态变量

    static修饰的变量,在程序第一次定义的时候初始化,并且直到程序结束的时候才被销毁,在此期间即使对象所在函数结束执行也不会对他造成影响

    例子

    int count_size()
    {
        static int siz = 0;
        return ++siz;
    }
    int main()
    {
        for(int i=1;i<=10;i++)
        {
            cout << count_size() << '
    ';
        }
        return 0;
    }
    /*
    输出结果
    1
    2
    3
    4
    5
     */
    

    智能指针

    内存泄露:动态内存分配的时候,忘记释放内存
    智能指针:share_ptr(允许多个指针访问一个对象),unique_ptr(独占所指向的对象),weak_ptr

    shared_ptr

    例子
        shared_ptr<int>p1 = make_shared<int>(42);//初始化
        auto q(p1);//拷贝
    //  auto q = p1;//赋值
    
    如何实现自动销毁所管理的对象

    我们可以认为每一个share_ptr都有一个关联的计数器,无论我们何时拷贝一个一个share_ptr,计算器都会递增,当我们给share_ptr赋一个新值或者share_ptr被销毁,计算器就会递减.一旦一个计算器变成0,他就会自动释放所管理的对象.

    unique_ptr

    一个unique_ptr"拥有"它所指的对象",与share_ptr不同.某个时刻只能有一个unique_ptr指向一个给定对象.当unique_ptr被销毁时,它所指向的对象也被销毁

    例子
        unique_ptr<int>p1 (new int(42));//初始化
        unique_ptr<int>p2(p1);//错误,不支持拷贝
        unique_ptr<int>p3;
        p3 = p2; //错误,不支持赋值
    
    如何实现unique_ptr拷贝

    我们不能拷贝或者赋值unique_ptr,但可以通过调用release和reset将指针的所以权从一个(非const)unique_ptr转移到另一个unique
    u.release():u放弃了指针的控制权,返回指针并将u职位空
    u.reset():释放u指向的对象

        unique_ptr<int>p1 (new int(42));//初始化
        unique_ptr<int>p2(p1.release());// p1.release()返回指针,并将p1置为空.
        unique_ptr<int>p3(new int(20));
        p2.reset(p3.release());//p2释放了原来指向p1的对象
    

    new和malloc的区别

    1、属性
    new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。

    2、参数
    使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。

    3、返回类型
    new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

    4、分配失败
    new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。

    5、自定义类型
    new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
    malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

    6、重载
    C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。

    7、内存区域
    new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

    多态

    多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态(.说了静态--函数重载,动态--虚函数)。
    C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

    虚函数

    继承的时候,对于某些函数,基类希望它的派生类自定义适应自己的版本

    深拷贝与浅拷贝

    【浅拷贝】是增加了一个指针,指向原来已经存在的内存。而【深拷贝】是增加了一个指针,并新开辟了一块空间

    构造和析构

    拷贝构造函数

    如果应该构造函数的第一个参数是自身的引用,且任何额外参数都有默认值,则此函数的拷贝构造函数

    析构函数

    class Foo{
        public:
            ~Foo();
    };
    

    为什么析构函数要用虚函数

    如果析构函数不被声明成虚函数,则编译器采用的绑定方式是静态绑定,在删除基类指针时,只会调用基类析构函数,而不调用派生类析构函数,这样就会导致基类指针指向的派生类对象析构不完全。若是将析构函数声明为虚函数,则可以解决此问题。

    当一个类有子类时,该类的析构函数必须是虚函数,原因:会有资源释放不完全的情况

    操作系统

    协程和线程的区别

    先简要说下结论:

    协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈,自己的局部变量,有自己的指令指针(IP,instruction pointer),但与其它协同程序共享全局变量等很多信息。

    协程(协同程序): 同一时间只能执行某个协程。开辟多个协程开销不大。协程适合对某任务进行分时处理。

    线程: 同一时间可以同时执行多个线程。开辟多条线程开销很大。线程适合多任务同时处理。

    1.协程,即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程实际上是在一个线程中,只不过每个协程对CUP进行分时,协程可以访问和使用unity的所有方法和component

    2.线程,多线程是阻塞式的,每个IO都必须开启一个新的线程,但是对于多CPU的系统应该使用thread,尤其是有大量数据运算的时刻,但是IO密集型就不适合;而且thread中不能操作unity的很多方法和component

    线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。

    进程通信的方式

    1. 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    2. 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    3. 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    4. 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
    5. 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    6. 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
    7. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

    设计模式

    单例模式

    在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。

    单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

    在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

    单例模式有 3 个特点:
    单例类只有一个实例对象;
    该单例对象必须由单例类自行创建;
    单例类对外提供一个访问该单例的全局访问点;

  • 相关阅读:
    软件工程第三次作业
    软件工程第二次实践
    软件工程第一次作业
    剑指offer-重新排序数组,倒置链表
    剑指offer-倒数第k个节点,镜像二叉树,O(1)时间删除链表节点,
    剑指offer-判断树1中是否有树2,合并有序链表,旋转打印二维数组
    matlab基本语法
    C/C++ 二维数组实参形参的总结
    剑指offer-实现power,打印1到n位最大数
    剑指offer-旋转数组,斐波那契数列,比特1的个数
  • 原文地址:https://www.cnblogs.com/hh13579/p/13084894.html
Copyright © 2020-2023  润新知