9 min to read
Leetcode刷题记录
到头来还是得做一做算法题

目录
哈希
子串
数组
矩阵
链表
哈希
两数之和
题目链接:leetcode 1
题解:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
/*
需要修改原始数据 vector<int>& nums
只读取数据,不修改 const vector<int>& nums
需要原始数据的独立副本(罕见) vector<int> nums(值传递)
unordered_map<int, int> hashtable; # 构建一个哈希表,key是数值,value是索引
*/
for (int i = 0; i < nums.size(); ++i) {
auto it = hashtable.find(target - nums[i]); // auto自动判断类型
if (it != hashtable.end()) { // 没找到返回 end() 迭代器
return {it->second, i}; // 通过 it->second 获取哈希表中存储的索引
}
hashtable[nums[i]] = i; // 由于只有唯一答案,在寻找后将元素加入
}
return {};
}
};
分析
- 题干讲到只有唯一解,每次寻找后再将元素加入哈希表,后续会将该元素纳入查找范围,避免了查找自己,又没有遗漏
子串
和为K的子数组
题目链接:leetcode 560
题解:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp; // 构造一个哈希表用于记录前缀和出现的次数
int count = 0; // 用于记录当前的前缀和
int ans = 0;
for(int i = 0; i < nums.size(); i++){
mp[count]++; // 记录当前位置前缀和的数量
count += nums[i];
if(mp.find(count - k) != mp.end()){ // 查找是否存在从某个位置到当前位置和为K
ans += mp[count - k]; // 加上满足该条件前缀和的数量
}
}
return ans;
}
};
分析
- 由于数据可能有负数,从当前位置向前看,前缀和满足条件的有多个,即有相同的前缀和,因此哈希表记录的是前缀和的值,以及其出现次数
数组
最大子数组和
题目链接:leetcode 53
题解:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> maxNow(nums.size()); // 初始化一个动态数组,用于记录以i结尾的最大子数组和
maxNow[0] = nums[0]; // 由于后面有用上 i-1 这里要进行初始化
int ansMax = nums[0]; // 遍历一遍找到最大的值
for(int i = 1; i < nums.size();i++){
maxNow[i] = max(nums[i] + maxNow[i-1], nums[i]); // 确定是否要加上前面的部分
ansMax = max(ansMax, maxNow[i]);
}
return ansMax;
}
};
分析
- 整体的最大子数组和并不是以最后一个元素结尾的最大子数组和,需要寻找,ansMax就是这个作用
- i结尾的最大子数组和,就看i-1结尾的最大子数组和是否大于0,再加上第i个元素
矩阵
矩阵置零
题目链接:leetcode 73
题解:
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int col = matrix.size();
int row = matrix[0].size();
vector<bool> col_zero(col, false); // 用于记录有0的行
vector<bool> row_zero(row, false); // 用于记录有0的列
for(int i = 0; i < col; i++){
for(int j = 0; j < row; j++){
if(matrix[i][j] == 0){
col_zero[i] = row_zero[j] = 1;
}
}
}
for(int i = 0; i < col; i++){
for(int j = 0; j < row; j++){
if(col_zero[i] ==1 || row_zero[j] == 1){
matrix[i][j] = 0;
}
}
}
}
};
分析
- 其实就是思路上用行和列来表示位置即可
链表
相交链表
题目链接:leetcode 160
题解:
/**
* Definition for singly-linked list.
* struct ListNode { // 自定义的链表
* int val;
* ListNode *next; // *是地址指示符,表示这里的next为一个ListNode类型的指针
* ListNode(int x) : val(x), next(NULL) {} // 构造函数,若还有next参数,eg: ListNode(6, next)
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode *> set; // 定义一个哈希表用于存地址,unordered_set用于储存唯一元素
ListNode *temp = headA; // temp是一个地址,*是在定义中起到一个标识的作用
while(temp != NULL){ // 在定义外使用不用加*,表示的仍然为一个地址,加上*反而是表示该地址对应的数据
set.insert(temp); // unordered_set增加元素用insert()方法
temp = temp->next; // ->是成员访问运算符,用于通过指针访问对象的成员(变量或函数),等价于先对指针解引用(*),再用点号(.)访问成员
}
temp = headB;
while(temp != NULL){
if(set.find(temp) != set.end()){
return temp;
}
temp = temp -> next;
}
return {};
}
};
分析
- 思路上很简单就是用哈希表存一份A的地址,再遍历B看是否有地址在该哈希表内
- 这里主要用来熟悉链表的构造与使用
反转链表
题目链接:leetcode 206
题解:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* backward = nullptr; // 第一个元素要指向NULL,因此反向链表先初始化一个NULL
ListNode *forwoard;
while(cur != nullptr){
forwoard = cur -> next; // 对于forward的定义是正向链表当前位置的下一个元素
cur -> next = backward; // 将当前位置next反向
backward = cur; // 沿正向方向移动
cur = forwoard; // 沿正向方向移动
}
return backward;
}
};
分析
- 从head开始,将其next变为反转后的下一个,再迭代
回文链表
题目链接:leetcode 234
题解:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
// 方法一:将值复制到数组中后用双指针法
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode *temp = head;
vector<int> nodeVal; // 记录下链表中所有的值
while(temp != nullptr){
nodeVal.push_back(temp->val);
temp = temp->next;
}
for(int i = 0; i < nodeVal.size(); i++){
if(nodeVal[i] != nodeVal[nodeVal.size()-1-i]){ // 双指针检测
return false;
}
}
return true;
}
};
// 方法二:递归
class Solution {
ListNode *front; // 定义一个全局的front
public:
bool recursively(ListNode *back){
if(back != nullptr){ // 当深入到链表最后一个节点时递归终止
if(!recursively(back->next)){ // 递归的核心
return false; // 回溯时若出现不匹配的直接false,然后会一层层上传至退出
}
if(front->val != back->val){ // 比较前后指针
return false;
}
front = front->next;
}
return true; // 两重作用:1.递归到最后一层返回true,从而开始比较 2.在每一层前后比较相同时返回true
}
bool isPalindrome(ListNode* head) {
front = head;
ListNode *back = head;
return recursively(back);
}
};
分析
- 方法一思路上很简单,将链表内容复制一份,然后前后比较
- 方法二到链表最后一个节点时,由于back是NULL所以会返回true,其上一层就会继续运行对节点的比较
环形链表
题目链接:leetcode 141
题解:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_set<ListNode *> set; // 创建一个用于存储节点地址的哈希表
ListNode *temp = head;
while(temp != NULL){
if(set.find(temp) != set.end()){
return true;
}
set.insert(temp);
temp = temp->next;
}
return false;
}
};
分析
- 在哈希表中发现已经存过的地址即返回true