剑指offer面试题(1-10)——java实现

面试题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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值