当前位置: 首页 > article >正文

62-Java-面试专题(1)__基础

62-Java-面试专题(1)__基础-- 笔记

笔记内容来源与黑马程序员教学视频


文章目录

  • 62-Java-面试专题(1)__基础-- 笔记
  • Java-面试专题(1)
    • 笔记中涉及资源:
  • 一、二分查找
    • ①:代码实现
      • 1. 流程
      • 2. 代码实现
      • 3. 测试
    • ②:解决整数溢出(方法一)
    • ③:解决整数溢出(方法二)
    • ③:选择题目
    • ④:注意事项
  • 二、冒泡排序
    • ①:初步实现
    • ②:减少冒泡次数
    • ③:进一步优化
    • ④:总结
  • 三、选择排序
    • ①:代码实现
    • ②:总结
  • 四、插入排序
    • ①:代码实现
    • ②:总结
    • ③:插入和选择推到某一论排序结果
  • 五、快速排序
    • ①:文字描述
    • ②:单边循环
    • ③:双边循环
      • 1. 代码实现
      • 2. 注意事项
    • ④:特点
  • 六、ArrayList扩容规则
  • 七、Iterator_FailFast_FailSafe
  • 八、LinkedList
  • 九、HashMap


Java-面试专题(1)

笔记中涉及资源:

一、二分查找

①:代码实现

1. 流程

  • 前提:有已排序数组A(假设已经做好)

  • 定义左边界L、右边界R,确定搜索范围,循环执行二分查找(3、4两步)

  • 获取中间索引M=Floor(L+R)/2)

  • 中间索引的值A[M]与待搜索的值T进行比较

    • ① A[M]==T表示找到,返回中间索引
    • ② A[M>T,中间值右侧的其它元素都大于T,无需比较,中间索引左边去找,M-1设置为右边界,重新查找
    • ③ A[M]<T,中间值左侧的其它元素都小于T,无需比较,中间索引右边去找,M+1设置为左边界,重新查找
  • 当L>R时,表示没有找到,应结束循环

2. 代码实现

    /**
     * 数据准备初始化一个排好序的数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 10;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        // 排序
        Arrays.sort(array);
        return array;
    }

    /**
     * 二分查找代码实现
     */
    public static void testBinarySearch() {
        int[] array = initArray();
        System.out.println(Arrays.toString(array));
        System.out.println("请输入您要查找的数据:");
        int number = scanner.nextInt();
        
        // 初始化 开头 结尾 中间的下标
        int start = 0, end = array.length -1, middle;
        while (start <= end) {
            middle = (start + end) >>1;
            if (array[middle] == number){
                System.out.println("您要查找的数字下标为:" + middle);
                return;
            }else if (array[middle] > number){
                end = middle - 1;
            }else {
                start = middle +1;
            }
        }
        System.err.println("您要查找的数字不存在!");
    }

    public static void main(String[] args) {
        testBinarySearch();
    }

3. 测试

在这里插入图片描述
在这里插入图片描述

②:解决整数溢出(方法一)

在这里插入图片描述

③:解决整数溢出(方法二)

在这里插入图片描述

③:选择题目

  • 1.有一个有序表为1,5,8,11,19,22,31,35,40,45,48,49,50当二分查找值为48的结点时,查找成功需要比较的次数
4
  • 奇数二分取中间

  • 偶数二分取中间靠左

  • 2.使用二分法在序列1,4,6,7,15,33,39,50,64,78,75,81,89,96中查找元素81时,需要经过()次比较

4
  • 3.在已经的128个数组中二分查找一个数,需要比较的次数最多不超过多少次
7
  • 2n=128或128/2/2…直到1
  • 问题转化log^2 128,如果手边有计算器,用log10 128/log10 2
    • 是整数,则该整数即为最终结果
    • 是小数,则舍去小数部分,整数加一为最终结果

④:注意事项

  • 1.目前介绍的二分查找是以jdk中Arrays.binarySearch的实现作为讲解示范,后续选择题的解答思路也是以此为准
  • 2.但实际上,二分查找有诸多变体,一旦使用变体的实现代码,则左右边界的选取会有变化,进而会影响之前选择题的答案选择

二、冒泡排序

①:初步实现

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    public static void main(String[] args) {
        // 调用方法获取一个无序的数组
        int[] array = initArray();
        System.out.println("排序前 :" + Arrays.toString(array));
        for (int i = 0; i < array.length -1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]){
                    array[j + 1] = array[j + 1] + array[j];
                    array[j] = array[j + 1] - array[j];
                    array[j + 1] = array[j + 1] - array[j];
                }
            }

        }
        System.out.println("排序后 :" + Arrays.toString(array));
    }

在这里插入图片描述

②:减少冒泡次数

在这里插入图片描述

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    public static void main(String[] args) {
        // 调用方法获取一个无序的数组
        int[] array = initArray();
        System.out.println("排序前 :" + Arrays.toString(array));
        for (int i = 0; i < array.length -1; i++) {
            // 是否发生交换
            boolean swapped = false;
            for (int j = 0; j < array.length - 1 - i; j++) {
                System.out.println("比较次数:" + j);
                if (array[j] > array[j + 1]){
                    array[j + 1] = array[j + 1] + array[j];
                    array[j] = array[j + 1] - array[j];
                    array[j + 1] = array[j + 1] - array[j];
                    swapped = true;
                }
            }
            if (!swapped) {
                break;
            }
            System.out.println("第" + (i+1) + "排序 :" + Arrays.toString(array));
        }
    }

在这里插入图片描述

③:进一步优化

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    public static void main(String[] args) {
        // 调用方法获取一个无序的数组
        int[] array = initArray();
        int n = array.length - 1;
        System.out.println("排序前 :" + Arrays.toString(array));
        for (int i = 0; i < n; i++) {
            int last = 0;
            for (int j = 0; j < n; j++) {
                System.out.println("比较次数:" + j);
                if (array[j] > array[j + 1]){
                    array[j + 1] = array[j + 1] + array[j];
                    array[j] = array[j + 1] - array[j];
                    array[j + 1] = array[j + 1] - array[j];
                    last = j;
                }
            }
            n = last;
            System.out.println("第" + (i+1) + "排序 :" + Arrays.toString(array));
        }
    }

在这里插入图片描述

④:总结

  • 文字描述
    (以升序为例)

    • 1.依次比较数组中相邻两个元素大小,若[a]>a[+1],则交换两个元素,两两都比较一遍称为一轮冒泡,结果是让最大的元素排至最后
    • 2.重复以上步骤,直到整个数组有序
  • 优化方式:

    • 每轮冒泡时,最后一次交换索引可以作为下一轮冒泡的比较次数,如果这个值为零,表示整个数组有序,直接退出外层循环即可

三、选择排序

①:代码实现

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    public static void main(String[] args) {
        int[] array = initArray();
        System.out.println("排序前 :" + Arrays.toString(array));
        for (int i = 0; i < array.length - 1; i++) {
            // 每轮最小值对应的下标
            int minIndex = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[minIndex] > array[j]){
                    minIndex = j;
                }
            }
            if (minIndex != i) {
                array[i] = array[minIndex] + array[i];
                array[minIndex] = array[i] - array[minIndex];
                array[i] = array[i] - array[minIndex];
            }
            System.out.println("第" + (i+1) + "次排序 :" + Arrays.toString(array));
        }
    }

②:总结

文字描述(以升序为例)

  • 1.将数组分为两个子集,排序的和未排序的,每一轮从未排序的子集中选出最小的元素,放入排序子集
  • 2.重复以上步骤,直到整个数组有序

优化方式

  • 1.为减少交换次数,每一轮可以先找最小的索引,在每轮最后再交换元素

与冒泡排序比较

  • 1.二者平均时间复杂度都是0(n2)
  • 2.选择排序一般要快于冒泡,因为其交换次数少
  • 3.但如果集合有序度高,冒泡优于选择
  • 4.冒泡属于稳定排序算法,而选择属于不稳定排序

四、插入排序

①:代码实现

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    public static void main(String[] args) {
        int[] array = initArray();
        System.out.println("排序前 :" + Arrays.toString(array));
        for (int i = 1; i < array.length; i++) {
            // 假设最小值
            int minNum = array[i];
            int j = i-1;
            while (j >= 0) {
                if (minNum < array[j]) {
                    array[j + 1] = array[j];
                }else {
                    break;
                }
                j--;
            }
            array[j + 1] = minNum;
            System.out.println("第" + (i) + "次排序 :" + Arrays.toString(array));
        }
    }

②:总结

文字描述(以升序为例)

  • 1.将数组分为两个区域,排序区域和未排序区域,每一轮从未排序区域中取出第一个元素,插入到排序区域(需保证顺序)
  • 2.重复以上步骤,直到整个数组有序
    优化方式
  • 1.待插入元素进行比较时,遇到比自己小的元素,就代表找到了插入位置,无需进行后续比较
  • 2.插入时可以直接移动元素,而不是交换元素
    与选择排序比较
  • 1.二者平均时间复杂度都是0(n2)
  • 2.大部分情况下,插入都略优于选择
  • 3.有序集合插入的时间复杂度为O(m)公
  • 4.插入属于稳定排序算法,而选择属于不稳定排序

③:插入和选择推到某一论排序结果

1. 使用直接插入排序算法对序列18,23,19,9,23,15进行排序,第三趟排序后的结果为()

  • A.9,18,15,23,19,23
  • B.18,23,19,9,23,15
  • C.18,19,23,9,23,15
  • D.9,18,19,23,23,15

2. 使用直接选择排序算法对序列18,23,19,9,23,15进行排序,第3趟排序后的结果为()

  • A.9,23,19,18,23,15
  • B.9,15,18,19,23,23
  • C.18,19,23,9,23,15
  • D.18,19,23,9,15,23

五、快速排序

①:文字描述

  • 每一轮排序选择一个基准点(pivot)进行分区

    • 1.让小于基准点的元素的进入一个分区,大于基准点的元素的进入另一个分区
    • 2.当分区完成时,基准点元素的位置就是其最终位置
  • 在子分区内重复以上过程,直至子分区元素个数少于等于1,这体现的是分而治之的思想(divide-and-conquer)

②:单边循环

单边循环快排(lomuto洛穆托分区方案)

  • 选择最右元素作为基准点元素
  • j指针负责找到比基准点小的元素,一旦找到则与ⅰ进行交换
  • ⅰ指针维护小于基准点元素的边界,也是每次交换的目标索引
  • 最后基准点与ⅰ交换,ⅰ即为分区位置

在这里插入图片描述

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    /**
     * 选择一个基准点(pivot)进行分区
     * @param array 排序数组
     * @param l 左边界
     * @param pivot 基准点
     * @return 返回值 = 分区后中间索引值
     */
    public static int  partition(int[] array, int l, int pivot){
        // 右侧基准点(值)
        int pivotNum = array[pivot];
        int i = l;
        for (int j = l; j < pivot; j++) {
            if (array[j] < pivotNum) {
                if (i != j){
                    swap(array, i, j);
                }
                i ++;
            }
        }
        if (i != pivot){
            swap(array, i, pivot);
        }
        return i;
    }

    /**
     * 交换位置
     * @param array 数组
     * @param i 位置1
     * @param j 位置2
     */
    private static void swap(int[] array, int i, int j) {
        int temp = array[j];
        array[j] = array[i];
        array[i] = temp;
    }

    /**
     * 递归排序
     * @param array 排序数组
     * @param l 左边界
     * @param pivot 基准点
     */
    public static void quick(int[] array, int l, int pivot){
        if (l >= pivot){
            return;
        }
        int index = partition(array, l, pivot);
        quick(array,l, index -1);
        quick(array,index +1,pivot);
    }

    public static void main(String[] args) {
        int[] array = initArray();
        System.out.println("排序前 :" + Arrays.toString(array));
        quick(array, 0, array.length - 1);
        System.out.println("排序后 :" + Arrays.toString(array));
    }

在这里插入图片描述

③:双边循环

双边循环快排(并不完全等价于hoare霍尔分区方案)

  • 选择最左元素作为基准点元素
  • j指针负责从右向左找比基准点小的元素,ⅰ指针负责从左向右
    找比基准点大的元素,一旦找到二者交换,直至ⅰ,j相交
  • 最后基准点与ⅰ(此时ⅰ与j相等)交换,ⅰ即为分区位置

1. 代码实现

    /**
     * 数据准备初始化一个数组
     */
    public static int[] initArray(){
        Random random = new Random();
        // 创建一个数组,长度在10-20之间
        int len = random.nextInt(10) + 5;
        int[] array = new int[len];
        // 遍历添加数据
        for (int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(100);
        }
        return array;
    }

    /**
     * 选择一个基准点(pivot)进行分区
     * @param array 排序数组
     * @param l 左边界
     * @param r 右边界
     * @return 返回值 = 分区后中间索引值
     */
    public static int  partition(int[] array, int l, int r){
        // 左侧基准点(值)
        int pivotNum = array[l];
        int i = l;
        int j = r;
        while (i < j) {
            // j从右边找比基准点小的值
            while (i < j && array[j] > pivotNum){
                j--;
            }
            // i 从左边找比基准点大的值
            while (i < j && array[i] <= pivotNum){
                i++;
            }
            swap(array, i, j);
        }
        swap(array, i, l);
        return i;
    }

    /**
     * 交换位置
     * @param array 数组
     * @param i 位置1
     * @param j 位置2
     */
    private static void swap(int[] array, int i, int j) {
        int temp = array[j];
        array[j] = array[i];
        array[i] = temp;
    }

    /**
     * 递归排序
     * @param array 排序数组
     * @param l 左边界
     * @param pivot 基准点
     */
    public static void quick(int[] array, int l, int pivot){
        if (l >= pivot){
            return;
        }
        int index = partition(array, l, pivot);
        quick(array,l, index -1);
        quick(array,index +1,pivot);
    }


    public static void main(String[] args) {
        int[] array = initArray();
        System.out.println("排序前 :" + Arrays.toString(array));
        quick(array, 0, array.length - 1);
        System.out.println("排序后 :" + Arrays.toString(array));
    }

2. 注意事项

  • 1.基准点在左边,并且要先 j 后 i (先从右边找在从左边找)

  • 2.while ( i < j && array[ j ] > pivotNum)

// j从右边找比基准点小的值
while (i < j && array[j] > pivotNum){
    j--;
}
  • 3.while (i < j && array[i] <= pivotNum)
// i 从左边找比基准点大的值
while (i < j && array[i] <= pivotNum){
    i++;
}

④:特点


附绿

  • 洛穆托vs霍尔 https:/qastack.cn/cs/11458/quicksort-partitioning-hoare-Vs-lomuto

六、ArrayList扩容规则

可以看看这位博主的:https://www.cnblogs.com/ruoli-0/p/13714389.html

  • ArrayList()会使用长度为零的数组
    • 直接调用无参方法初始容量为0(空数组)
  • ArrayList(int initialCapacity)会使用指定容量的数组
    • 调用有参方法数组容量为传入的容量值
  • public ArrayList(Collection<?extends E>c)会使用c的大小作为数组容量
    • 传入的是一个集合使用的是集合的大小
  • add(0 bject o)首次扩容为10,再次扩容为上次容量的1.5倍
  • addAll(Collection c)没有元素时,扩容为Math.max(10,实际元素个数),有元素时为Math.max(原容量1.5倍,实际元素个数)
    • 如过集合中没有元素 扩容会在(10和实际元素个数)中选择一个大的,有元素时扩容会在(原容量1.5倍和实际元素个数)选择一个大的

ArrayList的特点:

  • 1.ArrayList的底层数据结构是数组,所以查找遍历快,增删慢。

  • 2.ArrayList可随着元素的增长而自动扩容,正常扩容的话,每次扩容到原来的1.5倍。

  • 3.ArrayList的线程是不安全的。

ArrayList的扩容:

扩容可分为两种情况:

第一种情况,当ArrayList的容量为0时,此时添加元素的话,需要扩容,三种构造方法创建的ArrayList在扩容时略有不同:

  • 1.无参构造,创建ArrayList后容量为0,添加第一个元素后,容量变为10,此后若需要扩容,则正常扩容。

  • 2.传容量构造,当参数为0时,创建ArrayList后容量为0,添加第一个元素后,容量为1,此时ArrayList是满的,下次添加元素时需正常扩容。

  • 3.传列表构造,当列表为空时,创建ArrayList后容量为0,添加第一个元素后,容量为1,此时ArrayList是满的,下次添加元素时需正常扩容。

第二种情况,当ArrayList的容量大于0,并且ArrayList是满的时,此时添加元素的话,进行正常扩容,每次扩容到原来的1.5倍。

七、Iterator_FailFast_FailSafe

  • ArrayList是fail-fast的典型代表,遍历的同时不能修改,尽快失败
  • CopyOnWriteArrayList是fail-safe的典型代表,遍历的同时可以修改,原理是读写分离

并发篇-32-ConcurrentHashMap

八、LinkedList

九、HashMap


http://www.kler.cn/a/380057.html

相关文章:

  • 私有化视频平台EasyCVR视频汇聚平台接入RTMP协议推流为何无法播放?
  • LeetCode 第422场个人周赛
  • RabbitMQ的原理和集成使用
  • 面试题:JVM(三)
  • SpringBoot3如何读取配置文件application.properties的属性值
  • 【干货】如何实现安全的登录系统:从用户登录场景分析 Web 网络安全技术
  • C#-数组:一维数组、二维数组、交错数组
  • Linux/Unix awk命令
  • [极客大挑战 2019]BabySQL 1
  • 开源数据库 - mysql - innodb源码阅读 - master线程(一)
  • 24.11.4 OpenCV 图像滤波 卷积核概念 图像平滑处理 图像边缘处理 图像边缘检测 图像形态学操作 学习笔记
  • OpenAI + asyncio 异步调用
  • MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!
  • python NLTK快速入门
  • 计算机网络:网络层 —— 移动 IP 技术
  • 力扣904-水果成篮-两种思路
  • 【Kettle的安装与使用】使用Kettle实现mysql和hive的数据传输(使用Kettle将mysql数据导入hive、将hive数据导入mysql)
  • 思科路由器静态路由配置
  • 讲讲⾼并发的原则?
  • 从 vue 源码看问题 — vue 初始化都做了什么事?
  • CentOS Linux教程(11)--查看文件
  • RK3229 Android9自定义一个按键实现长按短按
  • 鸿蒙next字符串基础:掌握字符串操作与多语言支持
  • 如何选择最适合的消息队列?详解 Kafka、RocketMQ、RabbitMQ 的使用场景
  • Seelen UI 界面介绍与苹果界面类似
  • 易泊车牌识别相机:夜间识别的卓越之选