代码随想录算法训练营:5/60

非科班学习算法day5 | LeetCode242: 有效的字母异位词,Leetcode349: 两个数组的交集,Leetcode202: 快乐数,Leetcode1:两数之和

目录

介绍

一、基础概念补充:

1.哈希表

2.c++11新特性(部分)

范围for循环 (for-each loop)

自动类型推导 (auto 和 decltype)

二、LeetCode题目

1.LeetCode242:有效的字母异位词 

题目解析

 2.Leetcode349:两个数组的交集 

题目解析

3.Leetcode202:快乐数

题目解析

 

4.Leetcode1:两数之和

题目解析

总结


介绍

包含LC的四道题目,还有相应概念的补充。

相关图解和更多版本:

代码随想录 (programmercarl.com)https://programmercarl.com/#%E6%9C%AC%E7%AB%99%E8%83%8C%E6%99%AF


一、基础概念补充:

1.哈希表

        哈希表(Hash Table),也称为散列表,是一种数据结构,它实现了关联数组的抽象数据类型,能够以键值对的形式存储数据。哈希表通过一个特定的函数(哈希函数)将键转换为索引,从而实现快速的查找、插入和删除操作。

哈希表的优点是:

  • 平均时间复杂度:在理想情况下,插入、删除和查找操作的时间复杂度可以达到O(1)。
  • 动态大小调整:可以根据需要动态调整表的大小,以维持高效的性能。

总结就是:见到需要查找指定元素在不在范围内的时候首先想到哈希表

哈希表的常见 结构:    

  • std::unordered_map: 键值对存储,键唯一,非有序。
  • std::unordered_set: 仅存储键,键唯一,非有序。
  • std::unordered_multimap: 键值对存储,允许键重复。
  • std::unordered_multiset: 仅存储键,允许键重复。
  • 数组也是哈希表

2.c++11新特性(部分)

  1. 范围for循环 (for-each loop)

    使用 for(auto& item : container) 语法遍历容器或数组,使代码更加简洁。
  2. 自动类型推导 (auto 和 decltype)

    auto 关键字允许编译器自动推导变量的类型,简化了类型声明,特别是在使用复杂类型时。decltype 用于获取一个表达式的类型,常用于模板编程和函数返回类型推导。

二、LeetCode题目

1.LeetCode242:有效的字母异位词 

题目链接:242. 有效的字母异位词 - 力扣(LeetCode)

题目解析

       分析题目需求可知:找A里出现的东西B里是不是都出现了,且次数相同!

        符合哈希表的应用场景:找有没有。确定了方法之后,我们就要看选择什么容器来作为哈希表。如果无脑选择map肯定是可以的,分别记录值和值出现的次数。但是要知道相对来说数组的消耗性最小,也最简单。这里符合使用数组的条件(因为字母只有26个)。

        具体的实现还是依靠循环来查找。

 c++代码如下:

class Solution {
public:
    bool isAnagram(string s, string t) 
    {
        //建立数组,包含26个字母
        int nums[26] = {0};
        for (int i = 0; i < s.size(); i++)
        {
            //记录相对位置的字母的数量增加情况
            nums[s[i] - 'a']++;
        }
        
        //遍历字符串t
        for(int i = 0; i < t.size(); i++)
        {
            //记录相对位置字母的减少情况
            nums[t[i] - 'a']--;
        }

        //判断异位词
        for(int i = 0; i < 26; i++)
        {
            if(nums[i] != 0)
            {
                return false;
            }
        }

        //没有判断分支就返回真
        return true;
    }
};

注意点1:记录方式要注意,因为这里字母的记录方式是ASCII码,不过在本题中,不需要直到具体的值,所以只需要记录相对值就可以,所以采用了-‘a’的操作。

注意点2:注意最后的检验条件,两个字符串里的元素数量必须完全相同。

这里为了节省空间,所以在一个就在哈希表中操作,最后查看存储的数;如果对空间没有限制,我一开始做的是,用两个表,分别记录,然后因为元素相对于a的位置是一样的,然后比对两个表的值,但是这样明显不够简洁。

 2.Leetcode349:两个数组的交集 

题目链接:349. 两个数组的交集 - 力扣(LeetCode)

题目解析

       这题我记录三种不同的方法,这里是方法1,基于上一道题,我们知道在处理有限元素的问题时,完全可以采用数组作为哈希表。本处就是基于leetcode的备注求解。

 

 C++代码如下: 

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) 
    {
        int c_nums1[1001] = {0};
        int c_nums2[1001] = {0};

        for(int i = 0; i < nums1.size(); i++)
        {
            c_nums1[nums1[i] - 0]++;
        }

        for(int i = 0; i < nums2.size(); i++)
        {
            c_nums2[nums2[i] - 0]++;
        }


        vector<int> result;
        for(int i = 0; i < 1001; i++)
        {
            if(c_nums1[i] != 0 && c_nums2[i] != 0)
            {
                result.push_back(i);
            }
        }
        
        return result;
    }
};

这种写法就比较呆,也是记录相对位置信息,只不过这里是数字,其实就会引出来下面比较简洁的写法: 

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) 
    {
        //声明记录hash表
        int count[1001] = {0};

        //声明unordered_set
        unordered_set<int> us1;

        //声明记录结果unordered_set
        unordered_set<int> us;

        //遍历数组1
        for(auto num : nums1)
        {
            us1.insert(num);
            count[num]++;
        }

        //遍历顺组2
        for(auto num : nums2)
        {
            if(count[num] != 0)
            {
                us.insert(num);
            }
        }

        //声明返回vector
        vector<int> result;
        result.assign(us.begin(), us.end());
        return result;
    }
};

 注意点1:这里用了auto关键字,这是c++11引入的新特性可以自动推导类型,有两个地方比较适用:

        1.初始化就赋值的情况,比如

auto x = 10;

        2.对于STL中vector等容器的迭代器,在初始化的时候可以使用auto关键字,很方便

注意点2:for的范围基遍历,这也是c++11引入的新特性,可以完全遍历全部元素,但是不适用于仅仅遍历部分元素!

注意点3: 这里使用set的作用是去重,去重的作用非常明显,能大大提升效率,但是最后需要返回的是vector,所以要利用迭代器复制一份给vector。

但是还是不够简洁,而且依托于数组的思维,如果这里没有限定,那么我们能不能写呢,所以有了完全借助set的方法:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) 
    {
        //定义数组1的unordered_set
        unordered_set<int> c_nums1(nums1.begin(), nums1.end());
        
        //定义储存结果的unordered_set
        unordered_set<int> result;

        for(auto num : nums2)
        {
            if(c_nums1.find(num) != c_nums1.end())
            {
                result.insert(num);
            }
        }

        vector<int> re (result.begin(), result.end());
        return re;
    }
};

 注意点1:这里题目明确说了不要求返回顺序,那么相对来说unordered_set是最好的选择,这也是进一步优化的点。

注意点2:首先我们用unordered_set建立哈希表,记录数组1的情况,在遍历数组2的时候就对照哈希表来看是否符合,进而确定是不是要进行更新结果。这种思路对于两个数组或者字符串来说相当好用。

3.Leetcode202:快乐数

题目链接:202. 快乐数 - 力扣(LeetCode)

题目解析

       难点一:可以知道是要寻找是否出现1,那么怎么建立哈希表

       答:还是用set来记录

       难点二:怎么进行位数上的操作

       答:取余和整除

这里关于官方给出的很多情况的考虑我觉得没有必要,我仅仅是根据题目当中的意思来翻译,没有1,就会出现循环

C++代码如下:

class Solution {
public:
    int sumnum(int n)
    {
        int sum = 0;
        while(n)
        {
            //操作n
            sum += (n % 10) * (n % 10);
            n /=10;
        }
        return sum;
    }

    bool isHappy(int n) 
    {
        //设置set接收计算结果
        set<int> result;
       
        while(true)
        {
            int a = sumnum(n);
            if (a == 1)
            {
                return true;
            }

            if(result.find(a) != result.end())
            {
                return false;
            }

            else
            {
                result.insert(a);
            }
            n = a;
        }
        return false;     
    }
};

注意点1:为了代码的可读性,先创建了一个求和的成员函数,这里就充分用到了整除和取余的方法来帮助我们获取每一位上的值,这里的循环条件设定就是,除到最后为0,结束循环

注意点2:接下来的计算中,遇到1,返回真;没有1,判断是否已经出现过的值。在最后不要忘记将当前数字更新为计算后的值,才能实现循环更新。

 

4.Leetcode1:两数之和

题目链接:1. 两数之和 - 力扣(LeetCode)

题目解析

       最直接的想法,用两个循环嵌套一下,结束战斗,很显然复杂度O(n^2),我用python展示一下

python代码如下:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        #寻找两数之和都等于target的数
        for i in range(len(nums)):
            num1 = nums[i]
            for j in range(i+1, len(nums)):
                num2 = nums[j]
                if num1 + num2 == target :
                    return (i, j)

        

 但是对于这种形式,最好的一定不是这种O(n^2)的方法,这里我们也可以将问题理解成:对于一个确定的值,去寻找一个数,和它加和等于目标值,这正是运用hash的场景,那么问题首先还是:用什么容器?

        这里题目要求的是要下标值,所以就要利用map来进行有序的记录,将下标作为键,将对应的值作为键值。

c++代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        //建立map接受结果
        unordered_map<int, int> m;

        for(int i = 0; i < nums.size(); i++)
        {
            auto it = m.find(target - nums[i]);
            if(it != m.end())
            {
                return {it->second , i};
            }
            else
            {
                m.insert(pair<int, int>(nums[i], i));
            }
        }
        return {};
    }
};

总结


打卡第5天,坚持!!!

  • 55
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值