3)数组和字符串总结

在阅读本篇文章之前,希望你已经完成了代码随想录中的:

数组部分、字符串部分、双指针法非链表的部分。

1.双指针法总结

在出现两个场景的时候,我们可以考虑使用双指针法。

1.1 需要原地对数组或者字符串进行修改的时候

27. 移除元素 - 力扣(LeetCode)

54. 替换数字(第八期模拟笔试) (kamacoder.com)

26. 删除有序数组中的重复项 - 力扣(LeetCode)

283. 移动零 - 力扣(LeetCode)

88. 合并两个有序数组 - 力扣(LeetCode)

像这种题目都是要求你原地修改数组的(所以说机考几乎不可能出现,因为谁知道你机考输出的是原地修改的还是另起一个数组;但需要担心面试问到)

对于双指针需要从前面开始还是从后面开始,取决于:你更新完数组之后,数组的长度是变长了还是变短了。如果更新完数据之后数组长度变短,就从0开始;如果更新完数据之后数组长度变长,那就从nums.size() - 1(要先resize出新的长度)开始。

1.2 减少for循环的次数

当你在考虑使用多层for循环暴力求解的时候,就可以考虑使用双指针来减少一个for了

1. 两数之和 - 力扣(LeetCode)

15. 三数之和 - 力扣(LeetCode)

18. 四数之和 - 力扣(LeetCode)

当你想使用左右双指针来解决问题的时候,需要满足:

1. 需要在一串数据之中需要找到两个值,目标是它们相加等于某个值。

2. 这串数据是有序的。

1.3 双指针法在链表中使用

等到链表篇再讲

当然双指针的用法还有很多很杂的,这边就不多讲了,全靠做题时能否自己悟出来了

2. 去重逻辑

顺带一提,对于上面什么什么几数之和这种要求去重的题目,有一个公式去重法。

对于for循环的话,可以这样去重:

for (int i = 0; i < nums.size(); i++) {
    if (i > 0 && nums[i] == nums[i - 1]) {
        continue;
    }
    for (int j = i + 1; j < nums.size(); j++) {
        if (j > i + 1 && nums[j] == nums[j - 1]) {
            continue;
        }
        for (int k = j + 1; k < nums.size(); k++) {
            if (k > j + 1 && nums[k] == nums[k - 1]) {
                continue;
            }
            // 以此类推
        }
    }
}

 对于双指针的话,可以这样去重:

int left = 0;
int right = nums.size() - 1;
while (left < right) {
    if (left > 0 && nums[left] == nums[left - 1]) {
        left++;
        continue;
    }
    if (right < nums.size() - 1 && nums[right] == nums[right + 1]) {
        right++;
        continue;
    }
    // 你想写的主体内容
}

对于回溯算法的话,可以这样去重(看不明白也没关系,以后到回溯的时候还会再说一遍)

void function(vector<int> &nums, int start) 
{
    for (int i = start; i < nums.size(); i++) {
        if (i > start && nums[i] == nums[i - 1]) {
            continue;
        }
        // 你想写的函数体
        function(nums, i + 1);
    }
}

总而言之,去重的核心逻辑就是:

当指针>指针的起始位置,且nums[指针] = nums[指针-1](如果是从右往左移的指针则反过来)那就跳过当前循环。

3. 前缀和

58. 区间和 | 代码随想录 (programmercarl.com)

44. 开发商购买土地 | 代码随想录 (programmercarl.com)

本身前缀和的题目并不难以理解,本质上就是用一个新数组把知道当前遍历的值的和都记下来,然后需要取某一段之和的时候就将新数组相减。

前缀和后面还可能会在动态规划、哈希表等情况遇见,到时候再说。

4. KMP算法

这位更是重量级,机考不会考的,散了吧(面试也应该不会问吧)

严格来说这玩意也能算双指针,也能叫前缀,但要理解起来非常抽象

vector<int> prefix(needle.size(), 0);
int j = 0;
for (int i = 1; i < needle.size(); i++) {
    while (j > 0 && needle[i] != needle[j]) {
        j = prefix[j - 1];
    }
    if (needle[i] == needle[j]) {
        j++;
    }
    prefix[i] = j;
}

上面这一部是先找到prefix(或者叫next)数组,这个数组记录的是:到当前下标,多少个后缀与前缀是重复的。

比如aba的结果就是001,abab的结果就是0012,abcab的结果就是00012。

j = 0;
for (int i = 0; i < haystack.size(); i++) {
    while (j > 0 && haystack[i] != needle[j]) {
        j = prefix[j - 1];
    }
    if (haystack[i] == needle[j]) {
        j++;
        if (j == needle.size()) {
            return i - j + 1;
        }
    }
}
return -1;

然后在我们逐个比对的时候,当我们发现不匹配的时候,我们再去看看当前不匹配的位置的prefix有没有值,如果有值的话就说明,直到这里的后缀和某一段前缀重复的;因为有一段后缀和haystack已经匹配了,那么一定又会有那么一段前缀和haystack匹配,这一段前缀的匹配就可以被省略掉。

我知道上面这段文字很抽象,要是你看不懂的话要不你来找我,我给你打语音()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值