每周算法(week 1)【leetcode1~10】

前言

        今天开始刷面试算法题,虽然之前在蓝桥杯、程序设计天梯赛中拿过两个省一和一个国三,但是基本靠的都是我对 Java 语言的熟悉,至于算法我只会基本的双指针、快慢指针、差分数组等,最擅长的其实还是暴力。但是自认为应付面试还是远远不够的,所以今天开始是该好好修炼一下算法了。

leetcode 编号完成时间复习时间
1. 两数之和2024-06-30
2. 两数相加2024-06-30
3. 无重复字符的最长子串2024-06-30
4. 寻找两个正序数组的中位数2024-07-01
5. 最长回文串2024-07-01
6. 
7.
8. 
9. 回文数2024-07-02
10. 

1、两数之和(哈希)

1.1、暴力法 

  • 思路💡:双重遍历,确保数组中每两个数字都进行过一次求和,直到找到 nums[i] + nums[j] = target 的两个目标索引
  • 时间复杂度🕖: O(n2)
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        for(int i=0;i<nums.length-1;i++){
            for(int j=i+1;j<nums.length;j++){
                if(nums[i]+nums[j]==target){
                    res[0]=i;
                    res[1]=j;
                    return res;
                }
            }
        }
        return nums;
    }
}

1.2、哈希表

  • 思路💡:使用一个哈希表来存储遍历过的元素,每次插入元素前判断是否存在 target - nums[i]
  • 时间复杂度🕖:因为哈希表的插入和查询操作的复杂度是 O(1),所以总的时间复杂度是 O(n)
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(target-nums[i])){
                res[0] = map.get(target-nums[i]);
                res[1] = i;
                return res;
            }else{
                map.put(nums[i],i);
            }
        }
        return nums;
    }
}

2、两数相加(模拟)

 2.1、模拟算法

  • 思路💡:模拟普通列式计算的过程,用变量 t 代表当前位的计算和,则 t%10 为当前位的结果,t/10 作为下一轮计算的加数
  • 时间复杂度🕖:O(n)
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1); // 首节点
        ListNode cur = dummy; // 当前节点(负责穿针引线,将每位的结果连起来)
        int t = 0; // 每位的计算和
        while(l1 !=null || l2 !=null || t!=0){
            if(l1!=null){
                t += l1.val;
                l1 = l1.next;
            }
            if(l2!=null){
                t += l2.val;
                l2 = l2.next;
            }
            cur.next = new ListNode(t % 10);
            t /= 10;
            cur = cur.next;
        }
        return dummy.next;
    } 
}

3、无重复字符的最长子串(双指针——前后指针)

3.1、暴力法

  • 思路💡:双重循环,遍历每一种没有重复字符的字符串组合
  • 时间复杂度🕖:O(n2)
class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length()==0 || s.length()==1) return s.length();
        char[] arr = s.toCharArray();
        int max = Integer.MIN_VALUE;
        for(int i=0;i<arr.length-1;i++){
            Map<Character,Integer> map = new HashMap<>();
            map.put(arr[i],1);
            for(int j=i+1;j<arr.length;j++){
                if(map.containsKey(arr[j])) break;
                else map.put(arr[j],1);
            }
            max = Math.max(max,map.size());
        }
        return max;
    }
}

3.2、前后指针 

  • 思路💡:指针 j 负责在前面探索,当哈希表中存在索引 j 的元素时,指针 i 开始移动,直到区间内没有重复元素
  • 时间复杂度🕖:由于 i,j 均最多增加n次,且哈希表的插入和更新操作的复杂度都是 O(1)
    ,因此,总时间复杂度 O(n)
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] arr = s.toCharArray();
        Map<Character,Integer> map = new HashMap<>();
        int res = 0;
        for(int i=0,j=0;j<arr.length;j++){
            map.put(arr[j],map.getOrDefault(arr[j],0)+1);
            while(map.get(arr[j]) > 1){
                map.put(arr[i],map.get(arr[i++])-1);
            }
            res = Math.max(res,j - i + 1);
        }
        return res;
    }
}

4、寻找两个正序数组的中位数(归并排序)

  • 思路💡:使用归并算法将两个数组合并
  • 时间复杂度🕖:O( n + m )n 和 m 分别是两个数组的长度
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int[] nums = new int[m+n];

        // 如果nums1为空,则直接返回nums2的中位数
        if (m == 0) {
            if (n % 2 == 0) {
                return (nums2[n / 2 - 1] + nums2[n / 2]) / 2.0;
            } else {
                return nums2[n / 2];
            }
        }
        // 如果nums2为空,则直接返回nums1的中位数
        if (n == 0) {
            if (m % 2 == 0) {
                return (nums1[m / 2 - 1] + nums1[m / 2]) / 2.0;
            } else {
                return nums1[m / 2];
            }
        }

        int count = 0; // 合并后的数组的索引
        int i = 0,j = 0; // nums1 和 nums2 的索引
        while(count != (m+n)){
            if(i == m){ // 如果数组 nums1 的元素合并完毕
                while(j != n){
                    nums[count++] = nums2[j++];
                }
                break;
            }
            if(j == n){
                while(i != m){
                    nums[count++] = nums1[i++];
                }
                break;
            }

            if(nums1[i] < nums2[j]){
                nums[count++] = nums1[i++];
            }else{
                nums[count++] = nums2[j++];
            }
        }

        // 到这里已经合并结束了 count=nums.length
        return count%2==0?(nums[count/2-1] + nums[count/2])/2.0:nums[count/2];
    }
}

 5、最长回文子串

5.1、双指针 + 暴力

  • 思路💡:使用双指针判断字符串是否是回文串,然后使用双重循环枚举出每一种子串
  • 时间复杂度🕖:O( n3 )
class Solution {
    public String longestPalindrome(String s) {
        String longest = "";
        for(int i=0;i<s.length();i++){
            for(int j=i+1;j<s.length()+1;j++){
                String sub = s.substring(i,j);
                if(isPalindrome(sub)){
                    longest = sub.length()>longest.length()?sub:longest;
                }
            }
        }
        return longest;
    }
    /**
     * 双指针判断回文串
    */
    public boolean isPalindrome(String str){
        int i = 0,j = str.length()-1;
        while(str.charAt(i) == str.charAt(j)){
            if(i >= j) return true;
            i++;
            j--;
        }
        return false;
    }
}

5.2、 中心扩散法

  • 思路💡:暴力枚举所有回文串的中心,然后使用中心扩散计算回文串的长度
    • 首先寻找边界,因为回文串的中心可能不是一个字符(比如 "abba",或者 "abbba" ,这种可以统一将右指针移动到边界)
    • 此时,左右指针都指向相同的字符,然后左右指针开始同时扩散,找到该中心最长的回文串
    • 长度 =  右指针 - 左指针 + 1
  • 时间复杂度🕖:O(N2)
class Solution {
    public String longestPalindrome(String s) {
        int left=0,right=0;
        int max = 0; // 最长
        for(int i=0;i<s.length();i++){
            int subLeft = i,subRight = i;

            // 寻找右边界
            while(subRight<s.length()-1 && s.charAt(i)==s.charAt(subRight+1)){
                subRight++;
            }
            
            // 左右扩散
            while(subRight<s.length()-1 && subLeft-1>=0 && s.charAt(subLeft-1)==s.charAt(subRight+1)){
                subLeft--;
                subRight++;
            }

            int len = subRight - subLeft + 1;
            if(len > max){
                left = subLeft;
                right = subRight;
                max = len;
            }
        }
        return s.substring(left,right+1);
    }
}

 6、Z 字形变换

感觉这种题对面试没意义,跳过

7、整数反转

纯数学题,跳过

8、字符串转换整数

纯模拟题,这类题做太多了,基本通过了,剩下个别一两个用例懒得写了,意义不大。

这里写一下从中学到的一点,从来没相关在算法里 try-catch 不过还挺好用的:

        try{
            if(Long.parseLong(sb.toString())< Integer.MIN_VALUE){
                return Integer.MIN_VALUE;
            }else if(Long.parseLong(sb.toString()) > Integer.MAX_VALUE){
                return Integer.MAX_VALUE;
            }
        }catch(Exception e){
            return 0;
        }

9、回文数

  • 思路💡:双指针
  • 时间复杂度🕖:O(N)
class Solution {
    public boolean isPalindrome(int x) {
        String s = String.valueOf(x);
        int i = 0,j = s.length()-1;
        while(s.charAt(i) == s.charAt(j)){
            if(i>=j)    return true;
            i++;
            j--;
        }
        return false;
    }
}

10、正则表达式

  • 思路💡没有思路,纯模拟,而且是最难的模拟
  • 时间复杂度🕖:

果断放弃,之后有思路了 再来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

让线程再跑一会

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

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

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

打赏作者

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

抵扣说明:

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

余额充值