Leetcode题解-算法-哈希表

1、两数之和

1. Two Sum(Easy)
给一个无须数组,找出两个下标,下标对应的数之和等于给定值,两下标不能相同。
Example:

Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

方法一:暴力搜索

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        vector<int>vec(2,0);
        for (int i = 0; i < n; i++){
            for (int j = 0; j < n; j++){
                if(nums[i] + nums[j] == target && i != j){
                    vec[0] = i;
                    vec[1] = j;
                }
            }
        }
        return vec;
    }
};

时间复杂度:O(n2)
空间复杂度:O(1)

方法二:哈希
将每个数值作为 key,下标作为 key 的 value 值,对于每一个数 nums[i],寻找 key 值为 target - nums[i] 的元素是否存在,存在且是两个元素,就找到了答案。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        vector<int>vec;
        unordered_map<int,int>m;
        for(int i = 0; i < n; i++)
            m[nums[i]] = i;
        for(int i = 0; i < n; i++){
            if(m.find(target - nums[i]) != m.end()){
                vec.push_back(i);
                vec.push_back(m[target - nums[i]]);
                if(vec[0] != vec[1]){
                    break;
                }
                else
                    vec.clear();
            }       
        }
        return vec;
    }
};

时间复杂度:O(n)
空间复杂度:O(n)

2、判断数组中是否有重复的数

217. Contains Duplicate(Easy)
Example 1:

Input: [1,2,3,1]
Output: true

Example 2:

Input: [1,2,3,4]
Output: false

Example 3:

Input: [1,1,1,3,3,4,3,2,4,2]
Output: true

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_map<int,int>m;
        for (int i = 0; i < nums.size(); i++){
            m[nums[i]]++;
            if(m[nums[i]] >=2)
                return true;
        }
        return false;
    }
};

3、最长和谐序列

594. Longest Harmonious Subsequence(Easy)

Example 1:

Input: [1,3,2,2,5,2,3,7]
Output: 5
Explanation: The longest harmonious subsequence is [3,2,2,2,3].

和谐序列的最大最小值之差等于1,找出长度最长的和谐序列

class Solution {
public:
    int findLHS(vector<int>& nums) {
        if(nums.empty())
            return 0;
        map<int,int>m;
        int n = nums.size();
        for (int i = 0; i < n; i++)
            m[nums[i]]++;
        int res = 0;
        auto pre = m.begin();
        auto cur = m.begin();
        for (++cur ; cur != m.end(); pre++, cur++){
            if(cur->first == pre->first + 1)
                res = max(res, pre->second + cur->second);
        }
        return res;
    }
};

4、最长连续序列

128. Longest Consecutive Sequence(Hard)
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.Your algorithm should run in O(n) complexity.

给一个未排序的数组,从数组中挑出连续连续数列,最长的长度是多少,要求 O(n) 复杂度
Example:

Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

方法一:map统计
将每个元素插入 map 中,map 底层使用红黑树实现,按照 key 值排序,所以依次遍历 map 中的元素,寻找最长连续序列即可。

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.empty())
            return 0;
        map<int,int>m;
        int n = nums.size();
        for (int i = 0; i < n; i++)
            m[nums[i]]++;
        int res = 1;
        auto pre = m.begin();		//双指针,用于判断前后两元素是否连续
        auto cur = m.begin();
        int curmax = 1;				//当前连续序列的长度
        for (cur++; cur != m.end(); pre++, cur++){
		    if (cur->first == pre->first + 1){	//每次连续序列增加的新元素,更新最长序列的长度
			    curmax++;
			    res = max(res, curmax);
		    }
		    else
			    curmax = 1;			//开始一个新序列,长度为1
	    }
        res = max(res, curmax);
        return res;
    }
};

时间复杂度:O(nlogn) ,map插入的复杂度为O(logn)
空间复杂度:O(n)

方法二:unordered_map统计
unordered_map 使用哈希寻找元素,复杂度为O(1),key 值为元素值,value 为到该元素为止,连续数列的长度。每次插入新的元素,查看是否可以归入已有的连续序列中,并修改新的连续序列的首尾元素是 value 为新序列的长度。

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.empty())
            return 0;
        unordered_map<int, int> m;
	    int maxLen = 1;
	    for (auto &n : nums) {
		    if (m[n])
			    continue;
		    //孤立的元素,对应的 value 设为 1
		    if (m.find(n - 1) == m.end() && m.find(n + 1) == m.end()) {
			    m[n] = 1;
		    }
		    //n+1 存在,在元素右边可以连成序列,序列的首尾 key 分别是  n和 n+m[n+1]
		    //更新首尾 key 值对应的 value 为序列长度,长度为 m[n+1]+1,也就是原始长度加 1
		    else if (m.find(n - 1) == m.end()) {
			    m[n] = m[n + m[n + 1]] = m[n + 1] + 1;
			    maxLen = max(maxLen, m[n]);
		    }
		    //n-1 存在,在元素左边可以连成序列,序列的首尾 key 分别是 n-m[n-1] 和 n
		    //更新首尾 key 值对应的 value 为序列长度,长度为 m[n-1]+1,也就是原始长度加 1
    		else if (m.find(n + 1) == m.end()) {
    			m[n] = m[n - m[n - 1]] = m[n - 1] + 1;
    			maxLen = max(maxLen, m[n]);
    		}
	    	//n-1 和 n+1 都存在,在元素两侧都可以连成序列,序列的首尾 key 分别是 n-m[n-1] 和 n+m[n+1]
	    	//更新首尾 key 值 和当前位置 key 值对应的 value 为序列长度,长度为 m[n-1]+m[n+1]+1,也就是左右侧原始长度之和再加 1
		    else {
			    m[n] = m[n - m[n - 1]] = m[n + m[n + 1]] = 1 + m[n - 1] + m[n + 1];
    			maxLen = max(maxLen, m[n]);
	    	}
	    }
	    return maxLen;
    }
};

时间复杂度:O(n)
空间复杂度:O(n)

5、LRU

146. LRU Cache(Medium)
设计 LRU(最近最少使用)类,类中有 get 和 put 操作。
get(key) :key 存在返回 value,不存在返回 -1。
put(key, value):key 值不存在则插入,若达到了最大容量,就使之前最少只用的项无效。
要求插入,查找操作复杂度都是 O(1)

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4

解题思路:使用 hash 和双向链表,hash中 key 对应的 value 为指向链表对应元素的指针,双向链表中的元素为pair<int, int>,分别是 key 值和 value。

  • 插入节点时,如果节点存在,就将该元素从链表中取出,放到链表的头部,并更新 hash 中 key 值对应的指针。如果达到了最大容量,将链表尾部的元素删除,并删除 hash 中的元素。将要插入放入元素放到链表的开头,在 hash 中添加 key 值和 指向链表对应元素的指针。
  • 查找节点时,在 hash 中查找, 找不到,返回 -1,找到了,将该节点调用插入函数。
class LRUCache {
private:
    int capacity;
    list<pair<int, int>>l;
    unordered_map<int, list<pair<int, int>>::iterator>m;
public:
    LRUCache(int capacity) {
        this->capacity = capacity;
    }
    int get(int key) {
        int value = 0;
        auto item = m.find(key);
        if (item == m.end()) return -1;
        auto it = item->second;
        value = it->second;
        put(it->first, it->second);
        return value;
    }
    bool exist(int key, int value){
        auto item = m.find(key);
        if (item == m.end())
            return false;
        auto it = item->second;
        l.erase(it);
        l.push_front(pair<int, int>(key, value));
        m[key] = l.begin();
        return true;
    }
    void put(int key, int value) {
        if (exist(key, value)) return;
        if (l.size() == capacity){
            auto it = l.back();
            auto item = m.find(it.first);
            if (item != m.end())
                m.erase(item);
            l.pop_back();
        }
        l.push_front(pair<int, int>(key, value));
        m[key] = l.begin();
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值