HashTable哈希表练习查找插入删除217、349 、202、287、290、532、205、128

底层实现

HashSet是通过HasMap来实现的,HashMap的输入参数有Key、Value两个组成,在实现HashSet的时候,保持HashMap的Value为常量,相当于在HashMap中只对Key对象进行处理。

HashMap的底层是一个数组结构,数组中的每一项对应了一个链表,这种结构称“链表散列”的数据结构,即数组和链表的结合体;也叫散列表、哈希表。

put在放入数据时,如果放入数据的key已经存在与Map中,最后放入的数据会覆盖之前存在的数据,
而putIfAbsent在放入数据时,如果存在重复的key,那么putIfAbsent不会放入值。

128. 最长连续序列

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为 O(n)。

示例:

输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。
题解:
1)sort 比较
2)hashset
3)动态规划
sloution:
1)

class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums.length < 1) return 0;
        if(nums.length == 1) return 1;
        Arrays.sort(nums);

        int count = 1;
        int result = 1;
        for(int i = 1; i < nums.length; i++){
            if(nums[i] - nums[i-1] == 1){
                result++;
            }
            else if(nums[i] - nums[i-1] == 0) continue;
            else{
                result = 1;
            }
            count = Math.max(result,count);
        }
        return count;
    }
}
class Solution {
    public int longestConsecutive(int[] nums) {
        // 将 nums 使用hash表存储
        Set<Integer> num_set = new HashSet();
        for (int num : nums) {
            num_set.add(num);
        }
        // 最终的连续元素个数
        int longestStreak = 0;
        // 迭代hash表中的元素, 若表中不存在 num-1的数, 便开始向后查找与当前元素连续的数
        // 此目的是为了避免多次遍历一组连续的数
        for (int num : num_set) {
            if (!num_set.contains(num - 1)) {
                int currentNum = num;   // 当前数
                int currentStreak = 1;  // 连续数个数计数器
                // 若存在连续的数,则计数器+1
                while (num_set.contains(++currentNum) && (++currentStreak >0));

                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }
        return longestStreak;
    }
}

从0到nums.length遍历每个元素。
对于每个元素nums[i]我们都dfs的搜索nums中比它小1的元素,每搜索到一个,len+1。直到所有不到下一个为止,保存当前最大len为候选答案。

class Solution {
    Set<Integer> checkInNums = new HashSet<>(); 
    //初始化Set用来判断数字是否在nums中
    public int longestConsecutive(int[] nums) {
        if(nums.length == 0)
            return 0;
        for(int i = 0; i < nums.length; i++)
            checkInNums.add(nums[i]);
        int res = 0;
        for(int i = 0; i < nums.length; i++)
            res = Math.max(res, dfs(nums[i]));
        return res;
    }
    //返回小于等于当前数的最大连续序列的长度
    public int dfs(int cur) {
        int len = 1;    //当前长度为1
        if(checkInNums.contains(cur-1))
            len += dfs(cur-1);
        return len;
    }
}

我们很容易发现,dfs形成的递归树中有重复子结构。
比如[100, 4, 200, 1, 3, 2]
我们遍历4时,已经将dfs(3),dfs(2),dfs(1)的情况给遍历过了,因此,再往后递归的时候,我们应该剪枝。
于是添加备忘录Map。

class Solution {
    Set<Integer> checkInNums = new HashSet<>(); //初始化Set用来判断数字是否在nums中
    Map<Integer, Integer> m = new HashMap<>();  //备忘录
    public int longestConsecutive(int[] nums) {
        if(nums.length == 0)
            return 0;
        for(int i = 0; i < nums.length; i++)
            checkInNums.add(nums[i]);
        int res = 0;
        for(int i = 0; i < nums.length; i++)
            res = Math.max(res, dfs(nums[i]));
        return res;
    }
    //返回小于等于当前数的最大连续序列的长度
    public int dfs(int cur) {
        if(m.containsKey(cur))
            return m.get(cur);
        int len = 1;    //当前长度为1
        if(checkInNums.contains(cur-1))
            len += dfs(cur-1);
        m.put(cur, len);
        return len;
    }
}

作者:robinzhu-z

532. 数组中的K-diff数对

给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k.

示例 1:

输入: [3, 1, 4, 1, 5], k = 2
输出: 2
解释: 数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。
尽管数组中有两个1,但我们只应返回不同的数对的数量。
示例 2:

输入:[1, 2, 3, 4, 5], k = 1
输出: 4
解释: 数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5)。
示例 3:

输入: [1, 3, 1, 5, 4], k = 0
输出: 1
解释: 数组中只有一个 0-diff 数对,(1, 1)。

class Solution {
    public int findPairs(int[] nums, int k) {
        HashSet<Object> set = new HashSet<>();
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = i+1; j < nums.length ; j++) {
                if (Math.abs(nums[i] - nums[j])==k){
                    set.add(nums[i] + nums[j]);
                }
            }
        }
        return set.size();
    }
}

205. 同构字符串(类290)

给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。

示例 1:
输入: s = “egg”, t = “add”
输出: true
示例 2:
输入: s = “foo”, t = “bar”
输出: false
示例 3:
输入: s = “paper”, t = “title”
输出: true
题解:

sloution:

class Solution {
    public boolean isIsomorphic(String s, String t) {
     if(s==null||t==null) return false;
     if(s.length()!=t.length()) return false;

     HashMap<Character,Character> map=new HashMap<>();
     for(int i=0;i<s.length();i++){
     //key不存在
         if(!map.containsKey(s.charAt(i))){
         //value对应就false
             if(map.containsValue(t.charAt(i))){
                 return false;
             }
             //存入
             map.put(s.charAt(i),t.charAt(i));
         }else{
         //key存在但不对应(一个key两个value的情况)
             if(map.get(s.charAt(i))!=t.charAt(i)){
                 return false;
             }
         }
     }
     return true;
    }
}

290. 单词规律

给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。

示例1:
输入: pattern = “abba”, str = “dog cat cat dog”
输出: true
示例 2:
输入:pattern = “abba”, str = “dog cat cat fish”
输出: false
示例 3:
输入: pattern = “aaaa”, str = “dog cat cat dog”
输出: false
示例 4:
输入: pattern = “abba”, str = “dog dog dog dog”
输出: false

Java split() 分割字符串,返回一个字符串数组。
Java charAt() 返回指定索引处的字符。索引范围为从 0 到 length() - 1。
Java equals() 方法用于将字符串与指定的对象比较。
public boolean equals(Object anObject)

class Solution {
    public boolean wordPattern(String pattern, String str) {
     if(pattern==null||str==null) return false;
     String[] s =str.split(" ");
     if(pattern.length()!=s.length) return false;

     HashMap<Character,String> map= new HashMap<>();
     for(int i=0;i<pattern.length();i++){
         char key=pattern.charAt(i);
         if(map.containsKey(key)){//containsValve
            if(!map.get(key).equals(s[i]))  return false;
         }     
         else{
             if(map.containsValue(s[i])) return false;
             else map.put(key,s[i]);
             }
         }
         return true;
    }
}

202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。如果 n 是快乐数就返回 True ;不是,则返回 False 。

示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

题解:
1)暴力
2)快慢指针 最优
3)hash/set :判断happu number重要的一点是,当出现循环节时,也就是平方和等于前面出现过的数字时ok,所以用哈希表来存储之前算过的数,每次计算后先判断是不是1,如果不是再判断一下之前是否出现过这个数。

sloution:
2)见双指针
3)set

class Solution {
    private int calcu(int n){
         int sum = 0;
        while (n != 0){
            sum += n % 10 * (n % 10);
            n /= 10;
        }
        return sum;
    }

    public boolean isHappy(int n) {
    Set<Integer> set=new HashSet<>();
    set.add(n);
    while(true){
    n = calcu(n);
            if(n == 1)
                return true;
            if(set.contains(n))
                return false;
            set.add(n);
    }
    }
}

287. 寻找重复数

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3

不能更改原数组(假设数组是只读的);
只能使用额外的 O(1) 的空间。

题解:
1)hashset 判重 不满2
2)排序 相邻判重 不满1
3)二分查找
4)快慢指针
3、4非常规 见于题解

sloution:
1)set
3、4)见双指针集

class Solution {
    public int findDuplicate(int[] nums) {
        Set<Integer> set =new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
        if(!set.add(nums[i]))  return nums[i];
        }
        return -1;
    }
}

217. 存在重复元素

给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
题解:
1)利用set性质
2)一遍哈希遍历
3)排序后判断相邻元素是否相同
sloution:

1class Solution {
    public boolean containsDuplicate(int[] nums) {
    Set<Integer> res = new HashSet<Integer>();
    for(int i:nums) res.add(i);
    return res.size()< nums.length;
    }
}


2class Solution {
    public boolean containsDuplicate(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        int length = nums.length;

        for (int i = 0; i < length; i++){
            if (map.containsKey(nums[i])){
                return true;
            }
            map.put(nums[i], i);
        }
        return false;
    }
}
作者:lxiaocode

3class Solution {
    public boolean containsDuplicate(int[] nums) {
        int n=nums.length;
        Arrays.sort(nums);//排序复杂度O(nlogn)
        for(int i=0;i<n-1;i++){
            if(nums[i]==nums[i+1])
                return true;
        }
        return false;
    }
}
作者:agasar

349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

题解:
1)利用HashSet的性质:无序、元素不重复。先对一组数组排除重复元素,再利用Set contains方法与另一组进行比较

sloution:
1)两set

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet<Integer> set1=new HashSet<Integer>();
        HashSet<Integer> set2=new HashSet<Integer>();
        for(int i:nums1) set1.add(i);
        for(int i:nums2){
            if(set1.contains(i)){
                set2.add(i);
            }
        }
        int []new_num = new int[set2.size()];
        int j = 0;
        for(int i:set2){
            new_num[j] = i;
            j++;
        }
        return new_num;
    }
}

Java 集合类中的 Set.contains()方法
判断 Set 集合是否包含指定的对象。
该方法返回值为 boolean 类型,如果 Set 集合包含指定的对象,则返回 true,否则返回 false。

foreach的语句格式:
for(元素类型t 元素变量x : 遍历对象obj){
引用了x的java语句; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、 设计课题:哈希表设计 二、 需求分析: 课题的目的和任务:根据数据元素的关键字和哈希函数建立哈希表并初始化哈希表,用开放定址法处理冲突,按屏幕输出的功能表选择所需的功能实现哈希表对数据元素的插入显示查找删除。 初始化哈希表时把elem[MAXSIZE]、elemflag[MAXSIZE]和count分别置0。创建哈希表时按哈希函数创建哈希表,输入数据元素的关键字时,以“0”结束输入且要求关键字为正整数,数据元素个数不允许超过表长MAXSIZE。 输出的形式:根据所选择的哈希表的功能输出相应提示语句和正确结果。 程序的功能:将一组个数不超过哈希表长度的数据元素,按其关键字和哈希函数存入哈希表中,如果产生冲突用开放定址法处理并找出相应的地址。能实现哈希表对数据元素的插入显示查找删除。 测试数据: maxsize=10 哈希函数:H(key)=key%7 处理冲突方法: 开放定址法 Hi=(H(key)+di)%13 i=1,2,3,…,9 三、实验概要设计: ADT HashTable { 数据对象:D1={ai|ai∈elem[MAXSIZE],i=0,1,2,…,0≦n≦MAXSIZE } elem [MAXSIZE]是哈希表中关键字的集合,MAXSIZE为哈希表长。} D2={ai|ai∈elemflag[MAXSIZE]是哈希表中有无关键字的标志的集合,MAXSIZE为哈希表长。} 基本操作: Hash(key) 初始条件:数据元素关键字key已存在 操作结果:根据哈希函数计算相应地址,并返回此地址。 InitialHash(HashTable &H) 初始条件:哈希表H已存在 操作结果:初始化哈希表把elem[MAXSIZE]、elemflag[MAXSIZE]和count分别置0。 SearchHash(HashTable &H,int k) 初始条件:哈希表H已存在 操作结果:在开放定址哈希表H中查找关键字为k的元素,若查找成功,并返回SUCCESS;否则返回UNSUCCESS。 InsertHash(HashTable &H,int e) 初始条件:哈希表H已存在 操作结果:查找不成功时插入数据元素e到开放定址哈希表H中,并返回SUCCESS;否则输出已有此数!插入失败!并返回UNSUCCESS。 CreateHash(HashTable &H) 操作结果:构造哈希表。 PrintHash(HashTable H) 初始条件:哈希表H已存在 操作结果:将哈希表存储状态显示输出。 DeleteHash(HashTable &H,int e) 初始条件:哈希表H已存在 操作结果:若通过哈希函数找到相应的位置并且此位置的elemflag[MAXSIZE]==1,删除相应位置的数据元素的关键字并把elemflag[MAXSIZE]==2,否则,输出无此数的提示语。 2. 本程序包括如下功能模块 哈希函数模块,冲突处理模块,哈希表初始化模块,哈希表创建模块,哈希表显示模块,按关键字查找模块,插入模块,删除模块和主程序模块。 四、基本操作的算法描述: 1.宏定义 #define MAXSIZE 10 #define SUCCESS 1 #define UNSUCCESS 0 2.数据类型、数据元素的定义 typedef struct { int elem[MAXSIZE]; int elemflag[MAXSIZE]; int count; }HashTable; 3. 哈希表的基本操作的算法描述 //初始化哈希函数 void InitialHash(HashTable &H)/*哈希表初始化*/ { int i; H.count=0; for(i=0;i<MAXSIZE;i++) { H.elem[i]=0; H.elemflag[i]=0; } } //哈希函数 int Hash(int kn) /*哈希函数H(key)=key MOD 7*/ { return (kn%7); } //查找函数 int SearchHash(HashTable &H,int k) /*查找关键字为k的元素*/ { int t,s; s=t=Hash(k); //调用哈希函数 xun: if(H.elem[t]==k&&H.elemflag[t]==1) return SUCCESS; //如果相应的地址上的数等于k,返回1 else if(H.elem[t]!=k&&H.elemflag[t]==1) { //相应地址有数但不等于k并且H.elemflag[t]==1 t=(t+1)%MAXSIZE; // 处理冲突 goto xun; } else return UNSUCCESS; //返回0 } //插入函数 int InsertHash(HashTable &H,int e)/*插入元素e*/ { int p; p=Hash(e); if(SearchHash(H,e) ) //调用查找函数,如果有此数返回1 { cout<<"已有此数!"<<endl; return UNSUCCESS; //插入是吧,返回0 } else //查找失败,返回0 { H.elemflag[p]=1; //把状态标志置1 H.elem[p]=e; //把关键字放入相应的地址 H.count++; //计数加1 return SUCCESS; //插入成功,返回1 } } //创建哈希表的函数 void CreateHash(HashTable &H)/*创建哈希表*/ { int s; int e; cout<<"请输入哈希表:(输入0结束!)"<<endl; cin>>e; while(e) { s=InsertHash(H,e); //调用插入函数 if(!s) { cout<<"此数已存在!"; cin>>e; } else cin>>e; } } //显示函数 void PrintHash(HashTable H) /*显示元素及其位置*/ { cout<<"哈希表地址:"; int i; for(i=0;i<MAXSIZE;i++) cout<<i<<" "; cout<<endl<<"关键字: "; for(i=0;i<MAXSIZE;i++) cout<<H.elem[i]<<" "; cout<<endl<<"关键字标志:"; for(i=0;i<MAXSIZE;i++) cout<<H.elemflag[i]<<" "; } //删除函数 int DeleteHash(HashTable &H,int e) /*删除元素e*/ { int i; int a=0; for(i=0;i<MAXSIZE;i++) if(H.elem[i]==e&&H.elemflag[i]==1) { H.elemflag[i]=2; H.count--; H.elem[i]=0; a++; return SUCCESS; } if(!a) { cout<<"无此数!"<<endl; return UNSUCCESS; } } //主函数 void main() { HashTable H; int m,k,p; int R; do{ cout<<endl<<"\t\t******************请选择功能********************"<<endl; cout<<"\t\t\t1.初始化哈希表"<<endl<<"\t\t\t2.创建哈希表"<<endl<<"\t\t\t3.查找" <<endl<<"\t\t\t4.插入"<<endl<<"\t\t\t5.删除"<<endl<<"\t\t\t6.输出哈希表:"<<endl<<"\t\t\t0.退出"<<endl; cout<<"\t\t************************************************"<<endl; cin>>m; switch(m) { case 1: InitialHash(H);break; case 2: CreateHash(H);break; case 3: cout<<endl<<"请输入要查找的关键字:"; cin>>k; p=SearchHash(H,k); if(p) cout<<"查找成功!"<<endl; else cout<<"查找失败!"<<endl; break; case 4: cout<<endl<<"请输入要插入的关键字:"; cin>>R; p=InsertHash(H,R); if(p) cout<<"插入成功!"<<endl; else cout<<"插入失败!"<<endl; break; case 5: cout<<"请输出删除的关键字:"; cin>>R; p=DeleteHash(H,R); if(p) cout<<"删除成功!"<<endl; else cout<<"删除失败!"<<endl; break; case 6: PrintHash(H);break; case 0: break; default: cout<<endl<<"选择错误!";break; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值