重启人生计划-乘风破浪

🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳

如果你觉得这个【重启人生计划】对你也有一定的帮助,加入本专栏,开启新的训练计划,漫长成长路,千锤百炼,终飞升巅峰!无水文,不废话,唯有日以继日,终踏顶峰! ✨✨欢迎订阅本专栏✨✨

❤️❤️❤️ 最后,希望我的这篇文章能对你的有所帮助! 愿自己还有你在未来的日子,保持学习,保持进步,保持热爱,奔赴山海! ❤️❤️❤️

序言

大家好,我是最爱吃鱼罐头,距离离职已经过去一个月了,目前进度为9,打算重新找工作倒计时21天,当然这其中也会去投递面试。

人生海海,山山而川,不过尔尔。

今日回顾

前天大概休息了下,昨天貌似有点延续休息的状态去了,我的天啊,这个休息是一点都不能有,一旦沾染了休息的劲,就不行了。。。。

今天重新调整出发,乘风破浪,这两天的算法是有延续的,算法的难度不大,主要知道哈希表的概念,以及对应语言中哈希表的具体体现,这两天一直反复背诵MySQL的内容,具体能知道概念了,视频刷的很慢,赶紧的吧,真的是,先快速刷一轮吧。

两数之和

两数之和

这道题在第一天的时候就已经完成过了,并且我也提供哈希表的实现方式,这里也不多描述了。

快乐数

快乐数 📍

对于这道题可能面临的结果有:

  1. 最终等于1;
  2. 最终陷入循环;
  3. 最终趋近于无穷大。

主要思路有:

  1. 求出当前数字的下一个数字是多少,即求出当前数字的数位分离后的平方和;
  2. 判断是否进入了一个循环,即使用哈希表判断是否重复出现过,并且判断是否已经等于1。

代码实现:

package com.ygt.day8;

import java.util.HashSet;
import java.util.Set;

/**
 * 202. 快乐数
 * https://leetcode.cn/problems/happy-number/description/
 * 编写一个算法来判断一个数 n 是不是快乐数。
 * 「快乐数」 定义为:
 * 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
 * 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
 * 如果这个过程 结果为 1,那么这个数就是快乐数。
 * 如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
 * 输入:n = 19
 * 输出:true
 * 解释:
 * 1^2 + 9^2 = 82
 * 8^2 + 2^2 = 68
 * 6^2 + 8^2 = 100
 * 1^2 + 0^2 + 0^2 = 1
 *
 * @author ygt
 * @since 2024/8/19
 */
public class IsHappy {

    public static void main(String[] args) {
        System.out.println("快乐数:" +new IsHappy().isHappy(19));
    }

    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();

        // 判断当前数字是否等于1,并且是否在哈希表重复出现
        while (n != 1 && !set.contains(n)) {
            // 存储当前数字到集合中
            set.add(n);
            // 求出当前数字的下一个数字
            n = getNum(n);
        }

        // 退出循环后,判断是否等于1,因为可能是哈希表重复值退出循环的
        return n == 1;
    }

    private int getNum(int n) {
        // 进行数位分离后求平方和
        int sum = 0;
        while (n > 0) {
            int num = n % 10;
            n = n / 10;
            sum += num * num;
        }

        return sum;
    }
}

罗马数字转整数

罗马数字转整数 📍

这道题第一天也实现过啦。

整数转罗马数字

整数转罗马数字 📍

这道题简单的实现思路:通过将所有可能性的写入到数组中,通过遍历数组就可以得到最终的字符串。

代码实现:

package com.ygt.day8;

/**
 * 12. 整数转罗马数字
 * https://leetcode.cn/problems/integer-to-roman/description/
 * 七个不同的符号代表罗马数字,其值如下:
 * 符号	值
 * I	1
 * V	5
 * X	10
 * L	50
 * C	100
 * D	500
 * M	1000
 * 罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:
 * 如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
 * 如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I): IV ,9 是 10 (X) 减 1 (I):IX。
 * 仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
 * 只有 10 的次方(I, X, C, M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,
 * 请使用 减法形式。
 * 给定一个整数,将其转换为罗马数字。
 * 输入:num = 3749
 * 输出: "MMMDCCXLIX"
 * 解释:
 * 3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M)
 *  700 = DCC 由于 500 (D) + 100 (C) + 100 (C)
 *   40 = XL 由于 50 (L) 减 10 (X)
 *    9 = IX 由于 10 (X) 减 1 (I)
 * 注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位
 *
 * @author ygt
 * @since 2024/8/19
 */
public class IntToRoman {
    public static void main(String[] args) {
        System.out.println("整数转罗马数字的答案:" + new IntToRoman().intToRoman(3749));
    }

    public String intToRoman(int num) {
        // 创建两个数组来组成罗马字符串和值,其中得包括特殊规则的字符串和数值, 并且索引位置得对应
        int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        String[] strs = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

        // 构建字符串
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < nums.length; i++) {
            int value = nums[i];
            String str = strs[i];
            // 与当前num进行比较
            while (num >= value) {
                // 如果value比当前num小,就对当前num相减
                num -= value;
                sb.append(str);
            }

            if(num <= 0) {
                break;
            }
        }

        return sb.toString();
    }
}

四数相加 II

四数相加 II 📍

这题主要思路:

  1. 这个题目是4个数组,当然可以4层for循环,但是这样效率极其低下;
  2. 那我们可以分为两个两层for循环,我们将第一组求出的和存储在哈希表,另一组分别进行在哈希表中进行比对;
  3. 这样我们可以先求出nums1 + nums2 的和为sum,哈希表储存的键为sum,而值是对应sum出现的次数,我们求另一个组数组的和时,在哈希表中查找是否存在相反的键,即为相加0的情况了。

代码实现:

package com.ygt.day9;

import java.util.HashMap;

/**
 * 454. 四数相加 II
 * https://leetcode.cn/problems/4sum-ii/description/
 * 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
 * 0 <= i, j, k, l < n
 * nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
 * 示例 1:
 * 输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
 * 输出:2
 * 解释:
 * 两个元组如下:
 * 1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
 * 2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
 * @author ygt
 * @since 2024/8/20
 */
public class FourSumCount {
    public static void main(String[] args) {
        int[] nums1 = {-1, -1};
        int[] nums2 = {-1, 1};
        int[] nums3 = {-1, 1};
        int[] nums4 = {1, -1};
        System.out.println(new FourSumCount().fourSumCount(nums1, nums2, nums3, nums4));

    }

    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        // 建立一个哈希表存储第一组的和
        HashMap<Integer, Integer> map = new HashMap<>();

        // 先求出nums1和nums2的和
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++) {
                int sum = nums1[i] + nums2[j];
                map.put(sum, map.getOrDefault(sum, 0) + 1);
            }
        }

        // 最终返回的数量
        int result = 0;

        // 求出nums3和nums4的和
        for (int i = 0; i < nums3.length; i++) {
            for (int j = 0; j < nums4.length; j++) {
                int sum = -(nums3[i] + nums4[j]);
                // 判断相反的sum是否在哈希表中存在
                if(map.containsKey(sum)) {
                    // 这样应该是加上map中出现的次数
                    result += map.get(sum);
                }
            }
        }

        return result;
    }
}

两个数组的交集

两个数组的交集 📍

这道题的思路也是有点简单的:需要建立两个哈希表存储,第一个哈希表存储nums1中的所有的元素,而第二个哈希表是在遍历nums2时,存储与哈希表1出现过的元素。

那可不可以不用第二个哈希表呢,nums2与nums1的重复元素不确定,因此你建立数组时,无法确定其大小长度。

代码实现:

package com.ygt.day9;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * 349. 两个数组的交集
 * https://leetcode.cn/problems/intersection-of-two-arrays/description/
 * 给定两个数组 nums1 和 nums2 ,返回 它们的交集
 *  。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
 * 示例 1:
 * 输入:nums1 = [1,2,2,1], nums2 = [2,2]
 * 输出:[2]
 * @author ygt
 * @since 2024/8/20
 */
public class Intersection {
    public static void main(String[] args) {
        int[] nums1 = {1,2,2,1};
        int[] nums2 = {2,2};
        System.out.println(Arrays.toString(new Intersection().intersection(nums1, nums2)));
    }

    public int[] intersection(int[] nums1, int[] nums2) {
        // 建立两个哈希表
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> set2 = new HashSet<>();

        // 第一个哈希表存储nums1中的所有的元素
        for (int i = 0; i < nums1.length; i++) {
            set1.add(nums1[i]);
        }

        // 第二个哈希表是在遍历nums2时,存储与哈希表1出现过的元素。
        for (int i = 0; i < nums2.length; i++) {
            if(set1.contains(nums2[i])) {
                set2.add(nums2[i]);
            }
        }

        // 最后判断set2元素是否不为空
        if(set2.size() <= 0) {
            return new int[0];
        }

        // 将set2转换为数组返回
        return set2.stream().mapToInt( i -> i).toArray();
    }
}

有效的字母异位词

有效的字母异位词 📍

这道题的思路也很简单:

  1. 建立哈希表,遍历s中的字符串,如果存在相同的,值+1;
  2. 遍历t中的字符串,判断是否在哈希表中存在,并且相减后是否小于0,如果是的,直接返回false。

代码实现:

package com.ygt.day9;

import java.util.HashMap;
import java.util.Map;

/**
 * 242. 有效的字母异位词
 * https://leetcode.cn/problems/valid-anagram/description/
 * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
 * 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
 * 输入: s = "anagram", t = "nagaram"
 * 输出: true
 * @author ygt
 * @since 2024/8/20
 */
public class IsAnagram {
    public static void main(String[] args) {
        new IsAnagram().isAnagram("anagram", "nagaram");
    }

    public boolean isAnagram(String s, String t) {
        if(s.length() != t.length()){
            return false;
        }

        Map<Character, Integer> map = new HashMap<>(26);
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            map.put(ch, map.getOrDefault(ch, 0) + 1);
        }

        for (int i = 0; i < t.length(); i++) {
            char ch = t.charAt(i);
            map.put(ch, map.getOrDefault(ch, 0) - 1);
            if(map.get(ch) < 0) {
                return false;
            }
        }

        return true;
    }
}

赎金信

赎金信 📍

这道题的思路也很简单:

  1. 建立哈希表,遍历magazine中的字符串,如果存在相同的,值+1;
  2. 遍历ransomNote中的字符串,判断是否在哈希表中存在,并且相减后是否小于0,如果是的,直接返回false。

代码实现:

package com.ygt.day9;

import java.util.HashMap;

/**
 * 383. 赎金信
 * https://leetcode.cn/problems/ransom-note/description/
 * 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
 * 如果可以,返回 true ;否则返回 false 。
 * magazine 中的每个字符只能在 ransomNote 中使用一次。
 * 示例 3:
 * 输入:ransomNote = "aa", magazine = "aab"
 * 输出:true
 * @author ygt
 * @since 2024/8/20
 */
public class CanConstruct {
    public static void main(String[] args) {
        System.out.println(new CanConstruct().canConstruct("aa", "aab"));
    }

    public boolean canConstruct(String ransomNote, String magazine) {
        // 1. 建立哈希表
        HashMap<Character, Integer> map = new HashMap<>();

        // 2. 遍历magazine中的字符串,如果存在相同的,值+1;
        for (int i = 0; i < magazine.length(); i++) {
            char ch = magazine.charAt(i);
            map.put(ch, map.getOrDefault(ch, 0) + 1);
        }

        // 3. 遍历ransomNote中的字符串
        for (int i = 0; i < ransomNote.length(); i++) {
            char ch = ransomNote.charAt(i);
            map.put(ch, map.getOrDefault(ch, 0) - 1);
            // 判断是否小于0
            if(map.get(ch) < 0) {
                return false;
            }
        }
        return true;
    }
}

存在重复元素

存在重复元素📍

这道题的思路就很简单,遍历存储到哈希表中,如果存在了相同的值,返回true即可。

代码实现:

package com.ygt.day9;

import java.util.HashMap;

/**
 * 217. 存在重复元素
 * https://leetcode.cn/problems/contains-duplicate/description/
 * 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
 * 示例 1:
 * 输入:nums = [1,2,3,1]
 * 输出:true
 * @author ygt
 * @since 2024/8/20
 */
public class ContainsDuplicate {
    public static void main(String[] args) {
        int[] nums = {1,2,3,1};
        System.out.println(new ContainsDuplicate().containsDuplicate(nums));
    }

    public boolean containsDuplicate(int[] nums) {
        // 建立哈希表存储
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            // 只要存在相同的,就直接返回true
            if(map.containsKey(nums[i])) {
                return true;
            }
            map.put(nums[i], i);
        }

        return false;
    }
}

小结算法

今天的算法是有点简单,并且是有几道题都是已经做过了的。

明日内容

基础面试题

下面的题目的答案是基于自己的理解和思考去编写出来的,也希望大家如果看到了,可以根据自己的理解去转换为自己的答案。

当然很多思考也有参考别人的成分,但是自己能讲述出来就是最棒的。

这里有一篇阿里的多线程面试题

1. 进程与线程的区别?为什么要用多线程?

  • 进程:进程是程序的一次执行过程,是系统运行程序的基本单位。

  • 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。

  • 区别

    1. 一个程序至少有一个进程,一个进程至少有一个线程。
    2. 一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务即多个线程。
    3. 每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
  • 为什么要用多线程:

    • 发挥多核CPU的优势,采用多线程的方式去同时完成几件事情而不互相干扰。
    • 能够有效的防止阻塞,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
    • 提高程序的效率。

2. 什么是上下文切换?

上下文切换一般发生在多线程情况下,因为一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。而在多核cpu下,多线程是并行工作的,如果线程数多,单个核又会并发的调度线程,运行时就会让一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于上下文切换。

对于我们Java程序线程来说,一旦一个线程抢占到CPU资源的使用权后,另一个线程需要保存当前的一个状态,以便下次抢占成功后可以回到当前状态,JVM中有块内存地址叫程序计数器,用于记录保存线程执行到哪一行代码,它是每个线程独有的。执行任务从保存到再次加载的过程就是上下文切换。

实际上,上下文切换也是对系统意味着来说会消耗大量的CPU时间,消耗大量资源。

3. 创建线程的方式

创建线程有以下方式:

  • 继承Thread类,重载它的run方法,并通过start方法来启动线程;

  • 实现 Runnalbe接口,重载 Runnalbe接口中的run方法实现 ,以实例对象为参数创建Thread对象,最后调用线程的start方法;

  • 实现Callable接口方式,重写Callable接口中的call方法,并且这个call方法可以有返回值,以实例对象为参数创建FutureTask对象,这个FutureTask实例对象为参数创建Thread对象,最后调用线程的start方法;

需要注意三者的区别:

  • Thread是继承,而Runnalbe、Callable是实现。对于继承来说,只能单继承,而接口可以多实现。如果继承了 Thread类就无法再继承其他类了。
  • 三者都是最后采用Thread.start()去启动线程,而不是调用run方法,或者call方法的。
  • Runnable接口 run 方法无返回值;Callable接口 call 方法有返回值。
  • Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息
  • 使用实现 Runnable接口的方式创建的线程可以处理同一资源,而实现资源的共享,还可以继承其他类。

4. 线程的状态

NEW, 新建时,进入新建状态

RUNNABLE, 调用了start,进入可运行状态
 
BLOCKED,  竞争锁失败,进入阻塞状态

WAITING, 调用了wait、join、LockSupport.park(),就会使线程进去等待状态,需要由别的线程唤醒

TIMED_WAITING,调用了sleep、wait(time)join(time)LockSupport.parkNanos(time),这些都会进入计时等待的状态

TERMINATED; 线程执行完毕,进入终止状态。

5. start和run方法的区别

JVM执行start方法,会先创建一个线程,由创建出来的新线程去执行thread的run方法,这才起到多线程的效果。

start()和run()的主要区别如下:

  • start方法可以启动一个新线程,run方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程;
  • start方法实现了多线程,而run方法没有实现多线程;
  • start不能被重复调用,而run方法可以;
  • start方法中的run代码可以不执行完,就继续执行下面的代码,也就是说进行了线程切换。然而,如果直接调用run方法,就必须等待其代码全部执行完才能继续执行下面的代码。

6.sleep和wait的区别

sleep和wait方法他们都是可以暂停当前线程的执行,进入一个阻塞状态。

  • sleep:

    我们可以指定睡眠时间,即让程序暂停指定时间运行,时间到了会继续执行代码,如果时间未到我们想要换醒需要调用interrupt 方法来随时唤醒即可。而调用interrupt 会使得sleep()方法抛出InterruptedException 异常,当sleep()方法抛出异常我们就中断了sleep的方法,从而让程序继续运行下去。

  • wait:

    调用该方法,可以导致线程进入等待阻塞状态,会一直等待直到它被其他线程通过notify或者notifyAll方法唤醒。或者也可以使用wait(long timeout)表示时间到了自动执行,类似于sleep(long millis)。

    notify():该方法会随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。

    notifyAll():该方法会唤醒所有的wait对象。

两者的区别:

  • 两者所属的类不同:sleep是 Thread线程类的静态方法;而wait是 Object类的方法。

  • 两者是否是否锁呢:sleep不释放锁;wait释放锁。

  • 两者所使用的场景:sleep可以在任何需要的场景下调用;而wait必须使用在同步代码块或者同步方法中。

  • 两者不同唤醒机制:sleep方法执行睡眠时间完成后,线程会自动苏醒;而wait方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify或者 notifyAll方法,或者可以使用wait(long timeout)超时后线程会自动苏醒。

7. 死锁

死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

产生死锁的必要条件有:

两个或两个以上的线程在执行过程中,因争夺资源而造成了互相等待的状态。

  1. 互斥条件:线程对于所分配到的资源具有排它性,即一个资源只能被一个线程占用,直到被该线程(进程)释放。
  2. 请求与保持条件:一个线程因请求被占用资源而发生阻塞时,对已获得的资源保持不放,即一个线程在已经有一个资源的资格后,又提出了新的资源请求。
  3. 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  4. 循环等待条件:当发生死锁时,所等待的线程必定会形成一个资源的环形链(类似于死循环),造成永久阻塞。

算法

明日全新的题目类型,字符串,我想大家对字符串都很了解吧,那相关题目我们来玩一玩吧。

🌸 完结

最后,相关算法的代码也上传到gitee或者github上了。

乘风破浪会有时 直挂云帆济沧海

希望从明天开始,一起加油努力吧,成就更好的自己。

🥂 虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!✨✨✨

💟 感谢各位看到这里!愿你韶华不负,青春无悔!让我们一起加油吧! 🌼🌼🌼

💖 学到这里,今天的世界打烊了,晚安!🌙🌙🌙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最爱吃鱼罐头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值