面试题整理(自用)
面试题1:赋值运算符函数
java语言没有运算符重载:
Java doesn’t support user-defined operator overloading.
Java不支持用户自定义操作符重载。
面试题2:实现singleton模式
设计模式常见面试题汇总
singleton是单列模式,属于设计模式中的一种,设计模式总共有 23 种,总体来说可以分为三大类:
1.创建型模式( Creational Patterns ):关注于对象的创建,同时隐藏创建逻辑
工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式
2.结构型模式( Structural Patterns ):关注类和对象之间的组合
适配器模式、过滤器模式、装饰模式、享元模式、代理模式、外观模式、组合模式、桥接模式 关注类和对象之间的组合
3.行为型模式( Behavioral Patterns ):关注对象之间的通信
责任链模式、命令模式、中介者模式、观察者模式、状态模式、策略模式、模板模式、空对象模式、备忘录模式、迭代器模式、解释器模式、访问者模式
单例模式:是一种常用的软件设计模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例。
优点:不会频繁地创建和销毁对象,浪费系统资源。
使用场景:IO 、数据库连接、Redis 连接等。
实现:
推荐解法一:饿汉模式
/*
* 优点:类加载的时候创建一次实例,避免了多线程同步问题
* 缺点:即使单例没被用到也会创建,浪费内存
*/
public class Hungry {
private static Hungry instance = new Hungry();
private Hungry(){}
public static Hungry getInstance(){
return instance;
}
}
推荐解法二:静态内部类
/*
* 只要程序中不使用内部类,JVM就不会去加载这个单例类,
* 也不会创建单例对象,从而实现懒汉式的延迟加载。
* 这种方式可以同时保证延迟加载和线程安全。
*/
public class StaticInternClass {
private StaticInternClass(){}
private static class SingletonHodler{
private static StaticInternClass INSTANCE = new StaticInternClass();
}
public static StaticInternClass getInstance(){
return SingletonHodler.INSTANCE;
}
}
面试题3:二维数组中的查找
二维数组从左上角到右下角依次递增,完成这样的函数:
输入一个这样的二维数组和一个数n,判断该数组是否含有该数字
思路:从右上角开始遍历,如果n大于数组中的数字,说明这个数字不在该行,如果小于则不在该列,以此类推
public class FindNumberIn2DArray {
public static boolean findNumberIn2DArray(int[][] array,int rows,int columns,int number){
boolean flag = false;
if (array != null && columns > 0 && rows > 0){
int row = 0,column = columns - 1;//开始遍历的位置,数组的右上角
while (row < rows && column >= 0){
if (number > array[row][column]) row++;//目标值大于数组中的数字,不在该行,行数加一
else if (number < array[row][column]) column--;//目标值小于数组中的数字,不在该列,列数加一
else{//找到了
flag = true;
break;
}
}
}
return flag;
}
public static void main(String[] args) {
int a[][] = {{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
System.out.println(findNumberIn2DArray(a,4,3,10));
}
}
面试题4:替换空格
实现一个函数,把字符串中的空格都替换成“%20”
思路:
1.创建新的字符串:由于在字符串中将一个字符换成了三个字符,所以如果在原来的字符串上做替换会覆盖后面的内容,所以我们可以能需要创建一个新的字符串。
public static String replace1(String str){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < str.length(); i++){
if (str.charAt(i) == ' '){
stringBuilder.append("%20");
}else {
stringBuilder.append(str.charAt(i));
}
}
return stringBuilder.toString();
}
2.假设面试官让我们在原来的字符串上做替换,并且要保证后面有足够多的内存空间:
在字符串尾部填充任意字符,使得字符串的长度等于替换之后的长度。因为一个空格要替换成三个字符(%20),因此当遍历到一个空格时,需要在尾部填充两个任意字符。
令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。P1 和 P2 从后向前遍历,当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填充上 P1 指向字符的值。
从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。
public static String replace2(String str){
StringBuilder stringBuilder = new StringBuilder(str);
int p1 = stringBuilder.length() - 1;
for (int i = 0; i < p1; i++){
if (' ' == str.charAt(i)) stringBuilder.append(" ");
}
int p2 = stringBuilder.length() - 1;
while (p1 >= 0 && p2 > p1){
char temp = stringBuilder.charAt(p1--);
if (' ' == temp){
stringBuilder.setCharAt(p2--,'0');
stringBuilder.setCharAt(p2--,'2');
stringBuilder.setCharAt(p2--,'%');
}else {
stringBuilder.setCharAt(p2--,temp);
}
}
return stringBuilder.toString();
}
3.使用内置函数:indexOf 和 replace
public static String replace3(String str){
StringBuilder stringBuilder = new StringBuilder(str);
int index = 0;
while ((index = stringBuilder.indexOf(" ",index)) != -1){
stringBuilder.replace(index,index + 1,"%20");
}
return stringBuilder.toString();
}
面试题5:从尾到头打印链表
思路:借助栈结构
import java.util.Stack;
class Node{
int value;
Node next;
public Node() {}
public Node(int value) {
this.value = value;
}
}
public class ShowListFromTail {
public static void showListFromTail(Node head){
Node current = head;
if (current == null) return;
Stack<Node> nodes = new Stack<>();
while (current != null){
nodes.push(current);
current = current.next;
}
while (!nodes.empty()){
System.out.println(nodes.pop().value);
}
}
public static void main(String[] args) {
Node node = new Node(1);
Node node1 = new Node(2);
node.next = node1;
Node node2 = new Node(3);
node1.next = node2;
Node node3 = new Node(4);
node2.next = node3;
Node node4 = new Node( 5);
node3.next = node4;
showListFromTail(node);
}
}
面试题6:重建二叉树
输入一棵树的前序和后序遍历,重建出该二叉树并输出它的头结点(假设二叉树中不存在重复的数字)
思路:前序遍历第一个就是根节点,中序遍历中根节点左边的就是左子树,右边的就是右子树,再分别分析左子树和右子树,如此递归求解
class Node{
int value;
Node left;
Node right;
public Node() {}
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
", left=" + left +
", right=" + right +
'}';
}
}
public class RebuildBinaryTree {
public static Node rebuildBinaryTree(int[] preOrder,int preOrderStart,int preOrderEnd,
int[] inOrder,int inOrderStart,int inOrderEnd){
if (preOrderStart > preOrderEnd) return null;
if (inOrderStart > inOrderEnd) return null;
int value = preOrder[preOrderStart],i = inOrderStart;
Node root = new Node(value);
for (; i <= inOrderEnd; i++){
if (value == inOrder[i]){
break;
}
}
// i - inOrderStart是左子树的节点个数
root.left = rebuildBinaryTree(preOrder,preOrderStart + 1,preOrderStart + i - inOrderStart,
inOrder,inOrderStart,i - 1);
// i - inOrderStart + 1跳过根节点
root.right = rebuildBinaryTree(preOrder,preOrderStart + i - inOrderStart + 1,preOrderEnd,
inOrder,i + 1,inOrderEnd);
return root;
}
public static void main(String[] args) {
int[] preOrder = {1,2,4,7,3,5,6,8};
int[] inOrder = {4,7,2,1,5,3,8,6};
System.out.println(rebuildBinaryTree(preOrder,0,7,inOrder,0,7).toString());
}
}
面试题7:用两个栈实现队列
实现appedTail 和 deleteHead 方法
思路:队列的特性是先进先出,使用两个栈怎么实现呢
加入元素时:直接push进第一个栈
删除元素时:
1.如果第二栈为空,将第一个栈的元素弹出放入第二个栈中,直到第一个栈为空,然后弹出第二个栈的栈顶元素
2.如果第二个栈不为空,直接弹出栈顶元素
import java.util.Stack;
public class TwoStackTobeQueue {
private static Stack<Integer> stack1 = new Stack<>();
private static Stack<Integer> stack2 = new Stack<>();
public static void appendTail(int number){
stack1.push(number);
System.out.println(number + "========添加至队尾");
}
public static void deleteHead(){
if (!stack2.empty()) {
System.out.println( stack2.pop() + "========被删除");
}
else {
if (stack1.empty()){
System.out.println("没有元素可被删除!");
return;
}
while (!stack1.empty()){
stack2.push(stack1.pop());
}
System.out.println( stack2.pop() + "========被删除");
}
}
public static void main(String[] args) {
appendTail(1);
appendTail(2);
appendTail(3);
deleteHead();
appendTail(4);
deleteHead();
deleteHead();
deleteHead();
deleteHead();
}
}
面试题8:旋转数组的最小数字
把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的旋转。
输入一个递增排序的一个旋转,输出旋转数组的最小元素。
如{3,4,5,1,2}是{1,2,3,4,5}的一个旋转,该数组的最小值为1
思路:如果我们直接遍历数组,可以用O(n)的时间求解,但是这没有利用二维数组的旋转这一性质,要使用这一性质,我们可以参照二分查找法的思路。
使用两个指针,分别指向第一个元素和最后一个元素。
一般情况下,第一个指针指向的元素总是大于或等于最后一元素,此时我们找到中间元素,如果中间元素比第一个指针大,说明最小元素在右边,如果反之在左边;
特殊情况就是把最开始的0个元素放在数组末尾,此时第一个元素一定是最小的元素
public class SmallestNumberInRotateArray {
public static int findSmallestNumberInRotateArray(int[] array,int start,int end) throws Exception {
int len = array.length;
if (len == 0) throw new Exception("数组为空!");
// 特殊情况
else if (array[start] < array[end]) return array[start];
// 平凡情况
else if (end - start == 1) return array[start] < array[end] ? array[start] : array[end];
else {
int middle = (start + end) / 2;
if (array[middle] > array[start]) start = middle;
else end = middle;
}
return findSmallestNumberInRotateArray(array,start,end);
}
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try {
System.out.println(findSmallestNumberInRotateArray(arr,0,4));
} catch (Exception e) {
e.printStackTrace();
}
}
}
面试题9:斐波那契数列
0 1 1 2 3 5 8 13 21 33 ...第n个数是第n-1和n-2个数的和
思路:用递归很容易就做出来了,但是效率不高,我们可以将上一个得到的数字保存起来,用循环求解
public class Fibonaci {
public static int getFiboaci(int n) throws Exception {
if (n <= 0) throw new Exception("非法输入!");
if (n == 1) return 0;
int n_1 = 1,n_2 = 0,result = 0;
for (int i = 2;i < n;i++){
result = n_1 + n_2;
n_2 = n_1;
n_1 = result;
}
return result;
}
public static void main(String[] args) {
try {
System.out.println(getFiboaci(8));
} catch (Exception e) {
e.printStackTrace();
}
}
}
面试题10:二进制中1的个数
输入一个整数,输出该整数的二进制表示中1的个数
思路1:将整数和flag=1做&运算,等于1则1的个数加一,并把flag左移一位
public static int findOneNumber1(int number){
int result = 0,flag = 1;
if (number < 0) {
number = -number;
result++;
}
while (flag <= number){
if ((flag & number) != 0) result++;
flag = flag << 1;
}
return result;
}
思路2:把一个整数减去一再和原整数做与运算,会把该整数最右边的一个1变成0
public static int findOneNumber2(int number){
int result = 0;
if (number < 0){
number = -number;
result++;
}
while (number != 0){
result++;
number = (number - 1) & number;
}
return result;
}