数据结构算法——哈希表

1 概述

该博客结合leetcode原题介绍了如何利用哈希表降低程序的时间复杂度。

1.1 HashMap实现方式

将key通过一个function:变成一个数字(429),存入大小为30的数组中(取余数得到数组中的下标)。
在这里插入图片描述

1.2 Hash碰撞

Function的设计有可能算出重复的数组下标(尽量避免),有许多方法可以解决。此处介绍“拉链法”,即在同一个数组下标处存一个链表,将重复的key链起来。
在这里插入图片描述

1.3 更多实现方式

set和map类似,可以用Hash和Tree两种方式实现。
在这里插入图片描述

Hash的时间复杂度O(1),Tree的时间复杂度O(log(N)),但是Tree是有序排列,Hash是无序排列。
在这里插入图片描述

2 例题

2.1 有效的字母异位词

#Leetcode 242 有效的字母异位词
该题是让我们判断两个字符串是否可以通过字母顺序变换得到一样的结果。
(1)暴力解法
两个字符串分别排序,然后看是否相等。
*时间复杂度:O(NlogN)
*空间复杂度:O(1)

# coding = utf-8
class Solution(object):
    def isAnagram(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        return sorted(s)==sorted(t)

(2)利用hashmap
分别遍历两个字符串,将对应字母以及出现的总次数记录在字典中,然后比较字典是否相同。
*时间复杂度:O(N)
*空间复杂度:O(N)

# coding = utf-8
class Solution(object):
    def isAnagram(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        if len(s)!=len(t):
        	return False
        s_map, t_map = {}, {}
        for c in s:
        	if c in s_map:
        		s_map[c] += 1
        	else:
        		s_map[c] = 1
        for c in t:
        	if c in t_map:
        		t_map[c] += 1
        	else:
        		t_map[c] = 1
        if s_map==t_map:
        	return True
        else:
        	return False

2.2 数字之和

#Leetcode 1,15 两数之和,三数之和(高频题)
该题是让我们判断给定的数组中是否可以找到数字的组合满足求和等于target(该题target是0),并返回所有不重复的组合,下面我们以三数之和为例展开。
(1)暴力解法
对数组做三层循环,i从0开始,j从i+1开始,k从j+1开始,遍历完,找到所有的组合,最后去重。(此处使用了一个小技巧,先将数组排序,用set存储结果,避免最后去重的时间复杂度过大)
*时间复杂度:O(N^3)
*空间复杂度:O(1)

def threeSum(self, nums):
	if len(nums)<3:
		return []
	nums.sort()
	res = set()
	for i in range(0, len(nums)):
		for j in range(i+1, len(nums)):
			for k in range(j+1, len(nums)):
				if nums[i]+nums[j]+nums[k]==0:
					res.add((nums[i],nums[j],nums[k]))
	return map(list, res)

(2)使用hashmap
该解法考虑到遍历第1,2层时用target减去这两个数即所找的数,因此使用hashmap存储数组可以节省一层循环
*时间复杂度:O(N^2)
*空间复杂度:O(N)

def threeSum(self, nums):
	if len(nums)<3:
		return []
	d = {}
	res = set()
	for i, v in enumerate(nums):
		d[v] = 1
	for i in range(0, len(nums)):
		for j in range(i+1, len(nums)):
			if -nums[i]-nums[j] in d:
				res.add(tuple(sorted([nums[i],nums[j],-nums[i]-nums[j])))
	return map(list, res)

(3)两边往中间夹
比较反人类的做法,首先排序,依次遍历每个元素,此次观察后续数组的首尾,大于0则尾元素减一位,小于0则首元素加一位。
*时间复杂度:O(N^2)
*空间复杂度:O(1)

def threeSum(self, nums):
    if len(nums) < 3:
        return []
    nums.sort()
    res = set()
    for i, v in enumerate(nums):
        if i>=len(nums)-1: break
        j = i + 1
        k = len(nums) - 1
        while j < k:
            if nums[i] + nums[j] + nums[k] > 0:
                k -= 1
            elif nums[i] + nums[j] + nums[k] < 0:
                j += 1
            else:
                res.add((nums[i], nums[j], nums[k]))
                j += 1
                k -= 1
    return map(list, res)

2.3 K数之和

该题是面试中经常遇到,不在leetcode中出现,原则是将此问题抽象为递归,最小问题就是2-sum。


public void kSum(int[] nums, int k, int target, int from, int end, List<Integer> cur, List<List<Integer>> result){
if(k == 2) {
    int left = from;
    int right = end;
    while(left < right){
        int diff = target- nums[left] - nums[right];
        if (diff == 0){
            List<Integer> r = new ArrayList<>();
            r.add(nums[left]);
            r.add(nums[right]);
            r.addAll(cur);
            result.add(r);
            while(left < right && nums[left] == nums[left + 1]) left++;
            while(left < right && nums[right] == nums[right - 1]) right--;
            left++;
            right--;
        }else if (diff > 0){
            left++;
        }else {
            right--;
        }
    }
}else {  // k>2时
        for(int i = from; i < end - k + 2; i++){
            if(i > from && nums[i] == nums[i - 1]) continue;
            cur.add(nums[i]);
            kSum(nums, k - 1, target - nums[i], i + 1, end, cur, result);
            cur.remove(cur.size() - 1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值