MIT:算法导论——8.散列表

散列表的查找 散列表是根据关键字直接进行访问的数据结构。散列表通过散列函数将关键字映射到存储地址,建立了关键字和存储地址之间的一种直接映射关系。 散列函数(Hash function),又称为哈希函数,是将关键字映射到存储地址的函数,相当于一种映射规则。 方便理解,下面举例两个常见的散列函数 直接定址法:直接取关键字的某个线性函数作为散列函数,散列函数形式如下: hash(key)=a*key+b 例如,学生的学号{601001, 601002, 601005, …, 601045},那么可以设计散列函数为:H(key) 阅读详情






====================博客补充====================================================

摘要:

  本章介绍了散列表(hash table)的概念、散列函数的设计及散列冲突的处理。散列表类似与字典的目录,查找的元素都有一个key与之对应,在实践当中,散列技术的效率是很高的,合理的设计散函数和冲突处理方法,可以使得在散列表中查找一个元素的期望时间为O(1)。散列表是普通数组概念的推广,在散列表中,不是直接把关键字用作数组下标,而是根据关键字通过散列函数计算出来的。书中介绍散列表非常注重推理和证明,看的时候迷迷糊糊的,再次证明了数学真的很重要。在STL中map容器的功能就是散列表的功能,但是map采用的是红黑树实现的,后面接着学习,关于map的操作可以参考:http://www.cplusplus.com/reference/map/。

1、直接寻址表

  当关键字的的全域(范围)U比较小的时,直接寻址是简单有效的技术,一般可以采用数组实现直接寻址表,数组下标对应的就是关键字的值,即具有关键字k的元素被放在直接寻址表的槽k中。直接寻址表的字典操作实现比较简单,直接操作数组即可以,只需O(1)的时间。

2、散列表

  直接寻址表的不足之处在于当关键字的范围U很大时,在计算机内存容量的限制下,构造一个存储|U|大小的表不太实际。当存储在字典中的关键字集合K比所有可能的关键字域U要小的多时,散列表需要的存储空间要比直接寻址表少的很多。散列表通过散列函数h计算出关键字k在槽的位置。散列函数h将关键字域U映射到散列表T[0....m-1]的槽位上。即h:U->{0,1...,m-1}。采用散列函数的目的在于缩小需要处理的小标范围,从而降低了空间的开销。

  散列表存在的问题:两个关键字可能映射到同一个槽上,即碰撞(collision)。需要找到有效的办法来解决碰撞。

3、散列函数

  好的散列函数的特点是每个关键字都等可能的散列到m个槽位上的任何一个中去,并与其他的关键字已被散列到哪一个槽位无关。多数散列函数都是假定关键字域为自然数N={0,1,2,....},如果给的关键字不是自然数,则必须有一种方法将它们解释为自然数。例如对关键字为字符串时,可以通过将字符串中每个字符的ASCII码相加,转换为自然数。书中介绍了三种设计方案:除法散列法、乘法散法和全域散列法。

(1)除法散列法

  通过取k除以m的余数,将关键字k映射到m个槽的某一个中去。散列函数为:h(k)=k mod m 。m不应是2的幂,通常m的值是与2的整数幂不太接近的质数。

(2)乘法散列法

  这个方法看的时候不是很明白,没有搞清楚什么意思,先将基本的思想记录下来,日后好好消化一下。乘法散列法构造散列函数需要两个步骤。第一步,用关键字k乘上常数A(0<A<1),并抽取kA的小数部分。然后,用m乘以这个值,再取结果的底。散列函数如下:h(k) = m(kA mod 1)。

(3)全域散列

  给定一组散列函数H,每次进行散列时候从H中随机的选择一个散列函数h,使得h独立于要存储的关键字。全域散列函数类的平均性能是比较好的。

4、碰撞处理

   通常有两类方法处理碰撞:开放寻址(Open Addressing)法和链接(Chaining)法。前者是将所有结点均存放在散列表T[0..m-1]中;后者通常是把散列到同一槽中的所有元素放在一个链表中,而将此链表的头指针放在散列表T[0..m-1]中。

(1)开放寻址法

  所有的元素都在散列表中,每一个表项或包含动态集合的一个元素,或包含NIL。这种方法中散列表可能被填满,以致于不能插入任何新的元素。在开放寻址法中,当要插入一个元素时,可以连续地检查或探测散列表的各项,直到有一个空槽来放置待插入的关键字为止。有三种技术用于开放寻址法:线性探测、二次探测以及双重探测。

<1>线性探测

  给定一个普通的散列函数h':U —>{0,1,.....,m-1},线性探测方法采用的散列函数为:h(k,i) = (h'(k)+i)mod m,i=0,1,....,m-1  

     探测时从i=0开始,首先探查T[h'(k)],然后依次探测T[h'(k)+1],…,直到T[h'(k)+m-1],此后又循环到T[0],T[1],…,直到探测到T[h'(k)-1]为止。探测过程终止于三种情况: 
  (1)若当前探测的单元为空,则表示查找失败(若是插入则将key写入其中); 
  (2)若当前探测的单元中含有key,则查找成功,但对于插入意味着失败; 
  (3)若探测到T[h'(k)-1]时仍未发现空单元也未找到key,则无论是查找还是插入均意味着失败(此时表满)。

  线性探测方法较容易实现,但是存在一次群集问题,即连续被占用的槽的序列变的越来越长。采用例子进行说明线性探测过程,已知一组关键字为(26,36,41,38,44,15,68,12,6,51),用除余法构造散列函数,初始情况如下图所示:

散列过程如下图所示:

<2>二次探测

   二次探测法的探查序列是:h(k,i) =(h'(k)+i*i)%m ,0≤i≤m-1 。初次的探测位置为T[h'(k)],后序的探测位置在次基础上加一个偏移量,该偏移量以二次的方式依赖于i。该方法的缺陷是不易探查到整个散列空间。

<3>双重散列

  该方法是开放寻址的最好方法之一,因为其产生的排列具有随机选择的排列的许多特性。采用的散列函数为:h(k,i)=(h1(k)+ih2(k)) mod m。其中h1和h2为辅助散列函数。初始探测位置为T[h1(k)],后续的探测位置在此基础上加上偏移量h2(k)模m。

 (2)链接法

  将所有关键字为同义词的结点链接在同一个链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。

  举例说明链接法的执行过程,设有一组关键字为(26,36,41,38,44,15,68,12,6,51),用除余法构造散列函数,初始情况如下图所示:

最终结果如下图所示:

 5、字符串散列

  通常都是将元素的key转换为数字进行散列,如果key本身就是整数,那么散列函数可以采用keymod tablesize(要保证tablesize是质数)。而在实际工作中经常用字符串作为关键字,例如身姓名、职位等等。这个时候需要设计一个好的散列函数进程处理关键字为字符串的元素。参考《数据结构与算法分析》第5章,有以下几种处理方法:

方法1:将字符串的所有的字符的ASCII码值进行相加,将所得和作为元素的关键字。设计的散列函数如下所示:

复制代码
1 int hash(const string& key,int tablesize)
2 {
3     int hashVal = 0;
4     for(int i=0;i<key.length();i++)
5            hashVal += key[i];
6     return hashVal % tableSize;
7 }
复制代码

  此方法的缺点是不能有效的分布元素,例如假设关键字是有8个字母构成的字符串,散列表的长度为10007。字母最大的ASCII码为127,按照方法1可得到关键字对应的最大数值为127×8=1016,也就是说通过散列函数映射时只能映射到散列表的槽0-1016之间,这样导致大部分槽没有用到,分布不均匀,从而效率低下。

方法2:假设关键字至少有三个字母构成,散列函数只是取前三个字母进行散列。设计的散列函数如下所示:

1 int hash(const string& key,int tablesize)
2 {
3         //27 represents the number of letters plus the blank
4         return (key[0]+27*key[1]+729*key[2])%tablesize;
5 }

  该方法只是取字符串的前三个字符的ASCII码进行散列,最大的得到的数值是2851,如果散列的长度为10007,那么只有28%的空间被用到,大部分空间没有用到。因此如果散列表太大,就不太适用。

方法3:借助Horner's 规则,构造一个质数(通常是37)的多项式,(非常的巧妙,不知道为何是37)。计算公式为:key[keysize-i-1]37^i,0<=i<keysize求和。设计的散列函数如下所示:

复制代码
 1 int hash(const string & key,int tablesize)
 2 {
 3         int hashVal = 0;
 4         for(int i =0;i<key.length();i++)
 5             hashVal = 37*hashVal + key[i];
 6         hashVal %= tableSize;
 7         if(hashVal<0)  //计算的hashVal溢出
8 hashVal += tableSize; 9 return hashVal; 10 }
复制代码

  该方法存在的问题是如果字符串关键字比较长,散列函数的计算过程就变长,有可能导致计算的hashVal溢出。针对这种情况可以采取字符串的部分字符进行计算,例如计算偶数或者奇数位的字符。

6、再散列(rehashing)

  如果散列表满了,再往散列表中插入新的元素时候就会失败。这个时候可以通过创建另外一个散列表,使得新的散列表的长度是当前散列表的2倍多一些,重新计算各个元素的hash值,插入到新的散列表中。再散列的问题是在什么时候进行最好,有三种情况可以判断是否该进行再散列:

(1)当散列表将快要满了,给定一个范围,例如散列被中已经被用到了80%,这个时候进行再散列。

(2)当插入一个新元素失败时候,进行再散列。

(3)根据装载因子(存放n个元素的、具有m个槽位的散列表T,装载因子α=n/m,即每个链子中的平均存储的元素数目)进行判断,当装载因子达到一定的阈值时候,进行在散列。

  在采用链接法处理碰撞问题时,采用第三种方法进行在散列效率最好。

7、实例练习

  看完书后,有一股想把hash表实现的冲动。在此设计的散列表针对的是关键字为字符串的元素,采用字符串散列函数方法3进行设计散列函数,采用链接方法处理碰撞,然后采用根据装载因子(指定为1,同时将n个元素映射到一个链表上,即n==m时候)进行再散列。采用C++,借助vector和list,设计的hash表框架如下:

复制代码
 1 template <class T>
 2 class HashTable
 3 {
 4 public:
 5     HashTable(int size = 101);
 6     int insert(const T& x);
 7     int remove(const T& x);
 8     int contains(const T& x);
 9     void make_empty();
10     void display()const;
11 private:
12     vector<list<T> > lists;
13     int currentSize;//当前散列表中元素的个数
14     int hash(const string& key);
15     int myhash(const T& x);
16     void rehash();
17 };
复制代码

实现的完整程序如下所示:

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;

int nextPrime(const int n);

template <class T>
class HashTable
{
public:
    HashTable(int size = 101);
    int insert(const T& x);
    int remove(const T& x);
    int contains(const T& x);
    void make_empty();
    void display()const;
private:
    vector<list<T> > lists;
    int currentSize;
    int hash(const string& key);
    int myhash(const T& x);
    void rehash();
};

template <class T>
HashTable<T>::HashTable(int size)
{
    lists = vector<list<T> >(size);
    currentSize = 0;
}

template <class T>
int HashTable<T>::hash(const string& key)
{
    int hashVal = 0;
    int tableSize = lists.size();
    for(int i=0;i<key.length();i++)
        hashVal = 37*hashVal+key[i];
    hashVal %= tableSize;
    if(hashVal < 0)
        hashVal += tableSize;
    return hashVal;
}

template <class T>
int HashTable<T>:: myhash(const T& x)
{
    string key = x.getName();
    return hash(key);
}
template <class T>
int HashTable<T>::insert(const T& x)
{
    list<T> &whichlist = lists[myhash(x)];
    if(find(whichlist.begin(),whichlist.end(),x) != whichlist.end())
        return 0;
    whichlist.push_back(x);
    currentSize = currentSize + 1;
    if(currentSize > lists.size())
        rehash();
    return 1;
}

template <class T>
int HashTable<T>::remove(const T& x)
{

    typename std::list<T>::iterator iter;
    list<T> &whichlist = lists[myhash(x)];
    iter = find(whichlist.begin(),whichlist.end(),x);
    if( iter != whichlist.end())
    {
          whichlist.erase(iter);
          currentSize--;
          return 1;
    }
    return 0;
}

template <class T>
int HashTable<T>::contains(const T& x)
{
    list<T> whichlist;
    typename std::list<T>::iterator iter;
    whichlist = lists[myhash(x)];
    iter = find(whichlist.begin(),whichlist.end(),x);
    if( iter != whichlist.end())
          return 1;
    return 0;
}

template <class T>
void HashTable<T>::make_empty()
{
    for(int i=0;i<lists.size();i++)
        lists[i].clear();
    currentSize = 0;
    return 0;
}

template <class T>
void HashTable<T>::rehash()
{
    vector<list<T> > oldLists = lists;
    lists.resize(nextPrime(2*lists.size()));
    for(int i=0;i<lists.size();i++)
        lists[i].clear();
    currentSize = 0;
    for(int i=0;i<oldLists.size();i++)
    {
        typename std::list<T>::iterator iter = oldLists[i].begin();
        while(iter != oldLists[i].end())
            insert(*iter++);
    }
}
template <class T>
void HashTable<T>::display()const
{
    for(int i=0;i<lists.size();i++)
    {
        cout<<i<<": ";
        typename std::list<T>::const_iterator iter = lists[i].begin();
        while(iter != lists[i].end())
        {
            cout<<*iter<<" ";
            ++iter;
        }
        cout<<endl;
    }
}
int nextPrime(const int n)
{
    int ret,i;
    ret = n;
    while(1)
    {
        int flag = 1;
        for(i=2;i<sqrt(ret);i++)
            if(ret % i == 0)
            {
                flag = 0;
                break;
            }
        if(flag == 1)
            break;
        else
        {
            ret = ret +1;
            continue;
        }
    }
    return ret;
}

class Employee
{
public:
    Employee(){}
    Employee(const string n,int s=0):name(n),salary(s){ }
    const string & getName()const  { return name; }
    bool operator == (const Employee &rhs) const
    {
        return getName() == rhs.getName();
    }
    bool operator != (const Employee &rhs) const
    {
        return !(*this == rhs);
    }
    friend ostream& operator <<(ostream& out,const Employee& e)
    {
        out<<"("<<e.name<<","<<e.salary<<") ";
        return out;
    }
private:
    string name;
    int salary;
};

int main()
{
    Employee e1("Tom",6000);
    Employee e2("Anker",7000);
    Employee e3("Jermey",8000);
    Employee e4("Lucy",7500);
    HashTable<Employee> emp_table(13);

    emp_table.insert(e1);
    emp_table.insert(e2);
    emp_table.insert(e3);
    emp_table.insert(e4);

    cout<<"Hash table is: "<<endl;
    emp_table.display();
    if(emp_table.contains(e4) == 1)
        cout<<"Tom is exist in hash table"<<endl;
    if(emp_table.remove(e1) == 1)
          cout<<"Removing Tom form the hash table successfully"<<endl;
    if(emp_table.contains(e1) == 1)
        cout<<"Tom is exist in hash table"<<endl;
    else
        cout<<"Tom is not exist in hash table"<<endl;
    //emp_table.display();
    exit(0);
}


程序测试结果如下所示:







MIT 6.006算法导论课程:系统学习与本地实践指南 数据结构与算法是计算机科学的核心基础,它们定义了如何高效地组织和处理数据。其原理在于通过特定的数据模型(如数组、链表、树、图)和计算步骤(算法),在有限资源内解决计算问题。掌握这些基础概念的技术价值巨大,不仅能提升代码效率与系统性能,更是理解复杂软件设计与优化问题的关键。在实际应用中,它们是搜索引擎排序、社交网络推荐、路径导航等众多技术的基石。本文聚焦于麻省理工学院(MIT)经典的《6.006 算法导论》课程,提供了一个将系统理论学习与动手编程实践相结合的本地化学习方案。通过组织课程视频、讲义和编程挑战等资 阅读详情

相关推荐

MIT-6.s081-OS Lab: locks

本实验主要是重新设计锁,增加并行性 分别是为kmem和bcache设计 memory allocator https://blog.csdn.net/RedemptionC/article/details/108127655这里对kalloc做了一点基本的分析 他的设计是所有的cpu(NCPU为8,但是实际是3个)都使用一个freelist,这样lock contention就很高 要做的改进是为每一个cpu设置一个free list,每个freelist一个锁,这样不同的cpu就能独立的进行分.

RedemptionC的博客 3407

《算法导论》读书笔记之第11章 散列表

摘要:   本章介绍了散列表(hash table)的概念、散列函数的设计及散列冲突的处理。散列表类似与字典的目录,查找的元素都有一个key与之对应,在实践当中,散列技术的效率是很高的,合理的设计散函数和冲突处理方法,可以使得在散列表中查找一个元素的期望时间为O(1)。散列表是普通数组概念的推广,在散列表中,不是直接把关键字用作数组下标,而是根据关键字通过散列函数计算出来的。书中介绍散列表非常注...

weixin_34214500的博客 483

用二次探测再散列法解决冲突建立哈希表并查找

输入(从文件中读取):   Data.txt   每行:编号,权重   (1)建立数组,从 “Data.txt” 中读取编号和权重;   (2)从键盘读入待查找的权重数值,以除留余数法为哈希函数,二次探测再散列法解决冲突建立哈希表,基于哈希算法从数组中查找相应的记录,计算相应的查找时间,并在屏幕上输出显示。(提示:当前计算机时间 函数 C\C++中参见GetTickCount)。   (3)从键盘读入待查找的权重数值,以顺序查找算法从数组中查找相应的记录。计算相应的查找时间,并在屏幕上输出显示。   (4)在实验报告(格式已给出)中记录由(2)和(3)查找同一个数时的实验结果。

《数据结构》_PTA_数据结构作业7:查找

判断题 1-1 在散列表中,所谓同义词就是具有相同散列地址的两个元素 T 1-2 在散列表中,所谓同义词就是被不同散列函数映射到同一地址的两个元素。 F 1-3 若用平方探测法解决冲突,则插入新元素时,若散列表容量为质数,插入就一定可以成功。 F 1-4 将M个元素存入用长度为S的数组表示的散列表,则该表的装填因子为M/S。 T 1-5 在散列中,函数“插入”和“查找”具有同样的时间复杂度。 T 1-6 即使把2个元素散列到有100个单元的表中,仍然有可能发生冲突。 T 1-7 在检索一个单词时,用哈希算法

国家一级致癌物 8660

Difftest踩坑笔记(一)

Difftest是差分测试框架,有什么用呢?高性能CPU的设计需要做复杂的测试,传统的测试方法运行较慢,而且出了问题很难定位,Difftest就是为了解决这两个问题。

http://www.icfgblog.com/ 4195

Silvaco Atlas入门指南——从零开始掌握半导体器件仿真

本文为Silvaco Atlas半导体器件仿真软件的入门指南,详细介绍了从零开始掌握该工具的核心流程。内容涵盖Atlas的基本概念、仿真流程、核心文件(输入、结构、输出文件)、交互式与批处理两种操作模式,并逐步演示了如何从定义网格、划分区域、设置电极到进行掺杂、选择物理模型并求解,最终获取和分析器件特性的完整步骤。旨在帮助微电子相关专业人员和工程师快速上手这一强大的“数字实验室”,提升半导体器件设计与研发效率。

weixin_29306317的博客 398

hash冲突及解决方法(平均查找长度)

一、什么是hash冲突? 假设hash表的大小为9(即有9个槽),现在要把一串数据存到表里:5,28,19,15,20,33,12,17,10 简单计算一下:hash(5)=5, 所以数据5应该放在hash表的第5个槽里;hash(28)=1,所以数据28应该放在hash表的第1个槽里;hash(19)=1,也就是说,数据19也应该放在hash表的第1个槽里——于是就造成了碰撞(也称为冲突,collision)。 二、Hash冲突解决方法: 1.开放定址法(再散列法): 基本思想:当关键字key的哈希地址p

qq_41475583的博客 1万+

锐浪报表 Grid++Report 子报表 Delphi

锐浪报表 Grid++Report 是国内较好的报表控件,本人一直使用它,做出很多特色的报表。可以说你的报表设计出来,它基本上能做出来。 但是它帮助文件上,说的不深,近期因为要做一个有关社工的报表,大表中,嵌套2个子表。可以说,一个表到三个数据集,分别打印。 在认真看了帮助的基础上,网上探索,基本无此类,特别是Delphi的帮助,几乎为零。经二天摸索,做出来了。 一、设计思路 1、大表和二个小表,可以设计为三个表,分别使用三个GridppReport控件,取名...

lyhoo163的专栏 9071

散列表(四):冲突处理的方法之开地址法(二次探测再散列的实现)

前面的文章分析了开地址法的其中一种:线性探测再散列,这篇文章来讲开地址法的第二种:二次探测再散列 (二)、二次探测再散列 为改善“堆积”问题,减少为完成搜索所需的平均探查次数,可使用二次探测法。 通过某一个散列函数对表项的关键码 x 进行计算,得到桶号,它是一个非负整数。 若设表的长度为TableSize = 23,则在线性探测再散列举的例子中利用二次探查法所得到的散列结果如图...

winform采集 6876

手机抽奖页面代码html,html5大转盘抽奖支持手机转盘抽奖代码

特效描述:html5 大转盘抽奖 支持手机 转盘抽奖代码。html5转盘,html5抽奖,手机转盘,手机抽奖,手机转盘抽奖,可配置奖品抽奖。代码结构1. 引入CSS2. 引入JS3. HTML代码大转盘活动var turnplate={restaraunts:[],//大转盘奖品名称colors:[],//大转盘奖品区块对应背景颜色outsideRadius:192,//大转盘外圆的半径textR...

weixin_39628551的博客 1860

R语言实战:5分钟搞定相加交互效应分析(逻辑回归/Cox回归/GLMM/GEE全支持)

本文详细介绍了如何利用R语言中的`scitable`包快速实现相加交互效应分析,支持逻辑回归、Cox回归、GLMM和GEE等多种模型。通过自动化计算RERI、AP和SI等指标,大幅提升临床研究和流行病学分析的效率与准确性,特别适合处理多因素联合作用的复杂场景。

fanta的博客 669

MIT算法导论公开课之课程笔记

MIT算法导论公开课之第1课 课程简介及算法分析 MIT算法导论公开课之第2课 渐进符号、递归及解法 MIT算法导论公开课之第3课 分治法 MIT算法导论公开课之第4课 快排及随机化算法 MIT算法导论公开课之第5课 线性时间排序 MIT算法导论公开课之第6课 顺序统计、中值 MIT算法导论公开课之第7课 哈希表 MIT算法导论公开课之第8课 全域哈希和完全哈希 MIT算法导论公开...

rye_whiskey的博客 1万+

微信8.0所有URL Scheme大全:隐藏入口与使用场景详解 内附195条微信URL Scheme

URL Scheme 类似于一个协议地址,它的作用和网页的https://差不多,只不过它不是打开网页,而是直接跳转到微信App内部的功能页面。→ 打开微信扫一扫→ 打开零钱通详情→ 打开微信面对面支付微信功能的“快捷指令”。整理下来,微信的 URL Scheme 基本覆盖了支付、联系人、扫一扫、小程序、运动设备等多个入口。对开发者来说,这些 Scheme 就像是隐藏的快捷通道,在某些场景(比如支付唤起、活动红包、客服入口)能大大简化流程。微信对外部调用限制非常严格。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 2166

《算法导论》知识点总结

《算法导论》的学习路线: 1.MIT网易公开课《算法导论》,授课老师也是《算法导论》书的作者之一。讲解的内容几乎围绕书籍,不过其中跳跃表和自组织表书中没有,并行算法和缓存参数无关算法还没看。 2.课程配套的笔记,csdn博客:MIT算法导论公开课之课程笔记,可提前看一遍熟悉一下课程内容,这样看视频有重点。 3.看《算法导论》书籍,主要看了前半部分,其中高级数据结构和算法问题选编还没看。 =====...

changfei_1995的博客 1万+

华为OD机试真题2025双机位A卷 C语言 实现【采购订单】

在一个采购系统中,采购申请(PR)需要经过审批后才能生成采购订单(PO)。每个PR包含商品的单价(假设相同商品的单价一定是一样的)及数量信息。系统要求对商品进行分类处理:单价高于100元的商品需要单独处理,单价低于或等于100元的相同商品可以合并到同一采购订单PO中。针对单价低于100的小额订单,如果量大可以打折购买。

MISAYAONE的博客 350

ISMN数据处理

可批量下载,十分方便

m0_46268055的博客 1214

MIT 6.006算法导论:从数据结构到动态规划的完整学习指南

算法与数据结构是计算机科学的核心基础,它们通过定义数据组织方式和问题求解步骤,构建了软件系统的效率基石。其原理在于利用时间与空间复杂度的权衡,通过渐进分析(如大O表示法)评估不同策略的性能。掌握这些基础概念的技术价值在于,它能培养开发者抽象问题、设计高效解决方案的系统性思维,而不仅仅是编写代码。这种能力在搜索引擎优化、数据库索引、网络路由等应用场景中至关重要,直接决定了系统的响应速度与资源利用率。本文聚焦于MIT 6.006这门经典课程,它体系化地讲解了从哈希表、二叉堆等基础数据结构到动态规划、图算法等高级

weixin_30824599的博客 340

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

算法导论中英文版下载

算法导论:  学过计算机的都知道,这本书可以说是全世界最权威的算法课程的大学课本了,基本上全世界的名牌大学用的教材都是它。这本书一共四位作者,Thomas H. Cormen,Charles E. Leiserson 和 Ronald L.Rivest 是来自 MIT 的教授,Clifford Stein 是MIT出来的博士,现在哥伦比亚大学做教授,四人姓氏的首字母联在一起即是此书的英文简称(

首席技术执行官CTO(Chief Technology Officer)|技术总监--邓斌博客 1万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

MIT算法导论实战:排序、哈希、图论与动态规划核心精讲

算法是计算机科学中解决问题的一系列明确指令,其核心价值在于通过高效的计算逻辑,在有限资源内完成任务。从原理上看,算法通过特定的数据结构和计算步骤,将复杂问题分解为可处理的子问题,其效率通常用时间复杂度和空间复杂度衡量。在技术价值层面,掌握核心算法是构建高性能系统的基石,无论是数据库索引、搜索引擎还是推荐系统,其底层都依赖于高效的算法实现。应用场景广泛覆盖数据处理、网络分析、资源优化等领域。本文聚焦于排序算法、哈希表、图算法和动态规划这四大核心模块,通过结合《算法导论》的经典理论与高频面试题,提供从基础概念到

weixin_29210759的博客 337

《算法导论》中算法的部分实现[GitHub]

Introduction to Algorithms介绍GitHub地址 阅读《算法导论》过程中实现的部分算法(Java版)。已实现的算法(后续持续更新。。。) chapter 2插入排序 归并排序 chapter 3最大子数组问题 chapter 6堆 数据结构 堆排序 chapter 7快速排序 快速排序(随机增强版) chapter 8计数排序 桶排序 基数排序 chapter 9期望为线性

INotWant的博客 1759

Mit6.006-problemSet03

按顺序插入整数keys A=[47, 61, 36, 52, 56, 33, 92]到尺寸为7的哈希表中,使用哈希函数hk10k4mod7。哈希表的每个插槽,存储一个key(哈希到该插槽)的链表,后插入的追加到链表最后。所有key插入完成后,画一个哈希表的图。

code a better life~ 1万+

算法导论-散列表-MIT6.006 Lecture 8

Abstract Data Type(ADT)用一个key来保存对应的item内容 有几个操作: 1.insert 2.delelte 3.search key

michael_llh的博客 356

【转载】算法导论学习笔记

参考出处 链接: [https://blog.csdn.net/rye_whiskey/article/details/82216352] 作为记录我的算法之路的起点!!!! 简介 此算法导论课程为麻省理工学院的官方课程录像,以《算法导论》为教材,授课老师为这本书的作者Charles Leiserso...

qq_35982713的博客 232

MIT 6s081 lab8:locks

优化思路:建立一个blockno到buf的hash table,为每个桶单独加锁(降低锁的粒度),当两个进程同时访问的块哈希到同一个桶时,才会发生竞争,当桶中的空闲Buf不足时,从其他桶中获取Buf,并采用时间戳(全局ticks)的优化方式替换原本的双向链表。2、从0开始遍历其他桶,在每个桶中查找引用计数为0的最近最久没有使用的Buf,如果有,则先将这个buf从原本的桶中删去,再将这个buf添加到桶key,并修改buf的内容,返回。1、首先为每个CPU分配一个全局的内存空闲列表和锁,并完成初始化。

厚积而薄发 1062

算法导论11.4开放寻址法 练习总结

11.4-1 考虑将关键字 10、22、31、4、15、28、17、88、59用开放寻址法插入到一个长度为 m = 11 的散列表中,辅助散列函数为 h'( k ) = k mod m。试说明分别用线性探查,二次探查(c1 = 1,c2 = 3) 和双重散列h2( k ) = 1 + ( k mod (m-1))将这些关键字插入散列表的过程。 ANSWER: 序号 线性探查

chan15的博客 3403

MIT 6.S081 2020 Lab8 locks 个人全流程

在本实验中,你将学习如何重新设计代码以提高并行性。在多核计算机上,并行性差的一个常见表现是高锁竞争。为了减少竞争、提升并行性,通常需要同时修改数据结构和加锁策略。你将在本实验中对 xv6 的内存分配器和块缓存进行相关改进。记得切换分支到 lock。

EQUINOX1的博客 967
上一篇: MIT:算法导论——7.1.基本数据结构_栈、队列、链表、有根树
下一篇: MIT:算法导论——7.2.二叉树
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值