从 0 学算法第一周总结:1 万字 30 多幅图

完整 pdf 版请于知识星球内下载

作者:算法刷题日记全体星友

版权归属:算法刷题日记全体星友

整理:振哥


这周星球内每天打卡提交作业平均 90 人次,根据官方推荐的【星球周报】, 36 次入选星球活跃榜,最高排名星球第 8 名,这是不容易的,要知道知识星球用户数 2200万。我们的星球的内容数、活跃人数、新付费成员都超过 99% 的星球。

下面是 Day1-Day9 周报大纲:

  • 1 星球使用方法

  • 2 程序员还需要学算法吗?

  • 3 从零学算法大纲

  • Day 1 冒泡排序

    • 1.1 学习算法需要什么基础

    • 1.2 算法入门参考资料

    • 1.3 算法进阶参考资料

    • 1.4 算法入门最基本思路

    • 1.5 今日打卡题

    • 1.6 精选回答

  • Day 2: 选择排序

    • 2.1 作业点评

    • 2.2 今日打卡题

    • 2.3 精选回答

    • 2.3 作业点评

  • Day 3:什么是一个算法?

    • 3.1 今日打卡题

    • 3.2 精选回答1

    • 3.3 精选回答2

    • 3.4 精选回答3

    • 3.5 参考资料

  • Day 4:Hailstone 练习题

    • 4.1 今日打卡题

    • 4.2 精选回答 1

    • 4.3 精选回答 2

    • 4.4 Collatz 猜想

  • Day 5:数组中插入元素

    • 5.1 今日打卡题

    • 5.2 精彩回答

  • Day 6 :LeetCode 求中心索引

    • 6.1 今日打卡题

    • 6.2 精彩回答

    • 6.3 不高效解

    • 6.4 算法分析总结

  • Day 7 :如何培养算法思维?

    • 7.1 经历描述

    • 7.2 经验总结

    • 7.3 追求目标

    • 7.4 精选回答

  • Day 8 :两数之和

    • 8.1 今日打卡题

    • 8.2 精选回答

    • 8.3 分析总结

    • 8.4 不高效解 1

    • 8.5 不高效解 2

  • Day 9:什么是哈希表?

    • 9.1 今日打卡题

    • 9.2 精彩回答

  • 加入星球

1 星球使用方法

1 知识星球有 web 版和 app 版

2 基本功能

接下来以 web 版使用举例,点击发表主题,完成后,可以选择一个标签,一对 #, 比如 #算法刷题#,提交后,发表的主题最下面会有一个 “算法刷题“ 的标签。

3 点赞和评论

为其他星友的主题点赞和评论

4 点击查看精华主题和文件主题等

精华主题:

文件主题(都是我和星友上传的资料):

5 留意 置顶主题

置顶主题一般是星球的作业贴,精华帖,汇总贴等,注意查看。


2 程序员还需要学算法吗?

近来经常有朋友问我,程序员还需要学习算法吗?我的数学又不太好,我不想碰算法。

其实你说出了很多程序员的心声,因为很多程序员都持有此想法。

在这里,作为一个工作 6 年多的程序员,告诉大家一个秘密,程序员必须要懂些算法,尤其是基本的算法思维必须得有。

为啥呢?

只有具备算法思维的程序员才能写出赏心悦目的代码,注意程序可不是越短越好哦,而是执行效率高,占用存储空间少;

同时,具备算法思维的程序员其实已经甩开没有这方面思维的程序员,一大截!并且,随着码龄变长,优势愈加明显。

这些都是实在话,信不信由你。


3 从零学算法大纲

下面主要有我制定的学习大纲,一个蓝图。


Day 1 冒泡排序

作为算法刷题起航篇,我们有必要先做一个背景介绍,照顾一下算法入门的朋友。

1.1 学习算法需要什么基础

至少熟悉一门编程语言 c, c++, python, java等, 推荐 Python,入门简单

不需要任何算法基础

需要强大的毅力:做到不折不挠

养成喜欢总结的习惯

1.2 算法入门参考资料

书籍:算法图解

书籍:大话数据结构

书籍:数据结构和算法分析 - C 语言描述

书籍:妙趣横生的算法

免费的,算法可视化动画演示:(强烈推荐) https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

1.3 算法进阶参考资料

书籍:算法导论

书籍:编程珠玑

两本极好的外文PDF:免费开源, 已上传到知识星球里

1.4 算法入门最基本思路

学习数据结构:先了解典型的几种数据结构,数组、链表必备,建立意识:算法与数据结构紧密联系

算法,先从基础的算法开始,通常是排序算法,学会算法的评价指标

入门学习参考视频:油管上已经播放100万次+:https://www.youtube.com/watch?v=bum_19loj9A&t=50s

1.5 今日打卡题

学会冒泡排序算法

文章参考:

https://stackabuse.com/bubble-sort-in-python/

作业:写出冒泡的代码,并上传到知识星球里,详见下面介绍。

算法刷题 1:精通冒泡排序 对于大多数人来说,冒泡排序可能是他们在计算机科学课程中听说的第一种排序算法。

它高度直观且易于“转换”为代码,这对于新软件开发人员而言非常重要,因此他们可以轻松地将自己转变为可以在计算机上执行的形式。

但是,Bubble Sort 是在每种情况下性能最差的排序算法之一。但是,排序算法也不是一无是处,检查数组是否已排序,它通常优于快速排序等更有效的排序算法。

Bubble Sort 背后的想法非常简单,我们查看数组中相邻的成对元素,一次查看一对。

如果第一个元素大于第二个元素,则交换它们的位置,否则将它们继续移动。

想办法补全如下代码:

def bubble_sort(our_list):
  # 写出你的代码
  # 补充完整
  return our_sorted_list

1.6 精选回答

优化点:

  • 1、添加有序标记(flag),当没有元素交换时跳出循环

  • 2、记录有序/无序边界,已有序的元素不需要再被进行比较,因此每轮需比较的数列长度会减少

Day 2: 选择排序

2.1 作业点评

学习别人写的代码,也能从中发现一些问题。比如球友小六首先在群里提出来,有的代码把冒泡排序写成选择排序了,并且好多都出现这个问题。所以在此统一吆喝一声,大家看看有没有类似的错误。

区别:冒泡排序比较的一定是紧紧相邻的两个元素;而选择排序却不是每次比较紧邻的两个元素,而下面的代码就不是每次比较紧邻的两个元素,正是选择排序的基本实现。

下面是冒泡排序的例子:

下面是选择排序的例子:

你看一个不起眼的冒泡排序算法,如果细细品味起来也是很有意思的,虽然它的性能注定不好,但是我们的目的是为了训练算法思维,提升算法的分析和应用能力。从这个角度而言,我们的目的达到了。我相信坚持这样分析下去,一定可以让大家的算法思维能力变得更好。

2.2 今日打卡题

Day 2 算法题:写出选择排序

参考下面的几幅图,红色表示当前找到的未排序序列中的最小值,绿色表示当前被比较的元素:

又找到一个更小的值2,重新标记它为红色:

一轮比较后,找到最小值2并标记为黄色,表示就位,继续在未排序序列中寻找最小值:

补全下面代码:

def selection_sort(our_list):
  # 补全代码
  #
  #
  return our_sorted_list

2.3 精选回答

图一:简单选择排序

图二:同时找出最小值与最大值放在数列两侧,两边逐渐逼近,循环次数会减少一些

选择排序是种不稳定的排序,虽然代码有优化,但平均时间复杂度始终为 ,有兴趣的可以了解下堆排序(需要有树的基础),时间复杂度可以优化至

2.3 作业点评

算法步骤

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

重复第二步,直到所有元素均排序完毕。

这种性能差

有一部分球友提交了下面这个版本,此代码中虽然也没有太大问题,但是循环中只要找到小的就交换一次,增加了内存占用。

实际应该为在单次循环时不进行位置交换,而是单次循环后再进行位置交换。

def select_fun(list1):
    n = len(list1)
    for i in range(n-1):  # 开启循环,只需要循环n-1次即可,
        # 假设第i个值为最小值,min_value = list1[i]
        for j in range(i+1, n):  # j 输入i后面的数字
            if list1[j] < list1[i]:  # 如果发现后面存在比最小值档还小的数
                list1[i], list1[j] = list1[j], list1[i]  # 交换顺序
    return list1


if __name__ == '__main__':
    list2 = [3, 5, 6, 1, 2]
    print(select_fun(list2))
    """
    # 输出结果
    [1, 2, 3, 5, 6]
    """

性能良

大部分球友提交的代码实现版本:

def select_fun(list1):
    n = len(list1)
    count = 0  # 统计更改次数
    for i in range(n-1):  # 开启循环,只需要循环n-1次即可,
        min_index = i
        for j in range(i+1, n):  # j 输入i后面的数字
            if list1[j] < list1[min_index]:  # 如果发现后面存在比最小值档还小的数
                min_index = j  # 这里暂时不更改位置,等循环结束后再更改
        if min_index != i:  # 判断当前的最小值是不是i所在位置
            list1[i], list1[min_index] = list1[min_index], list1[i]
            count += 1
    print('交换次数', count)
    return list1


if __name__ == '__main__':
    list2 = [3, 5, 6, 1, 2]
    print(select_fun(list2))
    """
    # 输出结果
    交换次数 4
    [1, 2, 3, 5, 6]
    """

性能更好的实现

Day 3:什么是一个算法?

3.1 今日打卡题

什么是一个算法? 程序就等于算法吗?

理解以上参考清华大学邓俊辉老师的视频:

https://next.xuetangx.com/learn/THU08091000384/THU08091000384/1516243/video/1387095

3.2 精选回答1

3.3 精选回答2

3.4 精选回答3

根据清华大学邓俊辉老师的解释,算法的特质包括但不限于这些:

大家注意,算法特征邓工在最后是使用省略号,说明算法的特点不仅仅包括以上几条。

3.5 参考资料

下载地址:

https://t.zsxq.com/JEuVbYB

Day 4:Hailstone 练习题

4.1 今日打卡题

算法的这些主要特点有时并不像我们想象的那样好去证明。比如,我们会觉得算法有穷性是最容易判断的,其实不然,邓工在课程中介绍了一个例子,在此我引用一下:

def hailstone(n):
    length = 1
    while(1 < n):
        if n % 2 == 0:
            n = n/2
        else:
            n = n*3 + 1
        length += 1
    return length

以上这个 hailstone 函数满足有穷性吗?意思是对于任意的 n, while 循环都会执行有限次而退出吗?

根据邓工的课程,我们得知:

至今学术界都无法证明对于任意的 ,一定满足:

请列举几个不同的 n 值(如 n=  120, 7, 27),分别求出 hailstone 的返回值,体会证明算法有穷性的困难。

4.2 精选回答 1

4.3 精选回答 2

下面我们先绘制 1 万以内的正整数其 Hailstone 序列的长度,代码参考我的知识星球中星友 @Kevin :

注意,代码有一个瑕疵,应该是 n = n//2 这样才会返回一个整数

因为图形貌似冰雹,所以被称为 Hailstone 序列。

然后,星友@guolong 提出一个很好的问题,是否存在一些数,使while 循环一直执行下去?

个问题困扰着世人,至今都没有确定的答案。

4.4 Collatz 猜想

有一个很有意思的问题,我们这样实验:

def hailstone(n):
    length = 1
    seq = []
    while(1 < n):
        if n % 2 == 0:
            n = n//2
            seq.append(n)
        else:
            n = n * 3 + 1
            seq.append(n)
        length += 1
    return length,seq

print(hailstone(7))
print(hailstone(17))
print(hailstone(103))

打印结果如下,数字7,17,103的 Hailstone 序列长度分别为 17, 13, 88,最惊奇的是:每个这种序列的最后三个数一定是 4,2,1

(17, [22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1])
(13, [52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1])
(88, [310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276,
638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732,
866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1])

感兴趣的可一直向右滑动,查看序列的结尾是不是4,2,1这个周期!

并且已经得出:无论使用什么起始值,每个hailstone 序列最终都将停留在4、2、1个周期上。

计算机甚至已经检查了所有起始值(最大到 )(一个 19 位数字),并发现最终出现4、2、1个周期。

但是很遗憾:依然没有科学家能够证明所有序列都是这种情况。

这个开放问题在数学家 Lothar Collatz 于 1937 年首次提出该问题之后被称为 Collatz猜想。

令人惊讶的是,即使是最好的数学家都无法回答,如此简单的形成序列的公式也会引发一个问题。

Day 5:数组中插入元素

5.1 今日打卡题

那么接下来,我们趁热打铁,先学习最最基础的数据结构:array(数组)和vector(向量),数据结构和算法是相辅相成的,二者结合彰显算法之美,所以对于常见数据结构的掌握是很必要的。

参考教材如下:

http://www.xuetangx.com/courses/course-v1:TsinghuaX+30240184+sp/pdfbook/0/

Day5 打卡题:

def insert(lst, i, e):
    """
    lst:一个数组或向量,Python 就用 list 表达吧
    i:待插入元素的位置
    e:待插入元素
    """
    #
    #补全代码
    #

5.2 精彩回答

1、下标处理:正/负数绝对值大于数组长度则添加在数组末尾和首位,负数绝对值小于数组长度则用index+len(list)来处理

2、None值判断及优化:若插入位置刚好是None值,则直接替换

(PS:元素的插入需要考虑扩容问题,如果原本数组已满,则需要扩容。实际上许多API底层也是用最原始的方式去实现的,只是封装起来了就成了方便调用的API,建议可以的话尝试自己的方式去实现;顺便想下,如果把它封装起来需要优化哪些地方。)

Day 6 :LeetCode 求中心索引

6.1 今日打卡题

给定一个整数类型的数组 nums,请编写一个能够返回数组“中心索引”的方法。

我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。

如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

示例 1:

输入: nums = [1, 7, 3, 6, 5, 6] 输出: 3 解释: 索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。同时, 3 也是第一个符合要求的中心索引。

示例 2:

输入: nums = [1, 2, 3] 输出: -1 解释: 数组中不存在满足此条件的中心索引。

请补充完成下面的代码:

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:

6.2 精彩回答

参考星友 infrared62* 的分析和代码:

这种解法是很高效的。

infrared62* 的总结:Java and Python,前缀和的应用,前缀和可以简单看作数列前n项的和,在DP和树路径求和也有应用,同理还有后缀和,前缀积,后缀积。

6.3 不高效解

不过,我也看到有些星友是这么求解的:

相比第一种解法,这种求解方法不高效。

因为每迭代一次,都要 sum 求和两次,而 sum 求和本质也是一个循环,所以相当于嵌套 for 循环。

我们需要思考,是否有必要每次都要求和,显然是不必要的。

如果存在中心索引,则一定满足:中心索引左侧和 * 2 + nums[i] == sum(nums)

而 sum(nums) 一定是个定值, 中心索引的左侧求和可放在循环中逐渐累加得到,所以只用一层 for 即可。

6.4 算法分析总结

往往我们第一下想到的解法未必是最高效的,还要多加分析,培养算法思维才行。而如果想要培养算法思维和敏锐度,就要多加练习,通常来回训练 LeetCode 题是不错的方法。

训练后的结果,就像上面的星友 infrared62* 一样,看到这道题,马上想到一连串的类似思想:前缀和可以简单看作数列前n项的和,在DP和树路径求和也有应用,同理还有后缀和,前缀积,后缀积。

加油,只要我们坚持下去。

Day 7 :如何培养算法思维?

之前查到过一个比较好的算法学习方法总结,来自清华大学算法训练营 longyue0521 ,提出的 “做中学” 方法,个人也是比较认同的,大家不妨看看下面的详细介绍。

事半功倍:Learning by doing 做中学

7.1 经历描述

在我初学编程时,因没有掌握计算机相关专业的学习方法,走了不少弯路。

我总是想先“打好基础”,再走下一步,但这需要时间、毅力与坚持。

我花了很大气力学习,但都事倍功半!

我想找到效率更高的学习方法,于是我开始浏览美国计算机四大名校的课程网站。

经过一番研究,自学几门课程后,我发现了他们的教学套路:

  • 教授理论知识(一),小作业,用于巩固理论知识(一)

  • 教授理论知识(二),小作业,用于巩固理论知识(二)

  • 大作业,编程实践,需要用到理论知识(一)与(二)

  • 教授理论知识(三),小作业,用于巩固理论知识(三)

  • 教授理论知识(四),小作业,用于巩固理论知识(四)

  • 大作业,编程实践,需要用到理论知识(三)与(四)

  • 项目作业,编程实践,多人协作,需要用到理论知识(一)~(四)+ hits

  • 重复上述过程,一般重复4~6次,中间穿插期中考试,最后期末考试

由此“套路”总结出另一种学习方法——“迭代学习”法:

  • 理解待解决的问题

  • 学习部分理论知识

  • 动手实践尝试解决,无法解决,回到1或2

  • 成功解决抓紧总结

  • 即使现在回头看,我也不能说第一种学习方法有错,“迭代”学习法更好!但这两种学习方法都是以同一个核心为基础的——动手做,做中学!

你可以都尝试一下,然后选取自己喜欢、又高效的学习方法!

当然也欢迎分享你的学习方法!

7.2 经验总结

越早适应“迭代”学习法对你越有利。 大多数时候你没有足够的时间来“学完再做”。 若你在“迭代”学习过程产生的焦虑、沮丧、挫败感,请及时排解 排解后记得回来,坚持才能胜利! 个人建议 学习《算法设计》在借鉴学习《数据结构》的经验的同时,

需要做适当调整——在每次大迭代中应用“迭代”学习法:

第一次迭代,熟悉常用的算法设计策略,掌握策略的使用方法及适用的场景

  • 其实学习《数据结构》时你已经学了不少经典算法

  • 带着学到的算法设计策略回头总结、归纳经典算法

  • 可以在纸上画画设计策略与经典算法的关系图,是一对一,一对多,还是多对多

  • 这个阶段的重点,在脑中建立常用算法设计策略与经典算法的对应关系

  • 如果个人能力不错,可考虑与《数据结构》第三次迭代同时进行

第二次迭代,灵活运用算法设计策略,解决实际问题

  • 大量的解决问题,在此过程中总结出你个人解决问题的流程

  • 可以针对某项设计策略进行专项训练,但要考虑实际需求——工作、面试、竞赛

  • 此阶段的重点就是解决《数据结构》第三次迭代中的隐藏关卡,同时培养解决问题的感觉、自觉

  • 别忘了“迭代”学习方法

  • 大量训练、多与他人探讨、扩展自己的思路并及时总结

第三次迭代,对给定问题能运用数学证明你的算法设计策略是正确的、可行的、高效的

  • 这个阶段要做的事本应该融入到前两次迭代中的,甚至更早比如在离散数学课上

  • 之所以单抽出来是因为有太多的人因这个“拦路虎”而徘徊在“算法设计”的大门前迟迟不敢踏入半步,更有甚者转身离开了就再也没回来......

  • 如果你不擅长数学,或不打算从事科研及对数学要求较高的工作,可以跳过

  • 对于打算从事科研及对数学要求较高的工作的人来说,这也可以算迂回策略

  • 可以先从教材对经典算法的证明学习,然后重走第二次迭代实践(这才是看CLRS的时机)

  • 这个阶段的重点,有意识地运用数学来决定设计策略的选取

7.3 追求目标

你在学习《算法设计》的初期没能养成良好的推理、证明习惯,后期改正要费些功夫信心/底气不足,有强大的数学理论作为支撑你敲代码、测试或和别人辩论也底气十足尽管不完美但比起那些徘徊在门口、转身离开的人,你已进入“算法设计”的大门!

这难道不值得高兴吗?

7.4 精选回答

打卡第七天

我本人对编程最深的追求,大概是想在解决现实问题的过程中写出最漂亮的代码。曾经入手python时,以为的漂亮就是越简洁越好越短越好。什么时候认识到算法和数据结构的问题呢,大概是有些代码的写法的内在逻辑让我头秃的时候。我不能忍受自己死记硬背一些明明很有逻辑的东西,那个时候就开始回头一点点交补课费了。感谢振哥以及这个平台。最开始我都没想过打卡,就是花点钱看看算法的大佬都是什么样子。可振哥的作业设计有介绍有链接有大家的分享,一下子对我宽容了许多。虽然这并不能替代我自己去深耕这些知识背后的逻辑,但的确给了我些许的勇气告诉自己这也可以慢慢学。事实上,仔细去学习这些根本上的东西对自己理解代码书写代码思考问题的解决方式真的有很明显的改变。写这些感觉写早了,毕竟300天才走了7天,但也的确想写出来,接下来的路还要坚持走,给自己加油!

Day 8 :两数之和

8.1 今日打卡题

首先看下题目描述:

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]

题目参考链接:https://leetcode-cn.com/problems/two-sum

考察知识点:数组,哈希表(字典)

请补充完整如下代码:

class Solution:    
      def twoSum(self, nums: List[int], target: int) -> List[int]:

这道题目大家仔细思考下,想办法写出尽可能高效的代码。提示:牺牲空间换取时间

8.2 精选回答

思路:结合题目及提示这里使用字典。

用target减去列表中的某个值,并将该值作为key,其下标作为value存放到字典中,接着target依次减去剩下的列表项中的值并判断结果是否存在于字典,如果存在即表示列表中有两个值相加等于target,此时即可直接返回答案。并且字典存储可以起到去重的作用。

8.3 分析总结

大家注意审题,确定输入是什么,输出又是什么,假定又是什么。

输入:待寻找的列表 nums, 两数之和 target

输出:有且仅有满足要求的一对整数的下标

假定:一定存在,且仅有一个答案

题目分析:两个数之和等于 target, 首先标记所有遍历过的数,如果 target 减去当前被遍历到的值 e 后,即 target-e 被标记过,则找到答案。

判断值是否在某个容器中,做到 O(1) 时间复杂度的便是最常用的散列表,对应 Python 中的字典。就本题而言,键为标记元素值,字典值为数组下标,所以更加确定使用字典这个数据结构。

代码 :

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        d = {}
        for i,e in enumerate(nums):
            if target -e in d:
                return [i,d.get(target-e)]
            d[e] = i 

以上是比较高效的解法之一。

从星球中星友提交的代码看,有一些星友的代码就是上面的实现思路。

但是,也有一些星友的代码是这样的,解并没有达到时间复杂度为 O(n),大家不妨参考并回头检查下自己写的。

8.4 不高效解 1

index 复杂度为 O(n), 所以实际时间复杂度为 O(n^2),尽管表面上看只有一个 for 循环。

8.5 不高效解 2

下面代码两层 for,空间复杂度虽然为 O(1),但是时间复杂度为 O(n^2)。所以需要找到牺牲空间换取时间的方法。

以上使用散列表牺牲空间,但是换取时间,实际中能找到节省时间的解往往更有价值。

Day 9:什么是哈希表?

9.1 今日打卡题

我们来学习最重要的数据结构之一:散列表或哈希表。

那么什么是哈希表呢?哈希表怎么做到 O(1) 时间复杂度找到某个元素的呢?

提供参考资料《我的第一本算法书》

大家可参考如下哈希表的基本介绍:

9.2 精彩回答

概念:哈希表是根据键值(key value)而直接进行访问的数据结构。它通过把键值映射到一个位置来访问记录,以加快查找的速度。

具体映射过程是:把 Key 通过一个映射函数转换成一个整型数字,然后将该整型数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该取余数字为下标的数组空间里。这个映射函数称为哈希函数,映射过程称为哈希化,存放记录的数组叫做哈希表

查询:数组的特点是寻址容易,插入和删除困难;而链表的特点是寻址困难,插入和删除容易;哈希表则实现了寻址容易,插入删除也容易。

当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value。哈希表是一个在时间和空间上做出权衡的数据结构。

如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如果没有时间限制,那么可以使用无序数组并进行顺序查找,这样只需要很少的内存。

冲突:哈希映射对不同的键,可能得到同一个散列地址,即同一个数组下标,这种现象称为冲突。

避免哈希冲突

1.拉链法:通过哈希函数,我们可以将键转换为数组的索引(0~M-1),但是对于两个或者多个键具有相同索引值的情况,我们需要有一种方法来处理这种冲突。一种比较直接的办法就是,将大小为M 的数组的每一个元素指向一个条链表,链表中的每一个节点都存储散列值为该索引的键值对,这就是拉链法。

2.线性探测法:基本原理为,使用大小为M的数组来保存N个键值对,其中M>N,我们需要使用数组中的空位解决碰撞冲突。当碰撞发生时即一个键的散列值被另外一个键占用时,直接检查散列表中的下一个位置即将索引值加1,这样的线性探测会出现三种结果:1)命中,该位置的键和被查找的键相同;2)未命中,键为空;3)继续查找,该位置和键被查找的键不同。

加入星球

加入星球,从零学程序员必备的算法,每天在星球内记录学习过程并打卡,超赞!

长按二维码领取优惠券,打卡 300 天,退还除平台收取的其他所有费用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值