力扣算法ing(70 / 100 )

5.14 有效的字母异位词

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

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

提示:

  • 1 <= s.length, t.length <= 5 * 104

  • st 仅包含小写字母

我的思路:

字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语,并使用所有原字母一次。

是否出现过?

Map = a n g r m

t: n a g a r a m return true

but!

如果t为an也返回true

所以赶紧更换思路!

不仅仅是出现过那么简单,要完全相同->出现的字符一样多

利用map记录下s字符以及出现的次数,遍历t字符,如果s字符里面有t的其中一个字符,值就要对应减一,当减到值为0的时候,就要消除键。如果最后map的长度等于0就是相等,不等于就是不相同

map的一些使用函数:

  1. new Map():创建一个 Map 对象。
  2. map.set(key, value):设置 Map 对象中键的值。如果键不存在,则会添加该键,并返回 Map 对象。
  3. map.get(key):返回键对应的值,如果不存在,则返回 undefined
  4. map.has(key):返回一个布尔值,表示 Map 中是否存在该键。
  5. map.delete(key):如果存在该键,则移除该键及其对应的值,并返回 true;否则返回 false
  6. map.clear():移除 Map 对象中的所有键值对。
  7. map.size:返回 Map 对象中键值对的数量。
  8. map.keys():返回一个迭代器,包含 Map 对象中所有的键。
  9. map.values():返回一个迭代器,包含 Map 对象中所有的值。
  10. map.entries():返回一个迭代器,包含 Map 对象中所有的键值对。
  11. map.forEach(callbackFn[, thisArg]):对 Map 中的每个键值对执行一次给定的回调函数。

我的代码:

function isAnagram(s: string, t: string): boolean {
    let map = new Map<string , number>();//值,出现的次数
    // 判断两个的长度是否相同
    if(s.length !== t.length ){
        return false;
    }
    // 将s里面的字符遍历到map里面
    for(let i = 0 ; i < s.length ; i++){
        map.set( s[i] , (map.get(s[i]) || 0 ) + 1);
    }
    // 进行判断t里面的
    let flag = true;
    for(let j = 0 ; j < t.length ; j++){
        if(!map.has(t[j])){
           return false;
        }else {
            // 如果有相同的,我们直接把map里面的对应的次数减1,全部移除了就证明两个是相等的
            map.set(t[j] , (map.get(t[j]) - 1 ))
            // 如果值(出现次数为0),delete这个键
            if(map.get(t[j]) === 0){
                map.delete(t[j]);
            }
        }
    }
    return map.size === 0;
};
return nums;

};


双指针思路:

因为要求是递增嘛,我们设两个指针指向原数组的首尾端,设置一个新的数组,从最后开始插入数字

我的代码:

let i = 0 , j = nums.length - 1;
let k = nums.length - 1; ;
let res = [];
while( i <= j){
let ans1 = nums[i] * nums[i];
let ans2 = nums[j] * nums[j];
if(ans1 > ans2){
res[k] = ans1;
k–;
i++;
}else {
res[k] = ans2;
k–;
j–;
}
}
return res;


总结:在数组中,双指针是一个很好的方法

# 5.9 数组合集---长度最小的子数组

给定一个含有 `n` 个正整数的数组和一个正整数 `target` **。**

找出该数组中满足其总和大于等于 `target` 的长度最小的 **子数组** `[numsl, numsl+1, ..., numsr-1, numsr]` ,并返回其长度**。**如果不存在符合条件的子数组,返回 `0` 。

 

**示例 1:**

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。


**示例 2:**

输入:target = 4, nums = [1,4,4]
输出:1


**示例 3:**

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0


 

**提示:**

- `1 <= target <= 109`
- `1 <= nums.length <= 105`
- `1 <= nums[i] <= 104`



**进阶:**

- 如果你已经实现 `O(n)` 时间复杂度的解法, 请尝试设计一个 `O(n log(n))` 时间复杂度的解法。

我的思路:

双指针,滑窗思想

[2,3,1,2,4,3]

​         *

​           *

2<7 res++ j++

2 + 3 < 7  res++ j++

5 + 1 < 7  res++ j++

6 + 2 > 7 res-- i++ 8-2 = 6

6 < 7 j++  

6 + 4 > 7 res-- i++  10-3 = 7 = 7 res-- i++

7 - 2 = 5 

5 + 3 = 8 res-- i++   8-2 = 6

当sum<target => res++ , j++ sum+=

当sum >= tartget => res-- , i++ sum-=  缩小窗口

总的来说sum>=target的时候进行缩小窗口操作,其他的进行扩大窗口的操作

我的代码:

function minSubArrayLen(target: number, nums: number[]): number {
let i = 0 ;
let j = 0;
let min = Infinity;
// [1,4,4]
let sum = 0 ;
while(j < nums.length ){
sum += nums[j];
while(sum >= target){
min = Math.min(min , j - i + 1);
sum -= nums[i];
i++;
}
j++;
}
return min === Infinity ? 0 : min;
};


# 5.12 数组合集---59.螺旋矩阵

给你一个正整数 `n` ,生成一个包含 `1` 到 `n2` 所有元素,且元素按顺时针顺序螺旋排列的 `n x n` 正方形矩阵 `matrix` 。

 

**示例 1:**

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]


**示例 2:**

输入:n = 1
输出:[[1]]


 

**提示:**

- `1 <= n <= 20`

这道题目可以说在面试中出现频率较高的题目,**本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。**

我的思路:其实我最开始是没有思路的,除了一昧的循环,我没有任何思路,看到题解使用的边界方法,我突然领悟了

生成一个包含 1 到 n2 所有元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix
生成一个正方形的矩阵,它的值是元素按顺时针螺旋排列得到的
i j
00 01 02
10 11 12
20 21 22
定义边界
r=2 l=0 t=0 b=2
for(int i = 0 ; i < r ; i++){
[top][i]
top++
}
for(let j = top ; j < b ; j++){
[j][r]
r–;
}
for(let k = r ; k > l ; k-- ){
[b][k]
b–;
}
for(let m = b ; m > t ; m==){
[m][l];
l++
}


但是实现起来却没有这么简单:

- 矩阵声明问题:

  我最开始是用的

- ```
  let res : number[][] = [];

但是这样的声明不会创建一个n x n的二维数组,而是创建了一个空数组

  • let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
    

最终代码:

function generateMatrix(n: number): number[][] {
    // 定义边界
    let r = n - 1 , l = 0 , t = 0 , b = n - 1;
    const total = n * n ; 
    let num = 1 ; 
    // 这样的声明不会创建一个n x n的二维数组,而是创建了一个空数组。
    let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
    while(num <= total){
        for(let i = l ; i <= r ; i++){
            res[t][i] = num;
            num++;
        }
        t++;
        for(let j = t ; j <= b ; j++){
            res[j][r] = num;
            num++;
        }
        r--;
        for(let k = r ; k >= l ; k--){
            res[b][k] = num;
            num++;
        }
        b--;
        for(let m = b ; m >= t ; m--){
            res[m][l] = num;
            num++;
        }
        l++;
    }
    return res;
    
};

总结:这样的螺旋问题可以使用边界法,边界是动态的,我们循环的时候注意一下边界就好啦

5.13 数组总结

数组是存放在连续内存空间上的相同类型数据的集合。

数组可以方便的通过下标索引的方式获取到下标对应的数据。

正是因为数组在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。

数组的元素是不能删的,只能覆盖。

  • 二分法:循环不变量原则,只有在循环中坚持对区间的定义,才能清楚的把握循环中的各种细节。

    二分法是算法面试中的常考题,建议通过这道题目,锻炼自己手撕二分的能力

  • 双指针法:移除一个数字

    双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

  • class Solution {
    public:
        int removeElement(vector<int>& nums, int val) {
            int slowIndex = 0;
            for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
                if (val != nums[fastIndex]) {
                    nums[slowIndex++] = nums[fastIndex];
                }
            }
            return slowIndex;
        }
    };
    
  • 滑动窗口:

    滑动窗口如何移动 窗口起始位置,达到动态更新窗口大小的,从而得出长度最小的符合条件的长度。

    滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。

哈希表

eg:存储名字

通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

如果hashCode得到的数值大于 哈希表的大小了,也就是大于tableSize了,怎么办呢?

此时为了保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做一个取模的操作,这样我们就保证了学生姓名一定可以映射到哈希表上了。

几位学生的名字同时映射到哈希表 同一个索引下标的位置

哈希碰撞

  • 拉链法

    发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到他们了

  • 线性探测法

    依靠哈希表中的空位来解决碰撞问题。

常见的哈希结构

  • 数组

  • set (集合)

    有序且不重复

  • map(映射)

    map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制的

总结一下

总结一下,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!

5.14 有效的字母异位词

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

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

提示:

  • 1 <= s.length, t.length <= 5 * 104

  • st 仅包含小写字母

我的思路:

字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语,并使用所有原字母一次。

是否出现过?

Map = a n g r m

t: n a g a r a m return true

but!

如果t为an也返回true

所以赶紧更换思路!

不仅仅是出现过那么简单,要完全相同->出现的字符一样多

利用map记录下s字符以及出现的次数,遍历t字符,如果s字符里面有t的其中一个字符,值就要对应减一,当减到值为0的时候,就要消除键。如果最后map的长度等于0就是相等,不等于就是不相同

map的一些使用函数:

  1. new Map():创建一个 Map 对象。
  2. map.set(key, value):设置 Map 对象中键的值。如果键不存在,则会添加该键,并返回 Map 对象。
  3. map.get(key):返回键对应的值,如果不存在,则返回 undefined
  4. map.has(key):返回一个布尔值,表示 Map 中是否存在该键。
  5. map.delete(key):如果存在该键,则移除该键及其对应的值,并返回 true;否则返回 false
  6. map.clear():移除 Map 对象中的所有键值对。
  7. map.size:返回 Map 对象中键值对的数量。
  8. map.keys():返回一个迭代器,包含 Map 对象中所有的键。
  9. map.values():返回一个迭代器,包含 Map 对象中所有的值。
  10. map.entries():返回一个迭代器,包含 Map 对象中所有的键值对。
  11. map.forEach(callbackFn[, thisArg]):对 Map 中的每个键值对执行一次给定的回调函数。

我的代码:

function isAnagram(s: string, t: string): boolean {
    let map = new Map<string , number>();//值,出现的次数
    // 判断两个的长度是否相同
    if(s.length !== t.length ){
        return false;
    }
    // 将s里面的字符遍历到map里面
    for(let i = 0 ; i < s.length ; i++){
        map.set( s[i] , (map.get(s[i]) || 0 ) + 1);
    }
    // 进行判断t里面的
    let flag = true;
    for(let j = 0 ; j < t.length ; j++){
        if(!map.has(t[j])){
           return false;
        }else {
            // 如果有相同的,我们直接把map里面的对应的次数减1,全部移除了就证明两个是相等的
            map.set(t[j] , (map.get(t[j]) - 1 ))
            // 如果值(出现次数为0),delete这个键
            if(map.get(t[j]) === 0){
                map.delete(t[j]);
            }
        }
    }
    return map.size === 0;
};

总结:这道题考的是对map的敏感度以及map的方法的使用,利用map记录下s字符以及出现的次数,遍历t字符,如果s字符里面有t的其中一个字符,值就要对应减一,当减到值为0的时候,就要消除键。如果最后map的长度等于0就是相等,不等于就是不相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值