【leetcode hot 100】刷题记录与总结笔记(4/100)

(•̀ᴗ•́)و 大二打算法的小菜狗又回来啦!这次是以研究生找工作的目标开始刷题,坚持就是胜利!每天一道,完成比完美更加重要!

这篇笔记会使用Java和C++两种语言去刷题~

目录

[1、两数之和 - 哈希表](#1、两数之和 - 哈希表)

[2、字母异位词分组 - 哈希表](#2、字母异位词分组 - 哈希表)

[3、最长连续序列 - 哈希表](#3、最长连续序列 - 哈希表)

[4、移动零 - 双指针](#4、移动零 - 双指针)


1、两数之和 - 哈希表

这道题很容易想到O(n2)的双for循环暴力破解,这里就不再赘述。
思路

每遍历到一个位置,开始查map:前面有没有人和我匹配?有就直接返回,没有就把自己登记到map中,继续往后寻找

用Map存【nums[i] : 下标i】,注意值为key,下标为value

这样遍历数组nums时,对于每一个nums[i]:

  • 如果map中存在key = target - nums[i],说明当前数和前面的某个数之和恰好为target,直接返回[map(target-nums[i])下标,i]
  • 否则将【nums[i]:i】存入map中,继续向后寻找

【1】Java版

java 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> mp = new HashMap<>();

        for(int i = 0;i < nums.length; i++){
            if(mp.containsKey(target - nums[i])) 
               return new int[]{mp.get(target - nums[i]),i};
            mp.put(nums[i],i);
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}

【2】C++版

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> mp;
        for(int i=0;i<nums.size();i++){
            if(mp.count(target - nums[i]))
                return {mp[target - nums[i]],i};
            mp[nums[i]]=i;
        }
        return {};
    }
};

2、字母异位词分组 - 哈希表

题意

如果两个字符串从小到大排序后相等,那么两个字符串就互为字母异位词,否则不是。

例如 aab,aba,baa 排序后都是 aab,所以 aab,aba,baa 互为字母异位词。
思路

用哈希表存储【key:排序后的字符串,value列表:排序前的字符串】

最后按key输出每组value值

【1】Java版

cpp 复制代码
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> mp = new HashMap<>();

        for(String s : strs){
            char[] sorted = s.toCharArray();
            Arrays.sort(sorted);
        //- 如果map中不存在这个key,则创建一个新的空List作为value
        //- 如果map中已存在这个key,则直接返回对应的value(已有的List)
        //最后,将字符串s添加到这个(新的或已有的)List中
            mp.computeIfAbsent(new String(sorted),_ -> new ArrayList<>()).add(s);
        }
        return new ArrayList<>(mp.values());
    }
}

【2】C++版

cpp 复制代码
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> mp;

        for(string& s:strs){
            string sorted = s;
            sort(sorted.begin(),sorted.end());
            mp[sorted].push_back(s);
        }

        vector<vector<string>> res;
        for (auto& pair : mp) {
            res.push_back(pair.second);
        }
        return res;
    }
};

3、最长连续序列 - 哈希表

我尝试排序sort,只跑通一半样例,想【0,1,1,2】这种情况最长序列个数为3(0,1,1)或(1,1,2),但我的代码计算这个样例时跑出来是(0,1,1,2),故放弃,看题解


题意

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

0,1,1,2\] 答案:3 **思路**: 首先,本题是不能排序的,因为排序的时间复杂度是 O(nlogn),不符合题目 O(n) 的要求。 我们将数字存入set(去重),因为set可以快速判断元素是否存在set.contains() 核心思路:对于 nums 中的元素 x,以 x 为起点,不断查找下一个数 x+1,x+2,⋯ 是否在 nums 中,并统计序列的长度。 * 我们遍历set中的每一个元素x,对于每一个x,如果set中存在x-1,则跳过该x,因为后面以x-1为起点的序列肯定更长,避免重复计算 * 确定好起点x后,循环向后不断查找 y = x+1 是否在set中,如果存在则y++ * 循环结束后,y-1就是连续序列的最后一个数,则最长序列为【x \~ y-1】,长度为 y-x * 遍历每一个x都以其为起点(如果存在更小的以更小的为起点,避免重复计算),算出以它为起点的最长连续序列长度,最后通过res = max(res , y-x)维护全场最长序列长度

【1】Java版

cpp 复制代码
class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> st = new HashSet<>();
        for(int num: nums) st.add(num);

        int res = 0;
        for(int x: st){
            if(st.contains(x - 1)) continue;

            int y = x + 1;
            while(st.contains(y)) y++;
            res = Math.max(res, y - x);
        }
        return res;
    }
}

【2】C++版

cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        int res = 0;
        unordered_set<int> st(nums.begin(),nums.end());
        for(int x: st){
            if(st.contains(x-1)) continue;//如果序列中存在更小的,以更小的为起点
            //找到最小的起点x
            int y = x + 1;
            while(st.contains(y)) y++; //不断向后查找下一个数是否在set
            //循环结束后,y-1是最后一个在哈希表的数
            res = max(res, y - x); //从x~y-1序列长度为y-x
        }
        return res;
    }
};

4、移动零 - 双指针

题意:

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。

复制代码
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

思路:

最直接的思路是,把非零元素整体挪到最前面,零元素整体挪到最后面

我们可以维护下标【i0 ~ i-1】区间为零元素,a[i0]始终指向最靠左边的零元素

  • 当i=n时循环结束,此时【i0 ~ n-1】即为零元素,即把零元素挪到最后
  • :这里a[i0]始终在最左边,这样非零元素和a[i0]交换时始终在相对最前边,即保证非零元素原始顺序

遍历数组a,如果:

  • a[i]=0,则不操作
  • a[i]≠0,则交换swap(a[i],a[i0]),i0++

比如[0,1,0,3,12]:(绿色区间代表维护的【i0 ~ i-1】区间)

i=0 [++0++,1,0,3,12] 不操作 i0=0

i=1 [0,++1++,0,3,12] swap(a[i]=1,a[i0]=0) i0++(i0=1)

i=1 [++1,0++,0,3,12] 交换完

i=2 [1,0,++0++,3,12] 不操作 i0=1

i=3 [1,0,0,++3++,12] swap(a[i]=3,a[i0]=0) i0++(i0=2)

i=3 [1,++3++ ,0,++0++,12] 交换完

i=4 [1,3,0,0,++12++] swap(a[i]=12,a[i0]=0) i0++(i0=3)

i=4 [1,3,++12++,0,++0++] 交换完

i=5 [1,3,12,0,0] 遍历结束(i0=3)

【1】Java版

cpp 复制代码
class Solution {
    public void moveZeroes(int[] a) {
        int i0=0;
        for(int i=0;i<a.length;i++)
            if(a[i]!=0)
            {
                int t=a[i];
                a[i]=a[i0];
                a[i0]=t;
                i0++;
            }
        
    }
}

【2】C++版

cpp 复制代码
class Solution {
public:
    void moveZeroes(vector<int>& a) {
        int i0 = 0;
        for(int i = 0; i < a.size(); i++){
            if(a[i]!=0){
                swap(a[i],a[i0]);
                i0++;
            }
        }
    }
};
相关推荐
黑牛先生3 小时前
【GDB】调试Jsoncpp源码
开发语言·c++·算法
深蓝海拓3 小时前
PySide6从0开始学习的笔记(五) 信号与槽
笔记·qt·学习·pyqt
大大大大物~3 小时前
JVM 之 垃圾回收算法及其内部实现原理【垃圾回收的核心问题有哪些?分别怎么解决的?可达性分析解决了什么问题?回收算法有哪些?内部怎么实现的?】
jvm·算法
不夜牛仔3 小时前
算法笔记19 - 图和通用结构 | 图的两种遍历 | 三种拓扑排序 | 两种最小生成树算法Kruskal, Prim | 最短路径算法Dijkstra
笔记·算法
小龙报3 小时前
【算法通关指南:基础算法篇】高精度专题:一篇破除超数运算问题
c语言·数据结构·c++·算法·链表·贪心算法·visual studio
ULTRA??3 小时前
Informed RRT*算法,并包含圆形障碍物环境
人工智能·python·算法
Yupureki3 小时前
《算法竞赛从入门到国奖》算法基础:入门篇-双指针
c语言·开发语言·数据结构·c++·算法·visual studio
AshinGau3 小时前
权重衰减(Weight Decay)
神经网络·算法
Liangwei Lin4 小时前
洛谷 P3367 【模板】并查集
算法