(2021-04-02)笔试常见的“三驾马车”

Posted Mr. Dreamer Z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(2021-04-02)笔试常见的“三驾马车”相关的知识,希望对你有一定的参考价值。

笔试常见的“三驾马车”


前言

去找工作时,最容易遇到的三个笔试题。今天来深入了解一下,避免背题导致忘记。

1.冒泡排序

冒泡排序应该是很常见且也是最简单的笔试问题了,一个很简单的算法。
简单来说,冒泡排序:在水中,小气泡上浮,大气泡下沉。就是一个升序排序。

思路也很简单,将第一个数字挨个和后面的数字进行比较。如果前面的数比后面的数大,那么进行换位。对每个数字都进行此操作。

有这些数字。

第一轮,我们先将5和8比较。由于8大于5,所以不需要换位,接着用8和6比较,8大于6,交换位置。

接着,让8和3进行比较。继续换位置。。。。。后面照旧。

第二轮,我们继续用5和6开始比较

按照之前说的逻辑,但是最后一位不会再进行比较了。因为经过上一次的比较过程,已经将最大的值放到了末尾。因此不需要再进行比较了。

第三轮,继续从头开始比较,但是最后两位不需要比较了。

后续同理。。。。。

上代码:

  public static void main(String[] args) 
        int[] arr = 2,5,1,3,8,5,7,4,3;
        sort(arr);
        System.out.println(Arrays.toString(arr));
    

    public static int[] sort(int[] arr)
        for(int i=0;i<arr.length-1;i++)//-1是因为选择比较的参数会和它后面的参数进行比对,不需要数组的最后一位。
            for(int j=0;j<arr.length-1-i;j++)
                if(arr[j]>arr[j+1])
                    int temp = arr[j];//赋值
                    arr[j] = arr[j+1];//交换位置
                    arr[j+1] = temp;
                
            
        
        return arr;
    

2.快速排序

快速排序可以说是笔试题遇到最高频的一个题目了。它其实相当于一个“forkjoin”,分而治之的一个操作。

它的逻辑操作其实分为这几步:

1. 选择一个基准值privt(通常选择数组最左边的数据)。
2. 设定两个哨兵i和j,i通常是数组最左边的数据,j则是数组最右边的数据。
3. 从最右边的开始和基准值privit进行比较,满足这些值都是大于基准值的。然后从最左边的数值和基准值进行比较,满足这些值都小于基准值。简单来说,基准值右边的的值都必须大于等于它,基准值左边的值都必须小于等于它。
4. 重复上面3个操作,直到对应数组的左下标大于右下标(由于之前说了是分而治之的思想,所以会根据基准值分成一个个越来越短的数组)。

下面用一些比较直观的图来展示一下,

由于选择了最左边的数值为基准值,所以必须从右边开始判定。至于为什么不能从左边,后面会简单的说明一下。

按照之前说的步骤,右边的哨兵和基准值6比较,所经过的值都是必须大于基准值的。如果小于基准值,那么哨兵j会停下来。同理,左边的哨兵经过的值必须比基准值小。否则停下来。如果此时还没有相遇,那么会交换位置。

右边的哨兵j继续前进,到3这个数值。停下来。哨兵i前进,然后他们相遇了。相遇了他们就要停下来,因为没有路了。然后将3和基准值6进行替换。刚刚其实提到了,为啥选用最左边的数值为基准值时,必须由最右边的哨兵先“前进”。大家可以想一想,如果左边的哨兵先前进,那么3这个位置,他就不会停下来,而是到9这个位置停下来,那么9和6会进行位置交换。会对排序造成影响。
详见可以参考这篇文章:为什么需要从最右边开始前进

第一轮操作结束之后,会将数组拆分成两个(根据之前选定的基准值6)。然后这两个数组又会根据新的基准值分成新的数组。如下图

所以说它其实很像forkjoin这个工具类,但是没有forkjoin强大而已。

上代码:

   public static void main(String[] args) 
        int[] arr = 6,1,2,7,9,0,4,5,10,20;
        System.out.println("原数组值:"+ Arrays.toString(arr));
        quickSort(0, arr.length-1,arr);
    

    public static void quickSort(int left,int right,int[] arr)
        int i,j,temp;
        if(left>right)
            return;
        
        temp=arr[left]; //temp中存的就是基准数
        i=left;
        j=right;
        while(i!=j)
            //顺序很重要,要先从右边开始找 ,直到找到一个小于基准的值.如果你从左边开始的话,
            while(arr[j]>=temp && i<j)
                j--;
            
            //再找右边的 ,直到找到一个大于基准的值
            while(arr[i]<=temp && i<j)
                i++;
            
            //交换两个数在数组中的位置
            if(i<j) 
                int t=arr[i];
                arr[i]=arr[j];
                arr[j]=t;
                System.out.println("交换值后:");
                for (int k : arr) 
                    System.out.print(k+" ");
                
                System.out.println();
            
        
        //最后将基准为与i和j相等位置的数字交换
        arr[left]=arr[i];
        arr[i]=temp;
        System.out.println("基准值归位后:");
        for (int k : arr) 
            System.out.print(k+" ");
        
        System.out.println();
        //放空的原因是因为中间是基准值
        if(left<i-1)//右下标必须大于左下标,保证数组存在
            quickSort(left,i-1,arr);//继续处理左边的,这里是一个递归的过程
        
        if(right>j+1)//右下标必须大于左下标,保证数组存在
            quickSort(i+1,right,arr);//继续处理右边的 ,这里是一个递归的过程
        
    

3.Double Check Lock

DCL又叫双重检查锁,是由懒汉式的单例模式衍生而来,为了保证多线程下不重复创建实例。

3.1 懒汉单例模式

简单的介绍下什么叫单例模式,简单来说就是就是一次服务启动,只创建一次对应的实例。我创建好了之后,你们都只能用这次。

那什么是懒汉模式呢?就是你用到我的时候我再去创建,而不是立即创建。

上代码:

public class SingleTon 
    
    private static SingleTon instance;
    
    private SingleTon()
        
    
    
    public static SingleTon getInstance()
        if(instance==null)
            instance = new SingleTon();
        
        return instance;
    

上述代码就是懒汉的单例模式

DCL呢?就是为了保证避免重复创建实例

public class SingleTon 

    private static volatile SingleTon instance;

    private SingleTon()

    

    public static SingleTon getInstance()
        if(instance==null)
            synchronized (SingleTon.class)
                if(instance==null)
                    instance = new SingleTon();
                
            
        
        return instance;
    

至于为什么要用volatile,是因为volatile提供了内存屏障,避免指令重排序。
按照instance = new Instance()这一操作,在JVM中可以分为3部分。并不像我们想象的一样一步达成。

memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); // 2: 初始化对象
instance = memory; // 3:将生成的对象指向刚分配的内存地址

给对象分配内存空间,初始化对象,分配内存地址。
明显 2和3可能进行指令重排序,导致if判断误判。

至于为什么两次判null,是因为如果线程A已经创建了实例,但是线程B此时早已经经过了第一次判null,所以它依然会获取锁,会往下走。

以上是关于(2021-04-02)笔试常见的“三驾马车”的主要内容,如果未能解决你的问题,请参考以下文章

google三驾马车

大数据的那些事:三驾马车之坑人的MapReduce

C# 4、COM 互操作和 UPnP:三驾马车

华为云原生的“三驾马车”

需求访谈的三驾马车

需求访谈的三驾马车