实际面试遇到的代码题(2023年第一部分)

写在开始 :
① 本文主要记录笔者目前实际遇到过的代码题,主要是二分查找,冒泡排序,DCL单例模式和 一道关于字符串的处理问题, 小计八千多字,300多行, 阅读花费时间不多,;
② 建议每一种至少会手敲一种代码实现方式,其余思路了解即可;(必须盲敲一种解决办法, 先实现再优化!)
③ 仅用作个人记录, 复习交流, 希望有所帮助!

基础篇(算法排序)

1. 二分查找

1681300115950.png

public class BinarySearch_01 {

    public static void main(String[] args) {
        int[] arr = {1,5,8,11,19,23,45,48,49,50,66};
        int target = 48;
        int idx = binarySearch(arr,target);
        System.out.println(idx);
    }

    private static int binarySearch(int[] arr, int target) {
        int l = 0, r = arr.length -1, m;
        while (l <= r){
            m = (l+r)/2;
            if (arr[m] == target){
                return m;
            } else if (arr[m] > target){
                r = m-1;
            } else {
                l = m+1;
            }
        }
        return -1;
    }

}

:::info

  1. 整数溢出问题: m=l/2 + r/2 = l + (-l/2 + r/2) = l + (r-l)/2
  2. 无符号右移位运算: int m = l + (r-l) >>> 1; m= (r+l) >>> 1; 比除法的效率高 (因为CPU在处理位运算比处理除法等运算的时间周期消耗更少) 还能解决溢出问题
    :::
public class IntOverFlow {
    public static void main(String[] args) {
        int l = 0;
        int r = Integer.MAX_VALUE -1 ;
        int m = l + (r-l)/2;
        System.out.println(m);//1073741823

        l= m+1;
        m= l + (r-l)/2;
        System.out.println(m);//1610612735
        System.out.println(r);//2147483646
    }
}
public class IntOverFlow {
    public static void main(String[] args) {
        int l = 0;
        int r = Integer.MAX_VALUE -1 ;
        int m = l + (r-l) >>> 1;
        System.out.println(m);//1073741823

        l= m+1;
        m= (r+l) >>> 1;
        System.out.println(m);//1610612735
        System.out.println(r);//2147483646
    }
}
二分真题

1681302731409.png

tip

以上是以 jdk 中Arrays.binarySearch 实现作为讲解示范,
实际二分查找又很多变体,一旦使用变体实现,那么左右边界的选取会有变化, 力扣上有三种,可以参考.

2. 冒泡排序(算法)

package ithm;

import java.util.Arrays;

public class BubbleSort {

    public static void main(String[] args) {
        int[] arr = {5, 7, 9, 4, 1, 3, 2, 8};
        bubble(arr);
    }

    private static void bubble(int[] arr) {
        for (int j = 0; j < arr.length -1; j++) {
            for (int i = 0; i < arr.length-1; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                }
            }
            System.out.println("第" + (j + 1) + "轮冒泡" + Arrays.toString(arr));
        }
    }

    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}

1681303533969.png

package ithm;

import java.util.Arrays;

public class BubbleSort {

    public static void main(String[] args) {
        int[] arr = {5, 7, 9, 4, 1, 3, 2, 8};
        bubble(arr);
    }

    private static void bubble(int[] arr) {
        for (int j = 0; j < arr.length -1; j++) {
            for (int i = 0; i < arr.length-1-j; i++) { //减少比较次数
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                }
            }
            System.out.println("第" + (j + 1) + "轮冒泡" + Arrays.toString(arr));
        }
    }

    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}

package ithm;

import java.util.Arrays;

public class BubbleSort {

    public static void main(String[] args) {
        int[] arr = {5, 7, 9, 4, 1, 3, 2, 8};
        bubble(arr);
    }

    private static void bubble(int[] arr) {
        for (int j = 0; j < arr.length -1; j++) {
            boolean flag = false; // 是否发生交换
            for (int i = 0; i < arr.length-1-j; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                    flag = true;
                }
            }
            System.out.println("第" + (j + 1) + "轮冒泡" + Arrays.toString(arr));
            if (!flag){
                break;
            }
        }
    }

    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}

image.png

    private static void bubble(int[] arr) {
        for (int j = 0; j < arr.length -1; j++) {
            boolean flag = false; // 是否发生交换
            for (int i = 0; i < arr.length-1-j; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                    flag = true;
                }
            }
            System.out.println("第" + (j + 1) + "轮冒泡" + Arrays.toString(arr));
            if (!flag){
                break;
            }
        }
    }

image.png1681305811281.png
:::info

升序冒泡排序: 依次比较数组中相邻两个元素大小,如果后一个数相对更小,则交换两个元素,两两都比较一遍称为一轮冒泡,结果最大的元素在最后;
重复该步骤,直到整个数组有序;

:::

设计模式(单例模式双重检查锁模式DCL)

3. DCL

// 使用双重检查锁定(Double-Checked Locking,DCL)实现的单例模式

public class Singleton {

    // 私有的该类的引用
    private volatile static Singleton instance;

    private Singleton() {
        // 私有构造函数,防止外部实例化
    }
    
	// 提供公共的访问方式
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

//volatile 关键字用于确保变量可见性和禁止指令重排序,从而避免 DCL 的潜在问题。在 getInstance() 
//方法中,首先检查实例是否已经被创建,如果没有,则获取锁进行同步,并再次检查实例是否已被创建。
//这样可以避免多个线程同时进入同步块创建实例,提高了性能。
public class Demo {

    // 构造器私有化
    private Demo() {
    }

    // 属性私有化
    private volatile static Demo demo ;

    // 公共的访问方式
    public static Demo getInstance() {

        if (demo == null) {
            synchronized (Demo.class) {
                if (demo == null) {
                    demo = new Demo();
                }
            }
        }

        return demo;
    }


}

关于设计模式,
第一个单例模式的话, 大家最好实现简单的饿汉式,懒汉式以及DCL的实现方式, 并对静态内部类,枚举类等其余方式简单了解,还有通过序列化等方式破坏单例也可以简单了解;
第二个合计23种设计模式, 大家尽量熟悉常用的几款设计模式, 比如单例,工厂,策略,责任链,代理… 剩余的设计模式了解一下, 无论是面试还是后续工作擅于发现的话都高频出现 ! 比如Spring框架涉及到的十几种设计模式…

其余类型

题目一.输入字符串并输出符合要求的数字列表

//输入:CM->123->RM->123->256->234->178->178->RO>256->179 / 输入字符串由”CM、RM、RO”这三个字符串和若干数字通过”->”组合而成,中间不存在空格,”->”表示数字或者字符串之间的递进关系; // 输出:[123,234,178,256,179]

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

// 基于正则表达式和哈希集合
public class Demo {
    public static void main(String[] args) {
        String input = "CM->123->RM->123->256->234->178->178->RO>256->179";
        List<Integer> numbers = parseInputString(input);
        System.out.println(numbers);
    }

    public static List<Integer> parseInputString(String input) {
        String[] tokens = input.split("->");
        Set<String> visited = new HashSet<>();
        List<Integer> numbers = new ArrayList<>();

        for (String token : tokens) {
            if (token.matches("-?\d+")) { // 判断是否为数字
                Integer number = Integer.parseInt(token);
                if (!visited.contains(number.toString())) {
                    numbers.add(number);
                    visited.add(number.toString());
                }
            }
        }

        return numbers;
    }
}
package com.itkaka;

/*
*  从字符串中提取不重复的数字?输入:CM->123->RM->123->256->234->178->178->RO->256->179,输出[123,234,178,256,179]。
* */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo3 {

    public static void main(String[] args) {

        String s = "CM->123->RM->123->256->234->178->178->RO->256->179";
        String[] strs = s.split("\\D+");
        System.out.println(Arrays.toString(strs)); //[, 123, 123, 256, 234, 178, 178, 256, 179]

        List<Integer> list = new ArrayList<>();
		// TODO 
    	// 使用set 集合方法去重, 
    }

}

// 方法一:使用正则表达式和LinkedHashSet
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        String input = "CM->123->RM->123->256->234->178->178->RO>256->179";
        Set<Integer> numbers = extractUniqueNumbers(input);
        System.out.println(numbers);
    }

    public static Set<Integer> extractUniqueNumbers(String input) {
        Set<Integer> numbers = new LinkedHashSet<>();
        Pattern pattern = Pattern.compile("\d+");
        Matcher matcher = pattern.matcher(input);

        while (matcher.find()) {
            numbers.add(Integer.parseInt(matcher.group()));
        }

        return numbers;
    }
}
//使用正则表达式 \d+ 匹配连续的数字,并使用 Matcher 对象逐个查找数字。然后,将不重复的数字添加
//到 LinkedHashSet 中,由于 LinkedHashSet 保留插入顺序,结果集将按照数字第一次出现的顺序排列。
// 使用Stream API和去重操作

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo1 {
    public static void main(String[] args) {
        String input = "CM->123->RM->123->256->234->178->178->RO>256->179";
        List<Integer> numbers = extractUniqueNumbers(input);
        System.out.println(numbers);
    }

    public static List<Integer> extractUniqueNumbers(String input) {
        List<Integer> numbers = Stream.of(input.split("\D+"))
                .filter(s -> !s.isEmpty())
                .map(Integer::parseInt)
                .distinct()
                .collect(Collectors.toList());

        return numbers;
    }
}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo1 {

    public static void main(String[] args) {
/*
\\D+:表示至少一个非数字。
String[] strs = s.split("\\D+");:将字符串中的非数字字符作为分隔符,分割成多个子字符串并保存到一个字符串数组中。
if (!str.isEmpty()) { list.add(Integer.parseInt(str)); }:将子字符串转换为整数并存储到一个数组中。
Integer[] nums = list.toArray(new Integer[0]);:将存储数字的List转换为数组。*/
        String s = "CM->123->RM->178-179";
        String[] strs = s.split("\\D+");
        List<Integer> list = new ArrayList<>();

        for (String st:strs
             ) {
            if (!st.isEmpty()){
                list.add(Integer.parseInt(st));
            }
        }
        Integer[] nums = list.toArray(new Integer[0]);
        System.out.println(nums);// [Ljava.lang.Integer;@57829d67
        System.out.println(Arrays.toString(nums));//[123, 178, 179]



    }

}

使用 split(“\D+”) 将字符串分割为数字和非数字部分,并且通过 filter 方法过滤掉空字符串。然后,使用map 方法将字符串转换为整数,再通过 distinct 方法去除重复的数字,最后使用 collect 方法收集结果为列表。

题目二:最长公共前缀



/*
*       编写一个函数来查找字符串数组中的最长公共前缀。
        输入:strs = ["flower","flow","flight"]
        输出:"fl"
* */
public class Demo2 {

    public static void main(String[] args) {
        String[] strs = {"flower","flow","flight"};
        System.out.println(result(strs)); // fl
    }

    // 返回一个公共前缀的字符串(可能是空)
    public static String result(String[] strs) {

        // 参数非空校验
        if (strs == null || strs.length == 0) {
            return "";
        }

        // 依次遍历元素,看是否重复,不重复直接返回,重复继续往后读
        String temp = strs[0];

        for (int i = 1; i < strs.length; i++) {
            String str1 = strs[i];

            int j = 0;

            while (j < temp.length() && j < str1.length() && temp.charAt(j) == str1.charAt(j)) {
                j++;

            }
            temp = temp.substring(0, j);

			// 这里需要再加一个校验
			if (temp.isEmpty()){
				break;
			}
        }

        return temp;

    }


}

写在最后 :
周六了, 下着小雨🌧 , 希望这篇博文 能够对大家有帮助 !
码字不易, 希望大家能够点赞加关注, 祝愿大家 代码0 BUG, 技术和收入节节高!💪💪💪💪

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值