(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)笔试常见的“三驾马车”的主要内容,如果未能解决你的问题,请参考以下文章