LeetCode 题解与知识点 1. 两数之和 Two Sum

题目链接

1. Two Sum 难度:$\color{#00965e}{easy}$

知识点

1.哈希(散列)函数

经典散列函数Times33应用非常广泛,核心算法如下:

hash(i) = hash(i-1) * 33 + str[i]

laruence大佬有一篇文章讲过:PHP中的Hash算法 - 风雪之隅

2.哈希碰撞处理方法

2.1链地址法(拉链法)(广泛使用)

即使用单向链表来处理哈希冲突的key;
php,java都是使用该方法

优点:

  • 处理冲突简单,且无堆积现象;
  • 平均查找长度短;
  • 链表中的结点是动态申请的,适合构造表不能确定长度的情况;
  • 节省空间,相对而言,拉链法的指针域可以忽略不计,因此较开放地址法更加节省空间。
  • 插入删除方便,插入结点应该在链首,删除结点比较方便,只需调整指针而不需要对其他冲突元素作调整。

Tips:如果链表过长,查询效率会从O(1),变成遍历链表,效率降低,这个时候可以使用树结构,来代替链表,提高查询效率。
(由此引出另一个知识点:二叉树二叉查找树),此处不赘述。

2.2开放寻址法
Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)

其中,m为哈希表的表长。di是产生冲突的时候的增量序列。如果di值可能为1,2,3,…m-1,称线性探测再散列。
如果di1,则每次冲突之后,向后移动1个位置,称线性探测再散列
如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,…k*k,-k*k(k<=m/2),称二次探测再散列
如果di取值可能为伪随机数列。称伪随机探测再散列

缺点:

  • 处理冲突简单,且无堆积现象;
  • 平均查找长度短;
2.3再哈希法Rehash

当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再冲突,第三位,直到不冲突为止.这种方法不易产生聚集,但增加了计算时间。

2.4建立一个公共溢出区

假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。

解法

1.暴力双循环略过不提;

2.哈希表(桶);

对于数组A,构造一个哈希,初次遍历时候就记录下值和数组下标,再遍历过程中,先判断哈希表里是否已经有target - A[i],有的话,直接返回就好了;

复杂度分析
  • 时间复杂度:O(N),其中 N是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。
  • 空间复杂度:O(N),其中 N 是数组中的元素数量。主要为哈希表的开销。

C语言没有hashTable结构,需要自行实现hashTable(知识点:如何实现hashTable插入/删除)这里使用PHP演示代码实现,其他语言类似,此处不赘述;

class Solution {

    /**
     * @param Integer[] $nums
     * @param Integer $target
     * @return Integer[]
     */
    function twoSum($nums, $target) {
        $len = count($nums);
        $table = [];
        $result = [];
        //php数组的底层实现就是hash表,这里就不像c一样计算最小值那些了步骤了,直接一把梭
        for($i=0;$i < $len;$i++) {
            //先判断是否有,有的话直接返回下标
            if (isset($table[$target - $nums[$i]])) {
                return [$table[$target - $nums[$i]],$i];
            }else{
            //否则hash里的数字 => 下标
                $table[$nums[$i]] = $i;
            }
        }
    }
}

你可能感兴趣的:(leetcode,php,哈希表,数据结构)