贪心算法与哈希表刷题笔记

哈希表

哈希表(Hash Table):也叫做散列表。是根据关键码值(Key Value)直接进行访问的数据结构。

哈希表通过键 key 映射函数 Hash(key) 计算出对应的值 value,把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数(散列函数),存放记录的数组叫做哈希表(散列表)

下面是本周做过的题(节选)(题目来自力扣):

第一题

242. 有效的字母异位词

给定两个字符串 *s**t* ,编写一个函数来判断 *t* 是否是 *s* 的字母异位词。

**注意:**若 *s**t* 中每个字符出现的次数都相同,则称 *s**t* 互为字母异位词。

本题是哈希表中的一道简单题,要判断串s和串t中字母出现次数是否相同,我们只需要把串s和串t中的字母按下标存储起来,再判断是否数量相等。

我们可以把每个字母减’a’,然后再存储。

下面是代码实现:

#include<string.h>
bool isAnagram(char* s, char* t) {
    int arr1[27]={0};
    int arr2[27]={0};
    int i=0;
    int lens=strlen(s);
    int lent=strlen(t);
    if(lens!=lent)
        return false;
    for(i=0;i<lens;i++){
        arr1[s[i]-'a']++;
    }
    for(i=0;i<lent;i++){
        arr2[t[i]-'a']++;
    }
    for(i=0;i<27;i++){
        if(arr1[i]!=arr2[i])
            return false;
    }
    return true;
}

第二题

13. 罗马数字转整数

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

这道题思路是当左边大于右边时用加法,左边小于右边时用减法。

所以我们可以先把字母之间的加减存储起来,然后就能直接使用了。

下面是代码实现:

int romanToInt(char* s) {
    int len=strlen(s);
    int arr[27]={0};
    arr['I'-'A']=1;
    arr['V'-'A']=5;
    arr['X'-'A']=10;
    arr['L'-'A']=50;
    arr['C'-'A']=100;
    arr['D'-'A']=500;
    arr['M'-'A']=1000;
    int i=0;
    int ans=0;
    for(i=0;i<len;i++){
        int m=arr[s[i]-'A'];//字母存储时减去'a'
        if(i<len-1&&m<arr[s[i+1]-'A'])//首先i要小于len-1,因为之后i要加1。然后再判断前后大小加或减
            ans-=m;
        else
            ans+=m;
    }
    return ans;
}

第三题

349. 两个数组的交集

给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

我们可以把nums1和nums2分别按下标存储到arr1,arr2里面,然后再判断是否有相同的。我这里想的是arr1,arr2中出现的都设置为1,然后加起来看是否等于2.

下面是代码实现:

int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
    int arr1[1001]={0};
    int arr2[1001]={0};
    int arr[1001]={0};
    for(int i=0;i<nums1Size;i++){
        arr1[nums1[i]]++;
        if(arr1[nums1[i]]>=2)
            arr1[nums1[i]]=1;//如果大于1,就继续设置为1,这样最后加起来看是否等于2
    }
    int j=0;
    int* ans=(int*)malloc(sizeof(int)*nums2Size);
    for(int i=0;i<nums2Size;i++){
        arr2[nums2[i]]++;
        if(arr2[nums2[i]]>=2)
            arr2[nums2[i]]=1;
    }
    for(int i=0;i<1001;i++){
        arr[i]=arr1[i]+arr2[i];//两个数组加起来
    }
    for(int i=0;i<1001;i++){
        if(arr[i]==2){//如果等于2,则证明两个数组有交集
            ans[j++]=i;
        }
    }
    *returnSize=j;
    return ans;
}

贪心算法

贪心算法总的来说就是以局部最优从而推出全局最优。

但是贪心算法并没有固定的解决套路。

下面是本周做过的题:

第一题

455. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

我们这道题的思路就是尽量把大的饼干分给胃口大的小孩。

我们可以从大到小循环小孩,从最大的饼干开始的分发,如果大于小孩的胃口,ans++,然后饼干减小。

下面是代码实现:

int cmp(int* a,int* b){
    return *a-*b;
}
int findContentChildren(int* g, int gSize, int* s, int sSize) {
    qsort(g,gSize,sizeof(int),cmp);
    qsort(s,sSize,sizeof(int),cmp);
    int ans=0;
    int m=sSize-1;
    for(int i=gSize-1;i>=0;i--){
        if(m>=0&&s[m]>=g[i]){
            ans++;
            m--;        
        }
    }
    return ans;
}

第二题

376. 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 **摆动序列 。**第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
  • 相反,[1, 4, 7, 2, 5][1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列最长子序列的长度

这道题我们主要找拐点,因为他可能会出现平坡或者单点区间

下面是代码实现:

int wiggleMaxLength(int* nums, int numsSize){
    if(numsSize<=1)
        return numsSize;
    int ans=1;
    int n=0;
    int curDiff = 0;
        int preDiff = 0;  
        for (int i = 0; i < numsSize - 1; i++) {
            curDiff = nums[i + 1] - nums[i];//先把i+1减去i存起来,方便之后的判断和向后循环
            if ((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 && curDiff < 0)) {//这里判断的是前后异号,即判断是不是为拐点
                ans++;//如果是,则ans++
                preDiff = curDiff;
            }
        }
    return ans;
}

第三题

55. 跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false

我们只需要判断数组能否覆盖到最后一个,就可以得出结论

下面是代码实现:

bool canJump(int* nums, int numsSize) {
    if(numsSize<=1)
        return true;
    int cover=0;
    for(int i=0;i<=cover;i++){//如果中间断了,则跳出循环
        cover=fmax(cover,i+nums[i]);//cover储存的是能覆盖的到的最大范围
        if(cover>=numsSize-1)//这里判断的是能否覆盖到最后一位
            return true;
    }
    return false;
}

小结

关于算法的掌握还是需要多多练题,多见不同的题型我们才能够更加熟悉本算法。本周做了多道算法题,在之后的学习中也会继续练习。

已经到底啦!!

  • 32
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值