每日力扣[217-存在重复元素]

本文解析了如何使用C语言解决LeetCode中的数据结构问题217,介绍了暴力解法和利用qsort进行排序并检测重复的方法。重点在于提升算法效率,避免时间复杂度过高。

力扣LeetCode **数据结构**

217. 存在重复元素

  • 题目
    给定一个整数数组,判断是否存在重复元素。
    如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:

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

示例 2:

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

示例 3:

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

来源:力扣(LeetCode)

  • 题解【C语言】

默认的代码模板

bool containsDuplicate(int* nums, int numsSize){

}

  1. **暴力解法 **
bool containsDuplicate(int* nums, int numsSize){
    for (int i = 0; i < numsSize - 1; i++){
        for (int j = i + 1; j < numsSize; j++){
            if (*(nums+i) == *(nums+j)){
                return true;
            }
        }
    }
    return false;
}

运行结果:超出时间限制

原因:双重循环的时间复杂度为O(n2),超出了本题对于运行时间的的要求,如果对于运行时间没有特殊要求可以采用

  1. 调用库函数qsort()
bool containsDuplicate(int* nums, int numsSize){
    int compare(const void *a,const void *b){
        return *(int*)a - *(int*)b;
    }
    qsort(nums,numsSize,sizeof(int),compare);
    for (int i = 0; i < numsSize-1; i++){
        if (*(nums+i) == *(nums+i+1))
            return true;
    }
    return false;
}

运行结果:通过

说明

qsort()函数

  • 描述:

C 库函数void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))对数组进行排序。

  • 声明:

下面是 qsort() 函数的声明

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
  • 参数

    • base – 指向要排序的数组的第一个元素的指针。
    • nitems – 由 base 指向的数组中元素的个数。
    • size – 数组中每个元素的大小,以字节为单位。
    • compar – 用来比较两个元素的函数。
  • 返回值

    该函数不返回任何值。

  • 示例

//一维数组从小到大排序
#include<stdio.h>
#include<stdlib.h>
int comp(const void*a,const void*b){
	return *(int*)a-*(int*)b;
}
int main(){
	int i=0;
	int *array;
	int n;
	scanf("%d",&n);
	array=(int*)malloc(n*sizeof(int));
	for(;i<n;i++){
		scanf("%d",(array+i));
}
	qsort(array,n,sizeof(int),comp);
	for(i=0;i<n;i++){
	printf("%d\t",array[i]);
}
	return 0;
}
要高效刷完 **LeetCode Hot 100**(力扣热门100题),并能在面试中灵活应对,必须掌握一套核心的算法与数据结构知识体系。Hot 100 的题目覆盖了大多数大厂面试常考类型,以下是你**必须掌握的知识点、对应的数据结构/算法思想、典型题目示例和解题模板代码**。 --- ## ✅ 一、必备基础知识总览 | 类别 | 核心知识点 | |------|-----------| | 数据结构 | 数组、字符串、链表、栈、队列、哈希表、堆(优先队列)、树、图、并查集、前缀树(Trie) | | 算法思想 | 双指针、滑动窗口、递归、回溯、分治、贪心、动态规划(DP)、二分查找、BFS、DFS、拓扑排序 | | 技巧 | 哈希映射加速查找、哨兵技巧、状态机、区间合并、逆向思维 | --- ## ✅ 二、按类别详解 + 典型题目 + C++ 模板代码 ### 1. **数组 & 双指针** #### 常见题型: - 两数之和(哈希优化) - 三数之和(排序+双指针) - 移动零、删除有序数组重复项(快慢指针) #### 示例:两数之和 ```cpp #include <vector> #include <unordered_map> using namespace std; vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> map; for (int i = 0; i < nums.size(); ++i) { int complement = target - nums[i]; if (map.count(complement)) { return {map[complement], i}; } map[nums[i]] = i; } return {}; } ``` --- ### 2. **滑动窗口** #### 常见题型: - 最小覆盖子串(Hard) - 字符串的排列(Medium) - 无重复字符的最长子串(Medium) #### 模板代码(通用滑动窗口) ```cpp string minWindow(string s, string t) { unordered_map<char, int> need, window; for (char c : t) need[c]++; int left = 0, right = 0; int valid = 0; int start = 0, len = INT_MAX; while (right < s.size()) { char c = s[right++]; if (need.count(c)) { window[c]++; if (window[c] == need[c]) valid++; } while (valid == need.size()) { if (right - left < len) { start = left; len = right - left; } char d = s[left++]; if (need.count(d)) { if (window[d] == need[d]) valid--; window[d]--; } } } return len == INT_MAX ? "" : s.substr(start, len); } ``` --- ### 3. **链表** #### 常见题型: - 反转链表(迭代/递归) - 环形链表 II(找入口) - 合并两个有序链表 - K 个一组反转链表(Hard) #### 示例:反转链表(迭代) ```cpp struct ListNode { int val; ListNode *next; ListNode() : val(0), next(nullptr) {} ListNode(int x) : val(x), next(nullptr) {} }; ListNode* reverseList(ListNode* head) { ListNode* prev = nullptr; ListNode* curr = head; while (curr) { ListNode* next = curr->next; curr->next = prev; prev = curr; curr = next; } return prev; } ``` --- ### 4. **栈与队列** #### 常见题型: - 有效的括号(栈匹配) - 每日温度(单调栈) - 接雨水(Hard,双栈或双指针) - 用栈实现队列(设计题) #### 示例:单调栈(每日温度) ```cpp vector<int> dailyTemperatures(vector<int>& temperatures) { int n = temperatures.size(); vector<int> res(n, 0); stack<int> st; // 存储下标 for (int i = 0; i < n; ++i) { while (!st.empty() && temperatures[i] > temperatures[st.top()]) { int idx = st.top(); st.pop(); res[idx] = i - idx; } st.push(i); } return res; } ``` --- ### 5. **二叉树 & DFS/BFS** #### 常见题型: - 二叉树的最大深度(BFS/DFS) - 验证二叉搜索树(中序遍历) - 二叉树的最近公共祖先(LCA) - 层序遍历(BFS) #### 示例:层序遍历(BFS) ```cpp struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode() : val(0), left(nullptr), right(nullptr) {} TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> res; if (!root) return res; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int size = q.size(); vector<int> level; for (int i = 0; i < size; ++i) { TreeNode* node = q.front(); q.pop(); level.push_back(node->val); if (node->left) q.push(node->left); if (node->right) q.push(node->right); } res.push_back(level); } return res; } ``` --- ### 6. **回溯算法(Backtracking)** #### 常见题型: - 子集、组合、全排列 - N 皇后(Hard) - 复原 IP 地址 #### 示例:全排列 ```cpp vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> res; vector<int> path; vector<bool> used(nums.size(), false); function<void()> backtrack = [&]() { if (path.size() == nums.size()) { res.push_back(path); return; } for (int i = 0; i < nums.size(); ++i) { if (used[i]) continue; used[i] = true; path.push_back(nums[i]); backtrack(); path.pop_back(); used[i] = false; } }; backtrack(); return res; } ``` --- ### 7. **动态规划(Dynamic Programming)** #### 常见题型: - 爬楼梯(斐波那契) - 打家劫舍 I/II - 最长递增子序列(LIS) - 编辑距离(Hard) - 背包问题变种 #### 示例:最长递增子序列(LIS) ```cpp int lengthOfLIS(vector<int>& nums) { if (nums.empty()) return 0; vector<int> dp(nums.size(), 1); int maxLen = 1; for (int i = 1; i < nums.size(); ++i) { for (int j = 0; j < i; ++j) { if (nums[j] < nums[i]) { dp[i] = max(dp[i], dp[j] + 1); } } maxLen = max(maxLen, dp[i]); } return maxLen; } ``` > ⚠️ 进阶可用 `lower_bound` 实现 O(n log n) 解法。 --- ### 8. **二分查找** #### 常见题型: - 搜索旋转排序数组(Medium) - 寻找峰值(Medium) - 在排序数组中查找元素的第一个和最后一个位置 #### 示例:标准二分查找(左边界) ```cpp int searchLeftBound(vector<int>& nums, int target) { int left = 0, right = nums.size(); while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] < target) left = mid + 1; else right = mid; } return left < nums.size() && nums[left] == target ? left : -1; } ``` --- ### 9. **并查集(Union-Find)** #### 常见题型: - 岛屿数量(也可用DFS) - 朋友圈(省份数量) - 冗余连接 #### 并查集模板 ```cpp class UnionFind { public: vector<int> parent; int count; UnionFind(int n) : count(n) { parent.resize(n); for (int i = 0; i < n; ++i) parent[i] = i; } int find(int x) { if (parent[x] != x) parent[x] = find(parent[x]); // 路径压缩 return parent[x]; } void unite(int x, int y) { int rx = find(x), ry = find(y); if (rx != ry) { parent[rx] = ry; --count; } } }; ``` --- ### 10. **堆(优先队列)** #### 常见题型: - 数组中的第K个最大元素 - 前K个高频元素 - 合并K个升序链表 #### 示例:前K个高频元素 ```cpp vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> freq; for (int n : nums) freq[n]++; auto cmp = [](const pair<int,int>& a, const pair<int,int>& b) { return a.second < b.second; // 大顶堆 }; priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(cmp)> pq(cmp); for (auto& p : freq) { pq.push(p); } vector<int> res; for (int i = 0; i < k; ++i) { res.push_back(pq.top().first); pq.pop(); } return res; } ``` --- ## ✅ 三、学习建议 | 步骤 | 建议 | |------|------| | 第一步 | 掌握每类题型的**模板代码**和**通用思路** | | 第二步 | 按分类刷题(数组 → 链表 → 树 → 回溯 → DP) | | 第三步 | 总结变体题,比如“两数之和”有多种变形 | | 第四步 | 练习手写代码,避免依赖 IDE 自动补全 | | 第五步 | 模拟面试:限时答题 + 口述思路 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值