hash表和数组

哈希表基本概念和结构

hash表常用的有三种结构

  • 数组
  • set
  • table

不同哈希结构特点和使用条件

数组作为哈希表

数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。
如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

	// 简单创建一个数组进行运算就行,但是需要注意题目中给出的数组的长度,如果太长的话 就转而使用哈希set
	int[] store = new int[26];

set作为哈希表

set是一个集合,里面放的元素只能是一个key


    HashSet<String> sites = new HashSet<String>();
// 引入 HashSet 类      
import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    //创建
    HashSet<String> sites = new HashSet<String>();
    //添加
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");     // 重复的元素不会被添加
     //移除
        sites.remove("Taobao");  // 删除元素,删除成功返回 true,否则为 false
     //删除集合中所有元素可以使用 clear 方法:
      	sites.clear(); 
     //检查 hashMap 中是否存在指定的 key 对应的映射关系。
      	sites.containsKey()	
     //检查 hashMap 中是否存在指定的 value 对应的映射关系。
		sites.containsValue()
	//替换 hashMap 中是指定的 key 对应的 value。 	
		sites.replace()	
     //打印
        System.out.println(sites);
     //如果要计算 HashSet 中的元素数量可以使用 size() 方法:
     //可以使用 for-each 来迭代 HashSet 中的元素。
	    System.out.println(sites.size());  
		for (String i : sites) {
            System.out.println(i);
        }
    }
}

map作为哈希表

当需要判断和返回多个值的时候,就需要用map
map是一种<key, value>的结构
可以用key保存数值,用value在保存数值所在的下标。

当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

        // 创建 HashMap 对象 Sites
        HashMap<Integer, String> Sites = new HashMap<Integer, String>();
		// 添加键值对
        Sites.put(1, "Google");
		// 添加键值对
        Sites.put("one", "Google");
        //我们可以使用 get(key) 方法来获取 key 对应的 value:
        System.out.println(Sites.get(3));
		//我们可以使用 remove(key) 方法来删除 key 对应的键值对(key-value):
        Sites.remove(4);
		//删除所有键值对(key-value)可以使用 clear 方法:       
		Sites.clear();
		//计算大小
		System.out.println(Sites.size());
		// 输出 key 和 value
        for (Integer i : Sites.keySet()) {
            System.out.println("key: " + i + " value: " + Sites.get(i));
        }
        // 返回所有 value 值
        for(String value: Sites.values()) {
          // 输出每一个value
          System.out.print(value + ", "); 
          //对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中
          Sites.computeIfAbsent();
          //遍历 累加 value 值
          //getOrDefault()	获取指定 key 对应的 value,如果找不到 key ,则返回设置的默认值
          for(int num : nums) map.put(num, map.getOredfault(num,0) + 1);
          //遍历每一个value值
          for(Map.Entry<IntegerInteger> it : map.entrySet){};
          

自用看不懂的语法解释

			//查找map中的key值 并对于指定key值进行重新计算 如果不存在该值 就将该值 重新加入到map 中
			// k -> new ArrayList<>() 一种占位符的表示方式 因为value值 可以是多个 通过下行代码 进行添加,(mean:有一定的冲突存在,链式结构),所以 需要一个占位符 来进行占位
			//map.get(key).add(s);
            map.computeIfAbsent(key, k -> new ArrayList<>());
            map.get(key).add(s);           
	//使用优先队列来实现最小树结构
	PriorityQueue<Map.Entry<Integer, Integer>> pq = new PriorityQueue<>(
                (a, b) ->
            a.getValue() - b.getValue()
        );
        //该题 是347. Top K Frequent Elements 给定一个数列 求频率在k前的数组
        // 在使用 map进行记数操作之后 使用最小二叉树 进行维护 这样的话 就可以保证 数列一直维持在k个数组中间
        for (Map.Entry<Integer, Integer> it : map.entrySet()) {
            pq.add(it);
            if (pq.size() > k) pq.poll();
        }
        //注意map.entry --我们可以认为 Map.Entry是一个存储两个元素的元组——一个键和一个值 -- 所以遍历的时候需要使用entryset 函数 这也提供了一个遍历的方式

	List<Character> charList = new ArrayList<Character>();
	Set<Character> charSet = new HashSet<Character>();
**求数组中最长的连续数列**
因为对于时间复杂度有要求 所以不能使用sort来直接进行排序
sort 进行排序的时间复杂度是o(nlogn)
查找数列中所符合要求的数组 最方便的方法 是使用哈希表进行查询
所以 使用set建立数组
哈希表进行查询的时间复杂度 是o(n)

判断哈希表中数列是否是联系的 可以使用的前后所指向的元素进行判断
有点类似于 指针的算法 如果是开头字母的话 那数字的前指针就为空 而后指针式保持存在的

在做类似的题目的时候可以不是简单的使用定义去完成相关题目 而是辅助使用 本身数组或者是表格的排列特性进行

**判断数独矩阵的合理性**
数独矩阵 本身具有三个判断题条件
1. 矩阵的每行中不能存在 相同的数字 -- 使用哈希表存储相应的数字 如果说contains 相应的数字 就return false
2. 矩阵的每列中不能存在 相同的数字 和每行的判断条件一致
3. 矩阵的每个九宫格中不能存在 相同的数字 -- 九宫格的话 重点就是怎么确定矩阵的下标 将9个矩阵 的每一个看作是一个整体 下标的话 直接使用整除3 来进行判断
-java-
//block
        for (int i = 0; i < rows; i = i + 3) {
            for (int j = 0; j < cols; j = j + 3) {
                if (!checkBlock(i, j, board)) {
                    return false;
                }
            }
        }

        return true;
    }

    public boolean checkBlock(int idxI, int idxJ, char[][] board) {
        Set<Character> blockSet = new HashSet<>();
        int rows = idxI + 3;
        int cols = idxJ + 3;
        for (int i = idxI; i < rows; i++) {
            for (int j = idxJ; j < cols; j++) {
                if (board[i][j] == '.') {
                    continue;
                }

                if (blockSet.contains(board[i][j])) {
                    return false;
                }

                blockSet.add(board[i][j]);
            }
        }

        return true;

-python-
for r in range(9):
            for c in range(9):
                if board[r][c] == ".":
                    continue
                if (
                    board[r][c] in rows[r]
                    or board[r][c] in cols[c]
                    or board[r][c] in squares[(r // 3, c // 3)]
                ):
                    return False
                cols[c].add(board[r][c])
                rows[r].add(board[r][c])
                squares[(r // 3, c // 3)].add(board[r][c])
### 哈希数组的特点及适用场景对比 #### 特点对比 哈希数组都是常见的数据结构,但在特性上存在显著差异。 - **数组**是一个连续的内存区域,其中每个元素可以通过索引快速访问。由于其线性存储方式,数组支持随机访问,即通过下标可以立即定位到某个元素的位置[^4]。 - **哈希**则是一种基于键值对的数据结构,它通过哈希函数将键映射到特定的存储位置。理想情况下,哈希的时间复杂度接近于 O(1),这使得它非常适合用于频繁查询的操作[^5]。 #### 存储机制 - 数组的存储是顺序排列的,所有的元素都紧密地挨在一起,因此它的空间利用率较高,但如果需要动态调整大小,则可能涉及大量的复制操作。 - 哈希通常由桶(bucket)构成,每个桶负责存储一组具有相同哈希值的键值对。当发生哈希冲突时,可通过链地址法或其他策略来解决这些问题[^2]。 #### 时间复杂度 - 对于数组而言,在已知索引的情况下,读写操作均为常数时间复杂度 O(1);然而如果要按值查找某项,则需遍历整个列,最坏情况下的时间复杂度为 O(n)。 - 而对于哈希来说,只要设计良好的哈希函数并合理控制负载因子,基本操作如插入、删除以及检索都可以达到平均意义上的 O(1) 性能。 #### 空间消耗 - 数组的空间需求相对固定且紧凑,预先分配好之后不会轻易改变尺寸除非显式调用了扩容逻辑。 - 反观哈希,为了减少碰撞概率往往预留较多额外空间,并且随着条目增多还可能会触发重新散列过程,从而带来更高的内存开销。 #### 使用场合建议 - 如果应用程序主要依赖于依据整型偏移量迅速获取项目或者维持固定的集合成员关系,则应优先考虑采用数组形式示数据集。 - 当面临更复杂的关联映射需求——比如字典服务、缓存管理等领域里经常遇到的情况——此时选用具备高度灵活性特性的哈希会更加合适一些[^3]。 ```python # 示例代码展示如何创建简单的Python版hash table list(array) class HashTableExample: def __init__(self, size=8): self.size = size self.table = [[] for _ in range(self.size)] def hash_function(self, key): return sum(ord(c) for c in str(key)) % self.size def insert(self, key, value): index = self.hash_function(key) bucket = self.table[index] found = False for i, (k, v) in enumerate(bucket): if k == key: bucket[i] = (key, value) found = True break if not found: bucket.append((key, value)) example_hash_table = HashTableExample() example_hash_table.insert('apple', 50) print(example_hash_table.table) array_example = ['a','b','c'] print(array_example[1]) # 输出 'b' ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值