抽象概念一定要被具象化才能被理解吗?

转载自 https://www.yueyao1982.com/phil_faq/faq_abstraction.html 若侵犯了内容创作者的权益,请联系删除 正文: 恰恰相反,具体的问题一定要抽象化之后才能理解,虽然这乍听起来比较奇怪。我们不妨想一想中学物理中最简单的问题,比如斜面上物体的运动。我们是先把物体和斜面都作了抽象化——物体和斜面都有很多细节,即“象”,而这些“象”被我们忽略了,或者说是抽离了,亦即“抽象”。所以,我们通过牛顿力学来理解机械运动,只是在理解抽象物体的相互关系,比如它们之间的作用,即力,比如它们之间的相对运动:简而言之,我们是在研究一个抽象模型,并试图理解这个模型。我们能做到最好的,就是我们的抽象化很合理,亦即被抽掉的“象”都是次要的;相应的,我们能得到最好的结果就是:模型的预测结果和实际的观测结果之间误差很小,一般来说不能没有误差,除非被观测值的可能性是离散甚至是有限的——比如电子的自旋只有两种可能。即使我们真的把主要因素抽掉,我们仍然可以理解那个被抽象出来的模型,只是那个模型和经验世界已经不能很好的对应了。 稍微深入一些来说,并非所有的抽象概念都能具象化,或者说概念的世界至少从规则和可能性这些方面来讲,并不受制于经验的世界,虽然没有经验概念就无从产生。比如我们观察过很多“经验中的马”,忽略了彼此不同的“象”,比如颜色、大小、公母等因素,就产生了“马的概念”;同样,我们观察过很多“经验世界中的鸟”,忽略了彼此不同的“象”,就产生了“鸟的概念”。“马的概念”和“鸟的概念”都可以被具象化到自然界中真实的动物。然而,我们的思维还可以对概念进行加工,比如我们对“鸟”概念进行切分,得到了“翅膀的概念”(当然它还是可以被具象化的)。然后我们把“翅膀的概念”和“马的概念”进行组合,得到了“天马”的概念——这个概念虽然可以具象化为具体的玩具或模型,但不再能具象化为自然界中的动物了。与此相似,我们还可以进行更复杂的概念建构,比如用更多的翅膀构建出“六翼天使”,比如用多种动物的不同部分构建出了“龙”和“麒麟”等神兽。当然,到此为止,这些概念至少还是可以被在一定程度上具象化,比如画成图画,比如做成雕刻。但是,更进一步的概念建构使这点也不可能了。如果我们把自然数的计数泛化到空间的维度上,我们就构建出了N维空间——1维空间、2维空间和3维空间是可以被具象化的,4维...

leedcode——49. Group Anagram题解

 题目要求:

    Given an array of strings strs, group the anagrams together. You can return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

 

Example 1:

Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Example 2:

Input: strs = [""]
Output: [[""]]

Example 3:

Input: strs = ["a"]
Output: [["a"]]

 

Constraints:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] consists of lowercase English letters.

解法:

  


       vector<vector<string>> groupAnagrams(vector<string>& strs) {
    unordered_map<string, vector<string>> anagramGroups;
    
    for (const string& str : strs) {
        string sortedStr = str;
        sort(sortedStr.begin(), sortedStr.end());
        anagramGroups[sortedStr].push_back(str);
    }
    
    vector<vector<string>> result;
    for (auto it = anagramGroups.begin(); it != anagramGroups.end(); ++it) {
        result.push_back(it->second);
    }
    
    return result;
}

    it->second是什么意思

    
        在C++中,it->second是一个用于访问unordered_map中键值对的语法。在这个例子中,it是一个指向unordered_map中元素的迭代器,it->second表示当前迭代器指向的键值对中的值部分。

在这个特定的情况下,anagramGroups是一个unordered_map,它的键是经过排序后的字符串,值是原始的字符串列表。所以当我们遍历anagramGroups时,it->second表示当前排序后字符串对应的原始字符串列表。

    


    for (const string& str : strs)中为什么要用const和&


    在C++中,使用const修饰变量表示该变量是不可修改的,即它的值在声明后不能被修改。而使用&表示引用,它允许我们通过引用来操作原始数据,而不是创建数据的副本,这样可以提高程序的效率。

for (const string& str : strs)这个语句中,const string&表示我们遍历strs数组时,每个元素都是一个不可修改的字符串引用。这样做有两个好处:

  1. 使用const确保了在循环中不会意外地修改字符串的值,从而提高了代码的安全性。
  2. 使用引用&避免了在循环中复制字符串,提高了性能,尤其是当处理大量字符串时。

因此,for (const string& str : strs)这个语句可以保证安全地遍历字符串数组,并且在性能上有一定的优势。

    



    

++it 和 it++ 有什么区别

    在大多数情况下,++itit++ 都会增加迭代器 it 的值。它们之间的主要区别在于返回值和效率上。
  1. 返回值:

    • ++it(前置递增)会先递增 it 的值,然后返回递增后的迭代器。
    • it++(后置递增)会先返回当前的 it,然后再将 it 的值递增。
  2. 效率:

    • ++it 通常比 it++ 更高效。因为 ++it 只需要递增迭代器的值,而 it++ 还需要创建并返回 it 的副本,然后再递增原始迭代器的值。

在大多数情况下,这两者的区别并不会对代码的功能产生影响,但在某些特定情况下,例如在循环中对大型数据结构进行迭代时,前置递增操作 ++it 通常会更高效,因为它不需要创建副本。

    for (auto it = anagramGroups.begin(); it != anagramGroups.end(); ++it) {
result.push_back(it->second);
}
为什么这里不用&

    
        在这个循环中,我们使用了迭代器it来遍历anagramGroups中的每一个键值对。在C++中,迭代器是一种对象,它本身就代表了容器中的某个元素,而不是指向该元素的指针。

在循环中,it是一个迭代器对象,它按值传递给result.push_back()函数。因为我们只需要访问it->second,而不需要修改迭代器本身,所以不需要使用引用&来传递迭代器。

如果我们希望在循环中修改迭代器本身(例如,将迭代器向前移动),那么我们就需要使用引用&来传递迭代器,类似于for (auto& it = anagramGroups.begin(); it != anagramGroups.end(); ++it)。但在这个例子中,并没有对迭代器本身进行修改操作,因此不需要使用引用。

此博客中的热门博文

算法的八大常用思想