leetcode刷题记录26(2023-10-31)【计数质数(翻倍的思想,避免重复计算) | 课程表II(拓扑排序dfs、bfs)| 单词搜索 II(回溯法、字典树)| 存在重复元素(排序、哈希)】

204. 计数质数

给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。

示例 1:

输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:

输入:n = 0
输出:0
示例 3:

输入:n = 1
输出:0

提示:

0 < = n < = 5 ∗ 1 0 6 0 <= n <= 5 * 10^6 0<=n<=5106

思路1:如果一个数是质数,那么它*2,*3……等,也都是质数。

class Solution
{
public:
    int countPrimes(int n)
    {
        vector<int> isPrime(n, 1);
        int res = 0;
        for (int i = 2; i < n; i++)
        {
            if (isPrime[i])
            {
                res++;
                for (int j = 2; i * j < n; j++)
                {
                    isPrime[i * j] = 0;
                }
            }
        }
        return res;
    }
};

思路2:可以对其进行优化,不一定要从2开始,可以直接从当前数i进行翻倍,因为从2开始,其实之前已经计算过了。

class Solution
{
public:
    int countPrimes(int n)
    {
        vector<int> isPrime(n, 1);
        int res = 0;
        for (int i = 2; i < n; i++)
        {
            if (isPrime[i])
            {
                res++;
                for (int j = i; (long long)i * j < n; j++)
                {
                    isPrime[i * j] = 0;
                }
            }
        }
        return res;
    }
};

思路3:参考了官方题解的优化方法,感觉有些鸡肋了,虽然优化了重复计算,但引入了一个取余运算。数学上复杂度确实降低了,但if分支所需要的复杂度也不可忽略。1

class Solution
{
public:
    int countPrimes(int n)
    {
        vector<int> primes;
        vector<int> isPrime(n, 1);
        int res = 0;
        for (int i = 2; i < n; i++)
        {
            if (isPrime[i])
            {
                primes.push_back(i);
            }
            for (int j = 0; j < primes.size() & i * primes[j] < n; j++)
            {
                isPrime[i * primes[j]] = 0;
                if ((i % primes[j]) == 0)
                {
                    break;
                }
            }
        }
        return primes.size();
    }
};

从实际效果来看,思路3并没有快到哪里去。

思路2测试结果:

在这里插入图片描述

思路3测试结果:

在这里插入图片描述

210. 课程表 II

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:[0,1]
解释:总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
示例 2:

输入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
输出:[0,2,1,3]
解释:总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。
示例 3:

输入:numCourses = 1, prerequisites = []
输出:[0]

提示:
1 < = n u m C o u r s e s < = 2000 1 <= numCourses <= 2000 1<=numCourses<=2000
0 < = p r e r e q u i s i t e s . l e n g t h < = n u m C o u r s e s ∗ ( n u m C o u r s e s − 1 ) 0 <= prerequisites.length <= numCourses * (numCourses - 1) 0<=prerequisites.length<=numCourses(numCourses1)
p r e r e q u i s i t e s [ i ] . l e n g t h = = 2 prerequisites[i].length == 2 prerequisites[i].length==2
0 < = a i , b i < n u m C o u r s e s 0 <= a_i, b_i < numCourses 0<=ai,bi<numCourses
a i ! = b i a_i != b_i ai!=bi
所有[ai, bi] 互不相同

bfs逻辑上更好理解一些,题解也有采用dfs实现的方式2

#include <vector>
#include <queue>
using namespace std;

class Solution
{
public:
    vector<int> findOrder(int numCourses, vector<vector<int>> &prerequisites)
    {
        vector<vector<int>> graph(numCourses, vector<int>()); // 邻接表
        vector<int> indegree(numCourses, 0);                  // 入度
        for (int i = 0; i < prerequisites.size(); i++)
        {
            graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
            indegree[prerequisites[i][0]]++;
        }

        queue<int> curCourses;
        vector<int> res;
        for (int i = 0; i < numCourses; i++)
        {
            if (indegree[i] == 0)
            {
                res.push_back(i);
                curCourses.push(i);
            }
        }

        while (!curCourses.empty())
        {
            int curCourse = curCourses.front();
            curCourses.pop();
            for (int i = 0; i < graph[curCourse].size(); i++)
            {
                indegree[graph[curCourse][i]]--;
                if (indegree[graph[curCourse][i]] == 0)
                {
                    res.push_back(graph[curCourse][i]);
                    curCourses.push(graph[curCourse][i]);
                }
            }
        }

        if (res.size() == numCourses)
        {
            return res;
        }
        else
        {
            return {};
        }
    }
};

212. 单词搜索 II

给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例 1:

在这里插入图片描述

输入:board = [[“o”,“a”,“a”,“n”],[“e”,“t”,“a”,“e”],[“i”,“h”,“k”,“r”],[“i”,“f”,“l”,“v”]], words = [“oath”,“pea”,“eat”,“rain”]
输出:[“eat”,“oath”]
示例 2:

在这里插入图片描述

输入:board = [[“a”,“b”],[“c”,“d”]], words = [“abcb”]
输出:[]

提示:

m = = b o a r d . l e n g t h m == board.length m==board.length
n = = b o a r d [ i ] . l e n g t h n == board[i].length n==board[i].length
1 < = m , n < = 12 1 <= m, n <= 12 1<=m,n<=12
b o a r d [ i ] [ j ] board[i][j] board[i][j] 是一个小写英文字母
1 < = w o r d s . l e n g t h < = 3 ∗ 1 0 4 1 <= words.length <= 3 * 10^4 1<=words.length<=3104
1 < = w o r d s [ i ] . l e n g t h < = 10 1 <= words[i].length <= 10 1<=words[i].length<=10
w o r d s [ i ] words[i] words[i] 由小写英文字母组成
w o r d s words words 中的所有字符串互不相同

很自然可以想到回溯法,但是回溯法会超时:

#include <vector>
#include <string>
#include <map>
using namespace std;

class Solution
{
    int directX[4] = {-1, 1, 0, 0};
    int directY[4] = {0, 0, -1, 1};
    bool findWordInBoard(vector<vector<char>> &board, string &word, vector<vector<bool>> &visited, int x, int y, int curPos)
    {
        bool res = false;
        for (int i = 0; i < 4; i++)
        {
            if (x < 0 || x >= board.size() || y < 0 || y > board[0].size())
            {
                continue;
            }
            if (!visited[x][y] && word[curPos] == board[x][y])
            {
                visited[x][y] = true;
                if (curPos + 1 == word.size())
                {
                    res = true;
                }
                else
                {
                    res = findWordInBoard(board, word, visited, x + directX[i], y + directY[i], curPos + 1);
                }
                visited[x][y] = false;
            }
            if (res == true)
            {
                break;
            }
        }
        return res;
    }

public:
    vector<string> findWords(vector<vector<char>> &board, vector<string> &words)
    {
        vector<string> res;
        for (int i = 0; i < words.size(); i++)
        {
            string word = words[i];
            bool exist = false;
            for (int x = 0; x < board.size(); x++)
            {
                for (int y = 0; y < board[0].size(); y++)
                {
                    vector<vector<bool>> visited(board.size(), vector<bool>(board[0].size()));
                    exist = findWordInBoard(board, word, visited, x, y, 0);
                    if (exist)
                    {
                        res.push_back(word);
                        break;
                    }
                    else
                    {
                        continue;
                    }
                }
                if (exist)
                {
                    break;
                }
            }
        }
        return res;
    }
};

int main()
{
    vector<vector<char>> board = {{'o', 'a', 'a', 'n'}, {'e', 't', 'a', 'e'}, {'i', 'h', 'k', 'r'}, {'i', 'f', 'l', 'v'}};
    vector<string> words = {"oath", "pea", "eat", "rain"};
    Solution s;
    vector<string> res = s.findWords(board, words);
    for (int i = 0; i < res.size(); i++)
    {
        printf("%s\n", res[i].c_str());
    }
    return 0;
}

需要利用字典树(可以参考我的另外一篇博客中,关于Trie的实现:https://blog.csdn.net/myf_666/article/details/131379343)进行优化,这里也有一点不好想。优化的思路是,先把words中的单词全部插入到字典树中来,然后对board中的每一个块进行dfs,dfs的过程中判断当前路径是否startsWith,但还不能用startsWith这个函数,因为这样没有很好的利用到之前的信息,相当于进行了重复计算。(也会超时)

参照题解3,一种更好的做法是,不断地移动Trie指针,这样就不需要判断前边是否满足条件了,只需要判断当前遍历的字母是否满足条件。

代码如下:

class Trie
{
public:
    vector<Trie *> children;
    string word;
    Trie()
    {
        children = vector<Trie *>(26, nullptr);
        word = "";
    }

    void insert(string word)
    {
        Trie *curNode = this;
        for (int i = 0; i < word.size(); i++)
        {
            int index = word[i] - 'a';

            if (curNode->children[index] == nullptr)
            {
                curNode->children[index] = new Trie();
            }

            curNode = curNode->children[index];
        }

        curNode->word = word;
    }

    bool search(string word)
    {
        Trie *curNode = this;
        for (int i = 0; i < word.size(); i++)
        {
            if (curNode->children[word[i] - 'a'] == nullptr)
            {
                return false;
            }
            curNode = curNode->children[word[i] - 'a'];
        }
        // 搜索存在,表示的是必须要以这个单词为结尾
        return curNode != nullptr && curNode->word != "";
    }

    bool startsWith(string prefix)
    {
        Trie *curNode = this;
        for (int i = 0; i < prefix.size(); i++)
        {
            if (curNode->children[prefix[i] - 'a'] == nullptr)
            {
                return false;
            }
            curNode = curNode->children[prefix[i] - 'a'];
        }
        return curNode != nullptr;
    }
};

class Solution
{
    int directX[4] = {-1, 1, 0, 0};
    int directY[4] = {0, 0, -1, 1};
    bool dfs(vector<vector<char>> &board, int x, int y, Trie* trie, set<string> &res)
    {
        char ch = board[x][y];
        if (trie->children[ch - 'a'] == nullptr)
        {
            return false;
        }
        trie = trie->children[ch - 'a'];
        if (trie->word != "")
        {
            res.insert(trie->word);
        }
        board[x][y] = '#';
        for (int i = 0; i < 4; i++)
        {
            int newX = x + directX[i];
            int newY = y + directY[i];
            if (newX < 0 || newX >= board.size() || newY < 0 || newY >= board[0].size())
            {
                continue;
            }
            if (board[newX][newY] != '#')
            {
                dfs(board, newX, newY, trie, res);
            }
        }
        board[x][y] = ch;
        return true;
    }

public:
    vector<string> findWords(vector<vector<char>> &board, vector<string> &words)
    {
        Trie* trie = new Trie();
        for (int i = 0; i < words.size(); i++)
        {
            trie->insert(words[i]);
        }
        set<string> res;
        for (int x = 0; x < board.size(); x++)
        {
            for (int y = 0; y < board[0].size(); y++)
            {
                dfs(board, x, y, trie, res);
            }
        }
        return vector<string>(res.begin(), res.end());
    }
};

运行后发现已经AC,但时间较长:

在这里插入图片描述

还可以对已经搜索过的单词去重,这样就不需要利用set进行去重了。可以进一步优化减少时间。

class Solution
{
    int directX[4] = {-1, 1, 0, 0};
    int directY[4] = {0, 0, -1, 1};
    bool dfs(vector<vector<char>> &board, int x, int y, Trie *trie, vector<string> &res)
    {
        char ch = board[x][y];
        if (trie->children[ch - 'a'] == nullptr)
        {
            return false;
        }
        trie = trie->children[ch - 'a'];
        if (trie->word != "")
        {
            res.push_back(trie->word);
            trie->word = "";
        }
        board[x][y] = '#';
        for (int i = 0; i < 4; i++)
        {
            int newX = x + directX[i];
            int newY = y + directY[i];
            if (newX < 0 || newX >= board.size() || newY < 0 || newY >= board[0].size())
            {
                continue;
            }
            if (board[newX][newY] != '#')
            {
                dfs(board, newX, newY, trie, res);
            }
        }
        board[x][y] = ch;
        return true;
    }

public:
    vector<string> findWords(vector<vector<char>> &board, vector<string> &words)
    {
        Trie *trie = new Trie();
        for (int i = 0; i < words.size(); i++)
        {
            trie->insert(words[i]);
        }
        vector<string> res;
        for (int x = 0; x < board.size(); x++)
        {
            for (int y = 0; y < board[0].size(); y++)
            {
                dfs(board, x, y, trie, res);
            }
        }
        return res;
    }
};

在这里插入图片描述

217. 存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

示例 1:

输入:nums = [1,2,3,1]
输出:true

示例 2:

输入:nums = [1,2,3,4]
输出:false

示例 3:

输入:nums = [1,1,1,3,3,4,3,2,4,2]
输出:true

提示:

1 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
− 1 0 9 < = n u m s [ i ] < = 1 0 9 -10^9 <= nums[i] <= 10^9 109<=nums[i]<=109

最直观的想法就是利用哈希进行一个判重,遍历数组,如果发现了重复,那么就直接return true,否则,最后return false。

class Solution
{
public:
    bool containsDuplicate(vector<int> &nums)
    {
        unordered_set<int> st;
        for (auto it : nums)
        {
            if (st.find(it) != st.end())
            {
                return true;
            }
            st.insert(it);
        }
        return false;
    }
};

因为哈希表在空间上有O(N)的开销,如果想进一步在空间复杂度上优化的话,还可以利用排序的方法,比如快排,通过快排,可以实现O(logn)的空间复杂度,递归栈的空间复杂度。

class Solution
{
public:
    bool containsDuplicate(vector<int> &nums)
    {
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size() - 1; i++)
        {
            if (nums[i] == nums[i + 1])
            {
                return true;
            }
        }
        return false;
    }
};

  1. https://leetcode.cn/problems/count-primes/solutions/507273/ji-shu-zhi-shu-by-leetcode-solution/ ↩︎

  2. https://leetcode.cn/problems/course-schedule-ii/solutions/249149/ke-cheng-biao-ii-by-leetcode-solution/ ↩︎

  3. https://leetcode.cn/problems/word-search-ii/solutions/1000172/dan-ci-sou-suo-ii-by-leetcode-solution-7494/ ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherries Man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值