哈希只是种数据结构,算不上是什么思想算法应该,这里只是为了熟悉哈希的用法,要想起这个东西。
实现一个算法,确定一个字符串
s
的所有字符是否全都不同。
首先很容易想到使用哈希表的方法,此时时间和空间复杂度都为O(n)
class Solution {
public:
bool isUnique(string s) {
int n=s.size();
unordered_map umap;
for(int i=0;i
采用哈希表是为了记录每个字符出现的次数,但会使用多余的空间。因此可以采用位运算,每一个bit位记录一个字符是否出现,从而不使用额外的数据结构。
class Solution {
public:
bool isUnique(string s) {
int n=s.size(),tmp=0,step;
for(int i=0;i
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
和上一题基本上一样。首先想到哈希表。但本题不能采用位运算,因为上一题只有小写字母26个,而本题数据范围为2-10000,10000个bit位,会越界?。
class Solution {
public:
int findRepeatNumber(vector& nums) {
int n=nums.size();
unordered_map umap;
for(int i=0;i
注意题目的条件,他并不是任意的随机一个数组,而是长度为n,且范围在0~n-1。因此可以利用数组的特殊性进行解题优化。
如果没有重复或者缺失,每个元素都应该满足nums[i]=i,每个元素都有它应该在的一个位置。因此利用这个性质,可以依次遍历,把每个元素放在正确的位置上,如果在交换过程中,待交换位置元素已经放置正确,那说明交换位置的nums[i],和待交换位置的nums[nums[i]]相同,因此其为重复元素。
class Solution {
public:
int findRepeatNumber(vector& nums) {
int n=nums.size();
for(int i=0;i
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
class Solution {
public:
unordered_map umap;
int inx;
TreeNode* helper(vector& preorder, vector& inorder,int left,int right){
if(left>right) return nullptr;
int root_val=preorder[inx];
TreeNode* node=new TreeNode(root_val);
int root_idx_in=umap[root_val];
++inx;
node->left=helper(preorder,inorder,left,root_idx_in-1);
node->right=helper(preorder,inorder,root_idx_in+1,right);
return node;
}
TreeNode* buildTree(vector& preorder, vector& inorder) {
int n=preorder.size();
inx=0;
if(n==0) return nullptr;
for(int i=0;i
数组中出现次数超过一半的数字
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
首先找多数元素,肯定可以想到hash计数
class Solution {
public:
int majorityElement(vector& nums) {
unordered_map umap;
int n=nums.size();
for(int i=0;in/2) return nums[i];
}
return -1;
}
};
考虑题目的特殊性,多数元素大于元素数量的一半。因此可以先排序,中间元素一定是多数元素。
class Solution {
public:
int majorityElement(vector& nums) {
//排序,然后中间的
sort(nums.begin(),nums.end());
return nums[nums.size()/2];
}
};
对于本题,还可以采用投票法。将多数元素看为1,其他为-1,最后相加结果一定大于0。
class Solution {
public:
int majorityElement(vector& nums) {
int sum=0,tmp;
for(int i=0;i
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
采用哈希表建立各节点的映射关系。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* node=head;
unordered_map umap;
while(node!=nullptr){
umap[node]=new Node(node->val);
node=node->next;
}
node=head;
while(node!=nullptr){
umap[node]->next=umap[node->next];
umap[node]->random=umap[node->random];
node=node->next;
}
return umap[head];
}
};
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
采用unordered_map记录出现的字符,value为字符对应的索引。
采用left记录子字符串的起始索引。当 当前字符s[i]出现过的时候,此时子字符串的长度为i-left,并更新left=umap[s[i]]+1。
此时,需要将left~umap[s[i]]范围内的字符对应value置为0,接着进行下一轮子字符串的遍历查询。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n=s.size();
//哈希表记录已经出现的字符,key:字符 value:索引
//如果找到了,则将索引idx及之前的 字符的value记为0
int ans=0;
int left=0;
unordered_map umap;
for(int i=0;i
还有其他解法欸,后面再继续
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
标签里加了哈希表,但是感觉用动态规划三指针更好懂。题解里给的哈希表标签是用来建堆时去重的。也去学习下吧!!!不要懒
动态规划做法:下一个丑数是通过前面的丑数*2 *3 *5 然后比较出来的最小的数得到的。每个丑数都需要 *2 *3 *5 ,但是无法保证他的位置。题解里使用了三指针n1,n2,n3分别代表 *2 *3 *5 后的数,而i1,i2,i3是dp数组中的索引,如果乘积后成为了新的丑数或者和新的丑数相等,索引就会+1,由此保证不会有乘积/丑数被遗漏。
class Solution {
public:
int nthUglyNumber(int n) {
//首先理解题意,丑数是可以通过只用2 3 5随便怎么乘积可以得到的数
vectordp(n);
dp[0]=1;
int i1=0,i2=0,i3=0;
for(int i=1;i
最小堆+哈希。关于优先队列,还有堆,其实还不太熟悉。相当于采用堆是把当前丑数都*2 *3 *5 ,然后存入堆中排序,是堆顶的时候取出,有点暴力那味。而动态规划是一个个的按顺序的找。也很明显这个复杂度高很多。
(25条消息) c++优先队列(priority_queue)用法详解_c++优先级队列_吕白_的博客-CSDN博客
class Solution {
public:
int nthUglyNumber(int n) {
vector factors = {2, 3, 5};
unordered_set seen;
priority_queue, greater> heap; //小顶堆,升序排列的
seen.insert(1L); //1L就是1,但long类型,不然后面会溢出
heap.push(1L);
int ugly = 0;
for (int i = 0; i < n; i++) {
long curr = heap.top();
heap.pop(); //堆顶最小,弹出记录
ugly = (int)curr; //第i+1个丑数
for (int factor : factors) {
long next = curr * factor;
//没有出现过,所以插入堆和set中保存,priority_queue会自动排序,本质是个堆实现
if (!seen.count(next)) {
seen.insert(next);
heap.push(next);
}
}
}
return ugly;
}
};
//作者:LeetCode-Solution
//链接:https://leetcode.cn/problems/chou-shu-lcof/solution/chou-shu-by-leetcode-solution-0e5i/
//来源:力扣(LeetCode)
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
很快就能想到hash表计数啦。时间复杂度是O(n)
class Solution {
public:
char firstUniqChar(string s) {
int n=s.size();
unordered_map umap;
for(int i=0;i
题解里还提到了队列,平时做题基本上想不到用队列/栈之类的。
输入两个链表,找出它们的第一个公共节点。
最开始能想到的,哈希表先存储一条链的节点,再遍历第二条,如果有一样的就是公共节点。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
//哈希表先存储一条链的节点,再遍历第二条,如果有一样的就是公共节点
ListNode *node1=headA,*node2=headB;
unordered_map umap;
while(node1!=nullptr){
umap[node1]++;
node1=node1->next;
}
while(node2!=nullptr){
if(umap[node2]) return node2;
node2=node2->next;
}
return nullptr;
}
};
采用哈希会用到额外的空间,可以考虑双指针的方法。这个也好理解,反向来推的话,比如两个链表长度分别为len1,len2,两个指针node1,node2分别从两条链表起始开始,走到头后又从另一条链表开始,那么他们都走过的长度是len1+len2,相交节点到末尾距离令为tmp,是他们最后都为经历的,因此他们都会在len1+len2-tmp步长的时候相遇。再正向去想······感觉有点没解释清楚。
评论区真的——浪漫的嘞。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *node1=headA,*node2=headB;
while(node1!=node2){
if(node1!=nullptr) node1=node1->next;
else node1=headB;
if(node2!=nullptr) node2=node2->next;
else node2=headA;
}
return node2;
}
};
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
给了用哈希表的标签,官方题解就是把nums中元素放到set里面,然后遍历0~n-1看缺哪个。。只能说很抽象没必要吧。还不如直接遍历。
第一反应就是遍历数组,因为已经有序,如果不缺的话nums[i]=i,如果不满足那么缺的就是i呀。然后考虑边界情况。这个应该算暴力法吧。
class Solution {
public:
int missingNumber(vector& nums) {
//已经有序
int n=nums.size();
for(int i=0;i
上面这个方法时间复杂度O(n)。因为已经有序,还可以考虑二分,降低复杂度。二分一个很重要的要判断边界问题,什么时候去等什么时候不取等!!!
class Solution {
public:
int missingNumber(vector& nums) {
//采用二分实际上还是对nums[i]==i进行判断,只是不是全部遍历罢了
int left=0,right=nums.size()-1;
while(left
数学方法,就是0~n-1的和可求,原数组和也可求,两个相减就是缺的数,时间复杂度也为O(n)。没必要写。
位运算,两个一样的数异或为0,异或具有交换组合性质。因此可以将数组的数相异或,再和0~n-1异或。最后剩的就是差的那个。
class Solution {
public:
int missingNumber(vector& nums) {
int n=nums.size();
int ans=0;
for(int i=0;i