今日头条后台岗位实习面试记录

在3月初通过内推投递了今日头条,2日后收到电话确认视频面试。视频面试在牛客网上,会有一个视频以及一个双方都可以看得到的写代码的地方。目前准备的太不充分了,基本就是靠原来的记忆去裸面,代码题基本都没写过。在这里记录一下面试的题目以后也好准备

第一次视频面试

时长1个小时。面试官说正常时长应该是40分钟左右,因为写不出代码卡住了一会。项目经历没问,直接按照技能点问的。

1.python和java的区别

python和java都是面向对象语言。但在python中,可以直接定义函数,使用面向过程的思想进行编程,java则需要将函数封装在类中再进行调用。

python是解释性语言,直接运行;java先要把文件翻译成字节码文件再由jvm进行运行;

python有很多第三方库,但python3对python2的兼容有问题,开发速度快,代码量小;java版本更新稳定,格式要求严格,适合更大型的架构的开发;

python是一个动态语言,运行时可以改变结构,例如给类添加属性,也是在运行时才知道变量的类型,代码中不用声明;java则必须在代码中明确,属于静态语言;

其他就是语法及一些数据结构上的区别;

2.面向对象的特征

封装,继承,多态(事物在运行中存在不同的状态,父类实例指向子类对象,运行时会运行子类的实现)

3.重写与重载

方法的重载只和参数类型及参数个数相关,与返回类型无关

4.String a = new String("abc")分析其存储

在java中存在一个字符串池用于共享。如果是执行 String a = "abc" 则现在池中找是否存在,如果有则添加引用,若没有则创建放于池中,再将引用给a;若是new的,则创建对象“abc”,再引用给a ,a是在栈中的;变量+变量及substring都会采用new的方式产生新对象;

栈内存:基本类型变量及对象的引用变量

堆内存:new出来的对象

5.float是否为基础类型,基本类型有哪些,float a = 0.2是否会报错

基本类型:byte short int long float double boolean char;会报错,0.2默认是double类型,会损失精度所以不会自动转换

6.线性表有哪些,顺序表和链表有哪些不同

线性表的元素是线性1-1关系,具体体现就是顺序表及链表;顺序表的元素在内存中是用一组地址连续的存储单元来进行存储的。

7.linux系统查看日志的命令

/var/log/message 系统启动后的信息和错误日志,是Red Hat Linux中最常用的日志之一
/var/log/secure 与安全相关的日志信息
/var/log/maillog 与邮件相关的日志信息
/var/log/cron 与定时任务相关的日志信息
/var/log/spooler 与UUCP和news设备相关的日志信息
/var/log/boot.log 守护进程启动和停止相关的日志消息
/var/log/wtmp 该日志文件永久记录每个用户登录、注销及系统的启动、停机的事件

查看命令有cat ,more, less ,head等,查找相关内容grep,查找文件find,locate,whereis

8.编程,给定数组,有序,插入一个数的算法,返回应该插入的下标

复杂度较低的算法应该采用二分查找,普通的算法就是依次比较

class Helloworld
{
    public static void main(String[] args)
    {
        int[] arr = {13,15,19,28,33,45,78,106};
        int index = insertToArrary(arr,50);
        System.out.println("index = "+ index);
    }

    public static int insertToArrary(int[] arr, int key)
    {
        //头指针,中指针,尾指针
        int max,min,mid;
        min = 0;
        max = arr.length-1;

        while(min<=max)
        {
            mid = (min+max)/2;

            if(key>arr[mid])
                min = mid+1;
            else if(key<arr[mid])
                max = mid-1;
            else
                return mid;
            //在min<=max的情况下,遍历比较的最后结果是min=max,然后返回元素插入的位置
        }
        return min;
        //最后min>max的情况下,返回min值,就是元素插入的位置
    }
}

二分查找本身代码

public static int biSearch(int []array,int a){
        int lo=0;
        int hi=array.length-1;
        int mid;
        while(lo<=hi){
            mid=(lo+hi)/2;
            if(array[mid]==a){
                return mid+1;
            }else if(array[mid]<a){
                lo=mid+1;
            }else{
                hi=mid-1;
            }
        }
        return -1;
    }

9.编程,用两个栈实现一个队列

import java.util.Stack;

public class Demo07 {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);
    }

    public int pop() {
        if(stack2.size()<=0){
            while(stack1.size()>0){
                /*int data = stack1.peek();//查看栈顶元素,但不移除它
                stack1.pop();//弹出栈顶元素
                stack2.push(data);//压入
                 */                             
                stack2.push(stack1.pop());
            }
        }

        if(stack2.isEmpty()){
            try {
                throw new Exception("queue is empty.");
            } catch (Exception e) {
            }
        }
        /**
         * int head = stack2.peek();
         * stack2.pop();
         */
        int head = stack2.pop();
        return head;

    }
}

用两个队列实现一个栈,思想就是维持一个队列是空的

import java.util.LinkedList;

public class StackByTwoQueue {
    
    private LinkedList<String> queue1 = new LinkedList<String>();
    private LinkedList<String> queue2 = new LinkedList<String>();
    
    /*
     * 两个队列实现一个栈
     * pop完成出栈操作,push完成入栈操作
     */
    public void push(String obj) {
        if(queue1.isEmpty()){
            queue2.add(obj);
        }
        if(queue2.isEmpty()){
            queue1.add(obj);
        }
    }
    public String pop() {
        //两个栈都为空时,没有元素可以弹出
        if (queue1.isEmpty()&&queue2.isEmpty()) {
            try {
                throw new Exception("stack is empty");
            } catch (Exception e) {
            }
        }
        if(queue1.isEmpty()){
            while(queue2.size()>1){
                queue1.add(queue2.poll());
            }
            return queue2.poll();
        }
        if(queue2.isEmpty()){
            while(queue1.size()>1){
                queue2.add(queue1.poll());
            }
            return queue1.poll();
        }
        return null;
    }
    
    public static void main(String[] args) {
        StackByTwoQueue stack = new StackByTwoQueue();
        for(int i=0;i<10;i++){
            stack.push(i+"");
        }
        for(int i=0;i<20;i++){
            System.out.println(stack.pop());
        }
    }
    
}

10.socket编程

socket在应用层之下,传输层之上的一个借口


代码

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {
  public static void main(String[] args) throws Exception {
    // 监听指定的端口
    int port = 55533;
    ServerSocket server = new ServerSocket(port);
    
    // server将一直等待连接的到来
    System.out.println("server将一直等待连接的到来");
    Socket socket = server.accept();
    // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
    InputStream inputStream = socket.getInputStream();
    byte[] bytes = new byte[1024];
    int len;
    StringBuilder sb = new StringBuilder();
    while ((len = inputStream.read(bytes)) != -1) {
      //注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
      sb.append(new String(bytes, 0, len,"UTF-8"));
    }
    System.out.println("get message from client: " + sb);
    inputStream.close();
    socket.close();
    server.close();
  }
}

public class SocketClient {
  public static void main(String args[]) throws Exception {
    // 要连接的服务端IP地址和端口
    String host = "127.0.0.1"; 
    int port = 55533;
    // 与服务端建立连接
    Socket socket = new Socket(host, port);
    // 建立连接后获得输出流
    OutputStream outputStream = socket.getOutputStream();
    String message="你好  yiwangzhibujian";
    socket.getOutputStream().write(message.getBytes("UTF-8"));
    outputStream.close();
    socket.close();
  }
}

11.网络协议栈分为几层,TCP/IP运行于第几层,与UDP的区别

物理层,数据链路层,网络层,运输层,应用层,TCP/IP运行于运输层。TCP提供面向连接的服务,保证数据的传递,并提供拥塞控制机制,流模式,UDP提供无连接服务,数据报模式。

12.TCP/IP的握手过程

报文:SYN建立连接,FIN释放连接

总共需要客户端与服务器端发送三个包建立连接 客户端->(SYN)->服务器端 ->(SYN,ACK) ->客户端 ->(ACK)->服务器端

断开连接需要发送4个包 客户端 ->(FIN) ->服务器 ->(ACK)->(FIN) ->客户端 ->(ACK)

13.并发和并行的区别

并发:处理器同时处理多个任务,实际同一时刻只有一个任务在执行

并行:多核多处理器同时处理多个任务


第二天进行了二面,时间大概40分钟,其中问题和第一面有重复。

1.jvm垃圾回收机制及主要算法(相关概念及算法较多,继续学习深入理解java虚拟机后才能回答相关问题)

垃圾回收器负责分配内存,保证所有正在引用的对象存在于内存中,回收代码已不再引用的对象所在的内存。

分代垃圾回收器:不同的生命周期对象放在不同的地址池中,基于弱年代假设:越早分配的对象越容易失效,老对象很少会引用新对象

分为三个分代空间:

年轻代:两个suivivor,1个Eden区

Java应用在分配Java对象时,这些对象会被分配到年轻代堆空间中去

这个空间大多是小对象并且会被频繁回收

由于年轻代堆空间的垃圾回收会很频繁,因此其垃圾回收算法会更加重视回收效率

老年代:

年轻代堆空间的长期存活对象会转移到(也许是永久性转移)年老代堆空间

这个堆空间通常比年轻代的堆空间大,并且其空间增长速度较缓

由于大部分JVM堆空间都分配给了年老代,因此其垃圾回收算法需要更节省空间,此算法需要能够处理低垃圾密度的堆空间

持久代:

存放VM和Java类的元数据(metadata),以及interned字符串和类的静态变量

垃圾回收方式:

次收集:年轻带空间满后,触发,将还存活的对象移至老年代;

算法:Mark_copy,使用引用计数器进行标记,轮番使用suivivor区域,每次次收集后年纪+1;

全收集:老年代空间满后触发;持久代空间满后触发;System.gc()触发;

算法:

Serial GC 
Parallel GC 
Parallel Old GC(Parallel Compacting GC) 
Concurrent Mark & Sweep GC (or “CMS”) 
Garbage First (G1) GC

2.TCP为啥是可靠的

TCP在传输过程中,通信双方按照协议进行通信;将数据截断为合理的长度;超时重发;首部校验,若发现有改动则抛弃该包;通过可变大小的窗口协议进行流量控制;

针对乱序:在三次握手时,序列号被初始化,传输过程中将继续使用这个序列号,每传送一个包,序列号进行加1;TCP也会进行重新排序;

针对丢包:收到一个数据包过后,会用ACK应答码进行确认,发送方可以针对未确认的包进行重传;

针对重复:若已收到过该序列号的包,则丢弃;


3.TCP拥塞控制机制

可变大小的窗口协议:加法增大,乘法减小;

增大算法先采用慢启动算法,再才采用拥塞避免算法;


4.编程题,给定一棵二叉树,按层次打印出来,要求有换行;

如果是二叉树的层次遍历使用队列实现即可,入队,出队时若有左右子树则入队;

要求有换行的情况下需要增加两个指针,last指针指向每行结尾的节点,初始化为根节点,nlast指针指向最后入队的节点,初始化为null;当当前出队列的元素 = last时,则遇到一行结尾,打印换行符,并更新last 为 nlast定位到下一行结束节点(已经走到last表示已走完上一层,而此时队列尾的元素即为下一层的结尾节点)

public class TreeNode {

    int val = 0;

    TreeNode left = null;

    TreeNode right = null;

    public TreeNode(int val) {

        this.val = val;

    }

}

public class Solution {

    ArrayList > Print(TreeNode pRoot) {

        ArrayList > al=new ArrayList > ();

        ArrayList li=new ArrayList();    

        LinkedList theQueue=new LinkedList<>();

        TreeNode last=pRoot;

        TreeNode nlast=null;

        if(pRoot==null)

            return al;

        theQueue.add(pRoot);

        while(!theQueue.isEmpty())

            {

            TreeNode node=theQueue.remove();

            if(node.left!=null)

                {

                 theQueue.add(node.left);

                 nlast=node.left;

            }    

            if(node.right!=null)

                {

                theQueue.add(node.right);

                nlast=node.right;

            }

            li.add(node.val);

            if(node.equals(last))

                {

                al.add(new ArrayList(li));

                li.clear();

                last=nlast;

           }

        }

        return al;

    }

}

5.进程和线程的区别

(1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
(2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
(3)进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
(4)线程是轻两级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
(5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
(6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志

6.进程间通信

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

线程间通信:同一个进程间的可以直接进行通信,线程间的通信多用于线程的同步,所以线程没有像进程通信中用于数据交换的方式。

# 锁机制:包括互斥锁、条件变量、读写锁

# 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量

# 信号机制(Signal):类似进程间的信号处理

猜你喜欢

转载自blog.csdn.net/u011010851/article/details/79576895