java面经整理(2)

来源:互联网 发布:软件的应用价值 编辑:程序博客网 时间:2024/06/05 18:12

https://www.nowcoder.com/discuss/28073?type=2&order=3&pos=43&page=1

一.翻转二叉树

思路:对每一个结点,将它的左右子树进行交换,再对它的左右子结点进行同样的操作。递归

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode(int x) { val = x; }

 * }

 */

public class Solution {

    public TreeNode invertTree(TreeNode root) {

        if(root==null){

            return null;

        }

        if(root.left!=null){

            invertTree(root.left);

        }

        if(root.right!=null){

            invertTree(root.right);

        }

        TreeNode temp = root.left;

        root.left = root.right;

        root.right = temp;

        return root;

    }

}

二.notify()工作原理

java会为每个object对象分配一个monitor,当某个对象的同步方法)或同步快被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。

当一个线程调用一个对象的同步方法时,JVM会检查该对象的monitor。如果monitor没有被占用,那么这个线程就得到了monitor的占有权,可以继续执行该对象的同步方法;如果monitor被其他线程所占用,那么该线程将被挂起(wait,直到monitor被释放(notify

Ps.sleep()方法是Thread类的静态方法,不涉及到线程间同步概念,仅仅为了让一个线程自身获得一段沉睡时间,与wait区别,wait会挂起

三.翻转字符串

For example,

Given s = "the sky is blue",

return "blue is sky the".

我们首先将原字符串调用trim()来去除冗余空格,然后调用split()来分隔,分隔符设为"\\s+",这其实是一个正则表达式,\\s表示空格字符,+表示可以有一个或多个空格字符,那么我们就可以把单词分隔开装入一个字符串数组中,然后我们从末尾开始,一个个把单词取出来加入结果res中,并且单词之间加上空格字符,注意我们把第一个单词留着不取,然后返回的时候再加上即可

public class Solution {

    public String reverseWords(String s) {

        String res = "";

        String[] words = s.trim().split("\\s+");

        for (int i = words.length - 1; i > 0; --i) {

            res += words[i] + " ";   

        }

        return res + words[0];

    }

}

四.volitale的用途

在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。 

要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。 

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 

并发的三个问题:

1.原子性

是指一些操作或者全都执行,要么或者全都不执行,整个操作作为一个整体是不可分割的

比如"a = 1;""return a;"这样的操作都具有原子性。类似"a += b"这样的操作不具有原子性,在某些JVM"a += b"可能要经过这样三个步骤:

① 取出ab

② 计算a+b

③ 将计算结果写入内存

volitale不保证原子性,synchronized保证

2.可见性

java中每个线程都有自己的线程栈,当一个线程执行需要数据时,会到内存中将需要的数据复制到自己的线程栈中,然后对线程栈中的副本进行操作,再操作完成后再将数据写回到内存中。

Eg.    i = 3;

i = i+3;

i++;

i = j;

线程 1i的值读到自己的线程栈中,然后对i进行了加3操作,但是这一操作并没有被及时的写回到内存中,所以线程2在执行时看到的i的值仍然是3,两个线程在操作共享数据时,对共享数据的操作是彼此不可见的,针对这个问题,引入volitale,让其可见,也可以用synchronized同步锁

3.有序性

有序性:即程序执行的顺序按照代码的先后顺序执行,volitale一定程度上能实现,synchronized也是

 

五.静态方法和普通方法同时加上synchronized有什么区别

1.Synchronized修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。

   

 情况1:同一个对象在两个线程中分别访问该对象的两个同步方法

结果:会产生互斥。

解释:因为锁针对的是对象,当对象调用一个synchronized方法时,其他同步方法需要等待其执行结束并释放锁后才能执行。

 

 

情况2:不同对象在两个线程中调用同一个同步方法

结果:不会产生互斥。

解释:因为是两个对象,锁针对的是对象,并不是方法,所以可以并发执行,不会互斥。形象的来说就是因为我们每个线程在调用方法的时候都是new一个对象,那么就会出现两个空间,两把钥匙,

 

2.Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。

情况1:用类直接在两个线程中调用两个不同的同步方法

结果:会产生互斥。

解释:因为对静态对象加锁实际上对类(.class)加锁,类对象只有一个,可以理解为任何时候都只有一个空间,里面有N个房间,一把锁,因此房间(同步方法)之间一定是互斥的。

 

情况2:用一个类的静态对象在两个线程中调用静态方法或非静态方法

结果:会产生互斥。

解释:因为是一个对象调用,同上。

 

情况3:一个对象在两个线程中分别调用一个静态同步方法和一个非静态同步方法

结果:不会产生互斥。

解释:因为虽然是一个对象调用,但是两个方法的锁类型不同,调用的静态方法实际上是类对象在调用,即这两个方法产生的并不是同一个对象锁,因此不会互斥,会并发执行。

 

六.接雨水问题

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example, 
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

 

用两个指针分别指向数组的头和数组尾。 然后每次比较两个指针所指向的值,选小值指针向中间移动,并且每次更新遍历LeftMostHeight或者RightMostHeight, 这样就可以算出每个点的可以接的雨水数目。

    public int trapRainWater1(int[] heights) {

     int res = 0;

     int l= 0 ,r = heights.length - 1;

     int lmax = 0 , rmax= 0;

     while(l < r){

     lmax = Math.max(lmax, heights[l]);

     rmax = Math.max(rmax, heights[r]);

     if(lmax < rmax){

     res += lmax - heights[l];

     l++;

     }else{

     res += rmax - heights[r];

     r--;

     }

     }

    

    

     return res;

}

七.整数去重

循环嵌套

String[] array = {"a","b","c","c","d","e","e","e","a"};

List<String> list = new ArrayList<>();

for(int i=0;i<array.length;i++){

for(int j=i+1;j<array.length;j++){

if(array[i] == array[j]){

j = ++i;

}

}

list.add(array[i]);

}