面试 设计模式和算法 通关秘籍

1 黑铁

1.1 如何使用单例模式的饿汉式和饱汉式?


```Java
// 饿汉式
class SingleTon {
    private static final SingleTon instance = SingleTon();
    private SingleTon();

    public SingleTon getInstance() {
        return instance;
    }
}
// 懒汉式
class SingleTon2 {
    private static volatile SingleTon2 instance;

    private SingleTon2();
    
    public SingleTon2 getInstance() {
        if (instance == null) {
            synchronized(SingleTon2.class) {
                if (instance == null) {
                    return new SingleTon2();
                }
            }
        }
        return instance;
    }

}

1.2 单例模式的双重检验锁和静态内部类和枚举?

1.双重检验锁单例模式:避免了每次都加锁的性能问题。

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
        // 私有构造函数
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2.静态内部类单例模式:延迟加载,且线程安全。

public class Singleton {
    private Singleton() {
        // 私有构造函数
    }

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

3.枚举单例模式

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 实例方法
    }
}

2 青铜

2.1 常见的设计模式有哪些?

1.Builder(建造者)模式:将一个复杂对象的构建过程分解成多个简单对象的构建过程,并将它们逐步构建成一个复杂对象。在 Android 中,常用于创建复杂的对象,例如 AlertDialog.Builder。

2.Factory Method(工厂方法)模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。在 Android 中,常用于创建一系列相关对象,例如不同类型的 Dialog。

3.Adapter(适配器)模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而无法在一起工作的类可以一起工作。在 Android 中,常用于将不同类型的数据适配到 ListView、4.RecyclerView 等控件中显示。

4.Observer(观察者)模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。在 Android 中,常用于实现事件监听器和广播机制。

5.Singleton(单例)模式:保证一个类仅有一个实例,并提供一个全局访问点。在 Android 中,常用于管理全局的状态信息,例如应用程序的配置信息。

6.Decorator(装饰)模式:动态地给一个对象添加一些额外的职责,而不需要修改类的代码。在 Android 中,常用于对 View 或者 Drawable 进行装饰,例如使用 Drawable 和 ColorFilter 实现带圆角或者阴影的图片。

7.Facade(外观)模式:为子系统中的一组接口提供一个统一的接口,以便更方便地使用子系统。在 Android 中,常用于简化复杂的库或者系统的使用,例如在 MediaStore 中封装各种类型的媒体查询接口。

8.Template Method(模板方法)模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。在 Android 中,常用于封装通用的流程或者算法,例如 AsyncTask 中的 doInBackground 方法。

9.Strategy(策略)模式:定义一系列的算法,把它们一个个封装起来,并使它们可以相互替换。在 Android 中,常用于实现不同的业务逻辑或者算法,例如实现不同的排序方式。

10.Iterator(迭代器)模式:提供一种方法来访问聚合对象中的各个元素,而又不需要暴露该对象的内部表示。在 Android 中,常用于对数据结构进行遍历,例如使用 Cursor 对查询结果进行遍历。

2.2 设计模式分类

1.创建型模式:创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。
2.结构型模式:结构型模式(Structural Pattern)描述如何将类或者对 象结合在一起形成更大的结构,就像搭积木,可以通过 简单积木的组合形成复杂的、功能更为强大的结构。
3.行为型模式:行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。

2.3 创建型模式的设计模式有哪几种?

简单工厂模式(Simple Factory)
工厂方法模式(Factory Method)
抽象工厂模式(Abstract Factory)
建造者模式(Builder)
原型模式(Prototype)
单例模式(Singleton)

2.4 结构型模式的设计模式有哪几种?

适配器模式(Adapter)
桥接模式(Bridge)
组合模式(Composite)
装饰模式(Decorator)
外观模式(Facade)
享元模式(Flyweight)
代理模式(Proxy)

2.5 行为型模式的设计模式有哪几种?

职责链模式(Chain of Responsibility)
命令模式(Command)
解释器模式(Interpreter)
迭代器模式(Iterator)
中介者模式(Mediator)
备忘录模式(Memento)
观察者模式(Observer)
状态模式(State)
策略模式(Strategy)
模板方法模式(Template Method)
访问者模式(Visitor)

3 白银

常见的排序算法

3.1 冒泡排序

基本思路:比较相邻的元素。如果第一个比第二个大,就交换他们两个。

public static void bubbleSort(int[] numbers) {
    int temp = 0;
    int size = numbers.length;
    boolean flag = true;
    for (int i = 0; i < size - 1&&flag; i++) {
        flag = false;
        for (int j = 0; j < size - 1 - i; j++) {
            if (numbers[j] > numbers[j + 1]) // 交换两数位置
            {
                temp = numbers[j];
                numbers[j] = numbers[j + 1];
                numbers[j + 1] = temp;
                flag = true;
            }
        }
    }
}

时间复杂度O(n^2)

3.2 选择排序

选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。

public static void selectSort(int[] numbers) {
    int size = numbers.length; // 数组长度
    int temp = 0; // 中间变量
    for (int i = 0; i < size-1; i++) {
        int k = i; // 待确定的位置
        // 选择出应该在第i个位置的数
        for (int j = size - 1; j > i; j--) {
            if (numbers[j] < numbers[k]) {
                k = j;
            }
        }
        // 交换两个数
        temp = numbers[i];
        numbers[i] = numbers[k];
        numbers[k] = temp;
    }
}

时间复杂度O(n*n) 性能上优于冒泡排序 交换次数少

3.3 插入排序算法?

每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。

public static void insertSort(int[] numbers) {
    int size = numbers.length;
    int temp = 0;
    int j = 0;
    for (int i = 1; i < size; i++) {
        temp = numbers[i];
        // 假如temp比前面的值小,则将前面的值后移
        for (j = i; j > 0 && temp < numbers[j - 1]; j--) {
            numbers[j] = numbers[j - 1];
        }
        numbers[j] = temp;
    }
}

时间复杂度
O(n*n) 性能上优于冒泡排序和选择排序

3.4 快速排序算法

通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。

/**
 * 快速排序
 * 
 * @param numbers
 *            带排序数组
 */
public static void quick(int[] numbers) {
    if (numbers.length > 0) // 查看数组是否为空
    {
        quickSort(numbers, 0, numbers.length - 1);
    }
}
/**
 * 
 * @param numbers
 *            带排序数组
 * @param low
 *            开始位置
 * @param high
 *            结束位置
 */
public static void quickSort(int[] numbers, int low, int high) {
    if (low >= high) {
        return;
    }
    int middle = getMiddle(numbers, low, high); // 将numbers数组进行一分为二
    quickSort(numbers, low, middle - 1); // 对低字段表进行递归排序
    quickSort(numbers, middle + 1, high); // 对高字段表进行递归排序
}
/**
 * 查找出中轴(默认是最低位low)的在numbers数组排序后所在位置
 * 
 * @param numbers
 *            带查找数组
 * @param low
 *            开始位置
 * @param high
 *            结束位置
 * @return 中轴所在位置
 */
public static int getMiddle(int[] numbers, int low, int high) {
    int temp = numbers[low]; // 数组的第一个作为中轴
    while (low < high) {
        while (low < high && numbers[high] > temp) {
            high--;
        }
        numbers[low] = numbers[high];// 比中轴小的记录移到低端
        while (low < high && numbers[low] < temp) {
            low++;
        }
        numbers[high] = numbers[low]; // 比中轴大的记录移到高端
    }
    numbers[low] = temp; // 中轴记录到尾
    return low; // 返回中轴的位置
}

时间复杂度O(nlogn)
快速排序在序列中元素很少时,效率将比较低,不如插入排序,因此一般在序列中元素很少时使用插入排序,这样可以提高整体效率。

3.5 希尔排序

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

/**
 * 希尔排序的原理:根据需求,如果你想要结果从小到大排列,它会首先将数组进行分组,然后将较小值移到前面,较大值
 * 移到后面,最后将整个数组进行插入排序,这样比起一开始就用插入排序减少了数据交换和移动的次数,
 * 可以说希尔排序是加强 版的插入排序 拿数组5, 2,8, 9, 1, 3,4来说,数组长度为7,当increment为3时,数组分为两个序列
 * 5,2,8和9,1,3,4,第一次排序,9和5比较,1和2比较,3和8比较,4和比其下标值小increment的数组值相比较
 * 此例子是按照从小到大排列,所以小的会排在前面,第一次排序后数组为5, 1, 3, 4, 2, 8,9
 * 第一次后increment的值变为3/2=1,此时对数组进行插入排序, 实现数组从大到小排
 */
public static void shellSort(int[] data) {
    int j = 0;
    int temp = 0;
    // 每次将步长缩短为原来的一半
    for (int increment = data.length / 2; increment > 0; increment /= 2) {
        for (int i = increment; i < data.length; i++) {
            temp = data[i];
            for (j = i; j >= increment; j -= increment) {
                if (temp < data[j - increment])// 从小到大排
                {
                    data[j] = data[j - increment];
                } else {
                    break;
                }
            }
            data[j] = temp;
        }
    }

时间复杂度O(n^1.5)

3.6 堆排序算法

堆排序是一种树形选择排序,是对直接选择排序的有效改进。
**思想:**初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

public static void heapSort(int[] a){
    int arrayLength = a.length;
    // 循环建堆
    for (int i = 0; i < arrayLength - 1; i++) {
        // 建堆
        buildMaxHeap(a, arrayLength - 1 - i);
        // 交换堆顶和最后一个元素
        swap(a, 0, arrayLength - 1 - i);
        System.out.println(Arrays.toString(a));
    }
}
// 对data数组从0到lastIndex建大顶堆
public static void buildMaxHeap(int[] data, int lastIndex) {
    // 从lastIndex处节点(最后一个节点)的父节点开始
    for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
        // k保存正在判断的节点
        int k = i;
        // 如果当前k节点的子节点存在
        while (k * 2 + 1 <= lastIndex) {
            // k节点的左子节点的索引
            int biggerIndex = 2 * k + 1;
            // 如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
            if (biggerIndex < lastIndex) {
                // 若果右子节点的值较大
                if (data[biggerIndex] < data[biggerIndex + 1]) {
                    // biggerIndex总是记录较大子节点的索引
                    biggerIndex++;
                }
            }
            // 如果k节点的值小于其较大的子节点的值
            if (data[k] < data[biggerIndex]) {
                // 交换他们
                swap(data, k, biggerIndex);
                // 将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
                k = biggerIndex;
            } else {
                break;
            }
        }
    }
}
// 交换
private static void swap(int[] data, int i, int j) {
    int tmp = data[i];
    data[i] = data[j];
    data[j] = tmp;
}

时间复杂度O(nlogn)不适合待排序序列较少的情况

3.7 归并排序

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

/**
 * 归并排序
 * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
 * 时间复杂度为O(nlogn)
 * 稳定排序方式
 * @param nums 待排序数组
 * @return 输出有序数组
 */
public static int[] sort(int[] nums, int low, int high) {
    int mid = (low + high) / 2;
    if (low < high) {
        // 左边
        sort(nums, low, mid);
        // 右边
        sort(nums, mid + 1, high);
        // 左右归并
        merge(nums, low, mid, high);
    }
    return nums;
}
/**
 * 将数组中low到high位置的数进行排序
 * @param nums 待排序数组
 * @param low 待排的开始位置
 * @param mid 待排中间位置
 * @param high 待排结束位置
 */
public static void merge(int[] nums, int low, int mid, int high) {
    int[] temp = new int[high - low + 1];
    int i = low;// 左指针
    int j = mid + 1;// 右指针
    int k = 0;
    // 把较小的数先移到新数组中
    while (i <= mid && j <= high) {
        if (nums[i] < nums[j]) {
            temp[k++] = nums[i++];
        } else {
            temp[k++] = nums[j++];
        }
    }
    // 把左边剩余的数移入数组
    while (i <= mid) {
        temp[k++] = nums[i++];
    }
    // 把右边边剩余的数移入数组
    while (j <= high) {
        temp[k++] = nums[j++];
    }
    // 把新数组中的数覆盖nums数组
    for (int k2 = 0; k2 < temp.length; k2++) {
        nums[k2 + low] = temp[k2];
    }
}

间复杂度O(nlogn)

4 黄金

4.1 求二叉树第n层节点数?

public static int getNodesAtLevel(TreeNode root, int level) {
    if (root == null) {
        return 0;
    }
    if (level == 1) {
        return 1;
    }
    int leftNodes = getNodesAtLevel(root.left, level - 1);
    int rightNodes = getNodesAtLevel(root.right, level - 1);
    return leftNodes + rightNodes;
}

4.2 二叉树深度算法?

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public int maxDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int leftDepth = maxDepth(root.left);
    int rightDepth = maxDepth(root.right);
    return Math.max(leftDepth, rightDepth) + 1;
}

4.3 负数全部移动左侧(快速排序?)?

有一个整形数组,包含正数和负数,然后要求把数组内的所有负数移至正数的左边,且保证相对位置不变,要求时间复杂度为O𝑛
, 空间复杂度为O1
。例如,{10, -2, 5, 8, -4, 2, -3, 7, 12, -88, -23, 35}变化后是{-2, -4,-3, -88, -23,5, 8 ,10, 2, 7, 12, 35}。

 public void setParted(int[] a){  
    int temp=0;  
    int border=-1;  

    for(int i=0;i<a.length;i++){  
        if(a[i]<0){  
            temp=a[i];  
            a[i]=a[border+1];  
            a[border+1]=temp;  
            border++;  
        }  
    }  
    for(int j=0;j<a.length;j++){  
        System.out.println(a[j]);  
    }  
}

有点像快速排序二分法,选择的基准元素为0而已。遍历整个列表,每次将小于0的移动到依次递增的左侧。

4.4 两个有序链表合并?

class ListNode {
    int val;
    ListNode next;
    ListNode(int val) {
        this.val = val;
    }
}

public class MergeTwoSortedLists {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                current.next = l1;
                l1 = l1.next;
            } else {
                current.next = l2;
                l2 = l2.next;
            }
            current = current.next;
        }
        if (l1 != null) {
            current.next = l1;
        } else {
            current.next = l2;
        }
        return dummy.next;
    }
}

4.5 用Java实现:二叉树输出第 k 层节点元素?

实现二叉树输出第 k 层节点元素的Java代码,可以通过遍历二叉树并记录每个节点所在的层数来实现。以下是一个使用递归实现的例子:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public static void printKthLevel(TreeNode root, int k) {
    if (root == null) {
        return;
    }
    if (k == 1) {
        System.out.print(root.val + " ");
    } else {
        printKthLevel(root.left, k - 1);
        printKthLevel(root.right, k - 1);
    }
}

// 该方法接受一个二叉树的根节点和一个整数 k 作为输入,输出该二叉树中第 k 层的所有节点元素。

// 我们首先检查根节点是否为空,如果为空,直接返回。如果 k 等于 1,输出当前节点的值。否则,递归遍历左子树和右子树,并将 k 的值减一,直到 k 等于 1 时输出当前节点的值。这样就可以输出指定层数的所有节点元素。

// 调用如下:
public static void main(String[] args) {
    TreeNode root = new TreeNode(1);
    root.left = new TreeNode(2);
    root.right = new TreeNode(3);
    root.left.left = new TreeNode(4);
    root.left.right = new TreeNode(5);
    root.right.left = new TreeNode(6);
    root.right.right = new TreeNode(7);

    int k = 3;
    System.out.print("The elements in level " + k + " are: ");
    printKthLevel(root, k);
}

4.6 Java实现两个有序的链表的合并?

可以使用递归来实现两个有序链表的合并,具体步骤如下:
1.如果两个链表都为空,则返回空。
2.如果其中一个链表为空,则返回另一个链表。
3.如果两个链表都不为空,比较它们的头节点,将较小的头节点作为合并后的头节点。
4.对于剩下的链表部分,递归地调用步骤3,将结果连接到合并后的头节点后面。
5.返回合并后的链表头节点。

public class ListNode {
    int val;
    ListNode next;
    ListNode(int val) {
        this.val = val;
    }
}

public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

在这个代码中,我们首先判断两个链表是否为空,如果其中一个为空,则直接返回另一个链表。接着,我们比较两个链表的头节点,将较小的头节点作为合并后的头节点,并递归地将剩下的链表部分合并到后面。最后,我们返回合并后的链表头节点。

ListNode l1 = new ListNode(1);
l1.next = new ListNode(2);
l1.next.next = new ListNode(4);

ListNode l2 = new ListNode(1);
l2.next = new ListNode(3);
l2.next.next = new ListNode(4);

Solution solution = new Solution();
ListNode mergedList = solution.mergeTwoLists(l1, l2);

while (mergedList != null) {
    System.out.print(mergedList.val + " ");
    mergedList = mergedList.next;
}

// 输出:1 1 2 3 4 4 

4.7 二叉树层序遍历,奇数层逆序遍历节点,偶数层正序遍历?

import java.util.*;

public class Solution {
    public List<Integer> levelOrderTraversal(TreeNode root) {
        if (root == null) {
            return Collections.emptyList();
        }

        Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>();
        List<List<Integer>> levelNodes = new ArrayList<>();
        queue.add(new Pair<>(root, 0));
        while (!queue.isEmpty()) {
            Pair<TreeNode, Integer> pair = queue.poll();
            TreeNode node = pair.getKey();
            int depth = pair.getValue();
            if (depth >= levelNodes.size()) {
                levelNodes.add(new ArrayList<>());
            }
            levelNodes.get(depth).add(node.val);
            if (node.left != null) {
                queue.add(new Pair<>(node.left, depth + 1));
            }
            if (node.right != null) {
                queue.add(new Pair<>(node.right, depth + 1));
            }
        }

        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < levelNodes.size(); i++) {
            List<Integer> nodes = levelNodes.get(i);
            if (i % 2 == 0) {
                result.addAll(nodes);
            } else {
                Collections.reverse(nodes);
                result.addAll(nodes);
            }
        }

        return result;
    }
}

5 铂金

5.1 求无序数组中的中位数?(快速选择算法)

import java.util.Arrays;

public class MedianOfUnsortedArray {

    public static double findMedian(int[] nums) {
        int n = nums.length;
        int k = n / 2;

        int left = 0;
        int right = n - 1;

        while (left <= right) {
            int pivotIndex = partition(nums, left, right);
            if (pivotIndex == k) {
                break;
            } else if (pivotIndex < k) {
                left = pivotIndex + 1;
            } else {
                right = pivotIndex - 1;
            }
        }

        if (n % 2 == 0) {
            return (nums[k] + nums[k-1]) / 2.0;
        } else {
            return nums[k];
        }
    }

    private static int partition(int[] nums, int left, int right) {
        int pivot = nums[right];
        int i = left;
        for (int j = left; j < right; j++) {
            if (nums[j] < pivot) {
                swap(nums, i, j);
                i++;
            }
        }
        swap(nums, i, right);
        return i;
    }

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

    public static void main(String[] args) {
        int[] nums = {5, 2, 4, 1, 3};
        System.out.println(findMedian(nums)); // 3.0
    }
}

6 钻石

6.1 手写HashMap

public class MyHashMap<K, V> {
    private static final int DEFAULT_CAPACITY = 16;
    private Node<K, V>[] buckets;
    private int size = 0;

    public MyHashMap() {
        this(DEFAULT_CAPACITY);
    }

    public MyHashMap(int capacity) {
        buckets = new Node[capacity];
    }

    public void put(K key, V value) {
        int bucketIndex = getBucketIndex(key);
        Node<K, V> currentNode = buckets[bucketIndex];

        while (currentNode != null) {
            if (currentNode.key.equals(key)) {
                currentNode.value = value;
                return;
            }
            currentNode = currentNode.next;
        }

        Node<K, V> newNode = new Node<>(key, value);
        newNode.next = buckets[bucketIndex];
        buckets[bucketIndex] = newNode;
        size++;
    }

    public V get(K key) {
        int bucketIndex = getBucketIndex(key);
        Node<K, V> currentNode = buckets[bucketIndex];

        while (currentNode != null) {
            if (currentNode.key.equals(key)) {
                return currentNode.value;
            }
            currentNode = currentNode.next;
        }

        return null;
    }

    public void remove(K key) {
        int bucketIndex = getBucketIndex(key);
        Node<K, V> currentNode = buckets[bucketIndex];
        Node<K, V> prevNode = null;

        while (currentNode != null) {
            if (currentNode.key.equals(key)) {
                if (prevNode == null) {
                    buckets[bucketIndex] = currentNode.next;
                } else {
                    prevNode.next = currentNode.next;
                }
                size--;
                return;
            }
            prevNode = currentNode;
            currentNode = currentNode.next;
        }
    }

    public boolean containsKey(K key) {
        int bucketIndex = getBucketIndex(key);
        Node<K, V> currentNode = buckets[bucketIndex];

        while (currentNode != null) {
            if (currentNode.key.equals(key)) {
                return true;
            }
            currentNode = currentNode.next;
        }

        return false;
    }

    public int size() {
        return size;
    }

    private int getBucketIndex(K key) {
        int hashCode = key.hashCode();
        return hashCode % buckets.length;
    }

    private static class Node<K, V> {
        private final K key;
        private V value;
        private Node<K, V> next;

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }
}

7 大师

7.1 华为od-字符串最后一个单词的长度?

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        String str = in.nextLine();
        int length = lengthOfLastWord(str);
        System.out.println(length);
    }

    // 计算最后一个单词的长度
    public static int lengthOfLastWord(String s) {
        // 先去掉字符末尾的空格
        s = s.trim();

        int end = s.length() - 1;
        int start = end;
        // 依次从后往前遍历
        while (start >= 0 && s.charAt(start) != ' ') {
            start--;
        }

        return end - start;
    }
}

7.2 华为od-计算字符串出现次数?

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String str = in.nextLine().toLowerCase();
        char c = in.nextLine().toLowerCase().charAt(0);

        int count = displayTimes(str, c);
        System.out.println(count);
    }

    // 字符显示个数
    public static int displayTimes(String s, char c) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == c) {
                count++;
            }
        }
        return count;
    }
}

7.3 华为od-明明的随机数?

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        // 获取输入数据
        int n = in.nextInt();
        int[] nums = new int[501];
        
        for (int i = 0; i < n; i++) {
            int input = in.nextInt();
            nums[input] = 1;
        }

        // 结果输出
        for (int i = 0; i <= 500; i++) {
            if (nums[i] == 1) {
                System.out.println(i + " ");
            }
        }

    }
}

7.4

8 宗师

9 王者


   转载规则


《面试 设计模式和算法 通关秘籍》 Jason 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
面试 操作系统和网络 通关秘籍 面试 操作系统和网络 通关秘籍
1 黑铁 网络相关常识。 1.1 简单介绍下Https的原理? HTTPS是HTTP协议的加密版本,它使用SSL/TLS协议来保护Web通信的安全性。HTTPS基于公开密钥加密和数字证书认证技术,它通过在通信过程中使用SSL&#
2023-01-08
下一篇 
面试 Java 通关秘籍 面试 Java 通关秘籍
1 黑铁 对象,集合,泛型,反射的理解。 1.1 什么是多态? 面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采
2023-01-06
  目录