LeetCode:经典题之389 题解与延伸

系列目录

88.合并两个有序数组
52.螺旋数组
567.字符串的排列
643.子数组最大平均数

150.逆波兰表达式
61.旋转链表
160.相交链表
83.删除排序链表中的重复元素

389.找不同
1491.去掉最低工资和最高工资后的工资平均值
896.单调序列

206.反转链表
92.反转链表II

141.环形链表
142.环型链表



389.找不同

🌟数组+哈希表/计数法

原题链接

方法一 哈希表
class Solution {
public:
    char findTheDifference(string s, string t) {
        vector<int> table(26, 0);
        for (auto& ch: s)
            table[ch - 'a'] ++;
        
        for (auto& ch: t) {
            table[ch - 'a'] --;
            if (table[ch - 'a'] < 0)
                return ch;
        }

        // 这里return -1;也是可以的
      	// 字符串相关的题的错误指示符用——' '——较好
      	// 也就是return ' ';
      	return ' ';
    }
};

哈希表

简单介绍
一、种类:

1、存储结构:开放寻址法(更推荐)和拉链法

2、字符串哈希方式


二、主要作用:

将一个较大的空间或值域(通过一定的对应法则)映射到比较小的空间或值域

[ − 1 0 9 , 1 0 9 ] − > [ 1 0 5 , 0 ) [-10^9, 10^9] \quad->\quad[10^5, 0) [109,109]>[105,0)


三、一般的实现方式:

1、通过一个哈希函数h(x)映射

a. h(x)常用写法

对x取模运算,模上数组的长度N

h(x) = x mod N(这里的N一般取一个尽可能远离2的整数次幂的质数)

// 寻找合适长度 N的方法 
// 循环找到大于10万(1e5)的第一个整数
int main() {
    // 无上约束条件
    
	for (int i = 100000;; i ++) {
		bool flag = true;
    for (int j = 2; j * j <= i; j ++)
      if (i % j == 0) {
        flag = false;
        break;
      }
    if (flag) {
      cout << i << endl;
      break;
    }
    
  	return 0;
  }
}

b. 哈希碰撞(哈希冲突)

eg. h(5) = 2; h(10) = 2;

​ 用拉链法处理哈希冲突

拉链法:开一个一维数组存储哈希值,并辅助一个单链表;


一起来看一道例题吧~

AcWing.840 模拟散列表

维护一个集合,支持如下几种操作:

I x,插入一个整数 x;
Q x,询问整数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。

输入格式

第一行包含整数 N,表示操作数量。

接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。

输出格式

对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。

每个结果占一行。

数据范围

1 ≤ N ≤ 105
−109 ≤ x ≤ 109

输入样例:

5
I 1
I 2
I 3
Q 2
Q 5

输出样例:

Yes
No
#include <bits/stdc++.h>

using namespace std;

const int N = 100003;

// e[] 表示当前节点的值;ne[] 表示下一个节点的地址
// idx 表示当前用到哪个位置
int h[N], e[N], ne[N], idx;

// 插入x
void add(int x)
{
    int k = (x % N + N) % N;    	// 构造一个哈希函数,将x映射到[0,1e5)这个区间里
    e[idx] = x;
    ne[idx] = h[k];
    h[k] = idx ++;
}

// 查询x是否存在
bool contain(int x)                // 查询操作是一个布尔类型的操作(返回true表示存在)
{	
    // 见注释
    int k = (x % N + N) % N;
    for (int i = h[k]; i != -1; i = ne[i])
        if (e[i] == x)
            return true;
    
    return false;               	// 否则返回false
}

// 拉链法
int main()
{
    int n;
    scanf("%d", &n);
    
    memset(h, -1, sizeof h);    // 空指针一般用-1表示——将所有的槽清空
    
    while (n --) {
        char op[2];             // 若是用scanf的读入方式,尽量读入字符串。scanf会自动过滤掉字符串中的回车、空格、制表符等
        int x;                  // 降低出错的概率(一般不用scanf读入字符)
        scanf("%s%d", op, &x);
        
        if (*op == 'I') add(x);      // 指针和整数比较出错;(禁止指针和整数进行比较。)
        else {                       // 这里的*+指针(字符串)表示:(指针)op是一个指向char类型的指针变量
            if (contain(x)) puts("Yes");
            else puts("No");				
       	}// else_end
    }// while_end
    
    return 0;  
}

注解:

(x % N + N) / N

  • C++中的模运算
    • 若x为负,则余数(运算结果)为负
    • 若x为证,则余数为正
  • +N的目的
    • 将余数(可能为负的)变为整数

memset(h, -1, sizeof h); // 空指针一般用-1表示——将所有的槽清空

初始化

scanf("%s%d", op, &x)

若使用scanf读入方式,应尽量读入字符串
读入字符串,scanf会自动过滤掉空格、回车、制表符等,降低出错的概率;
一般不用scanf读入字符

char *op

a.如果不加星号*
会报错:指针和整数比较出错;(禁止指针和整数进行比较。)
b.这里的*+指针(字符串)表示:(指针)op是一个指向char类型的指针变量


四、基本操作(创建,销毁,增删 查——创销增删查

添加——void add(int x)

删除——remove(int x)

​ 构造一个布尔类型的函数,在h(x)上打上标记,从逻辑上删除x(物理上并没有删除)

查找——bool contain(int x)

​ a.找到h(x)所在的槽

​ b.遍历这个槽存的值,以及对应的单链表,比较值是否相同,判断是否存在x



方法二 求和
class Solution {
public:
  	char findTheDifference(string s, string t) {
      // accumulate of ..
      int as = 0, at = 0;
      for (auto ch: s)
        	as += ch;
      
      for (auto ch: t)
        	at += ch;
      
      return at - as;
    }
};



异或(XOR)运算

异或运算对任何给定的数字x 的性质,即x ^ x = 0x ^ 0 = x

LaTeX中,

\oplus 表示A和B的异或

\bigoplus 通常用于表示多个元素的异或

x ⊕ x = 0 x ⊕ 0 = x x\oplus x = 0\\x\oplus0=x xx=0x0=x

如果对两个相同的字符串进行异或,结果将是0;

如果再对结果和第三个字符串(即那个不同的字符)进行异或,结果将是那个不同的字符

此方法只适用于找出两个字符串之间的一个差异字符

方法三 异或运算 
// 类似方法二
class Solution {
public:
    char findTheDifference(string s, string t) {
      	// 声明一个char变量,用来存最后的结果
        char res = 0;
        for (auto ch: s)
            res ^= ch;

        for (auto ch: t) 
            res ^= ch;

        return res;
    }
};
  • 31
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值