欢迎来到我的博客 链接到标题

力扣速刷


开始写博客 链接到标题

一、哈希 链接到标题

  1. 两数之和

    class Solution {
    public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        // 外层循环:选定第一个数 nums[i]
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) { 
                if (nums[i] + nums[j] == target) {
                    return {i, j}; // 找到了,直接返回它们的下标
                }
            }
        }
        return {}; // 没找到
    }
    };
    

2.字母异位词分组 3***

   class Solution {
public:
 vector<vector<string>> groupAnagrams(vector<string>& strs) {
 // 创建一个哈希表
 // 键 (key) 是排序后的字符串,值 (value) 是所有属于这个键的原始字符串集合
 unordered_map<string, vector<string>> mp;
     for (string& str : strs) {
        string key = str;
        // 对字符串内部的字符进行排序,获取“特征键”
        sort(key.begin(), key.end());
        // 将原始字符串放入对应键的数组中
        mp[key].push_back(str);
    }

    // 把哈希表里分好组的结果提取出来
    vector<vector<string>> ans;
    
    for (auto it = mp.begin(); it != mp.end(); ++it) {
        ans.push_back(it->second);
    }

    return ans;
}
   };

二、双指针 链接到标题

1、移动零 :把所有不是0的数移到最前面就是把所有0移到最后面。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow = 0; // 慢指针,记录下一个非 0 元素存放的位置
        int n = nums.size();
        // fast 遍历整个数组
        for (int fast = 0; fast < n; ++fast) {
            // fast 找到了非 0 元素!
            if (nums[fast] != 0) {
                // 将找到的非 0 元素,交换到 slow 指定的安全位置
                std::swap(nums[slow], nums[fast]);
                // slow 向前一步,准备接收下一个
                slow++; 
            }
        }
    }
};

std::swap(nums[slow], nums[fast]);

三、移动窗口 链接到标题

1.无重复字符的最长子串 1*

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> window;
        int left = 0, max_length = 0;
        int winsize =  0;
        for (int right = 0; right < s.size(); right++) {
            // 收缩窗口直到无重复
            while (window.count(s[right])) {
                window.erase(s[left]);
                left++;
            }
            
            // 扩展窗口
            window.insert(s[right]);
            winsize = window.size();
            // ✅ 记录最长长度(不是保留窗口!)
            max_length = max(max_length, winsize);
        }
        
        return max_length;  // 只返回最长长度
    }
};

四、子串 链接到标题

1.和为 K 的子数组 (没看懂)

class Solution {
public:
 int subarraySum(vector<int>& nums, int k) {
 // 哈希表:用来记录【某个前缀和】出现的【次数】
 unordered_map<int, int> preSumMap;
    // 极其重要的一步初始化!
    // 意味着在没有遍历任何元素前,前缀和为 0 的情况已经出现了 1 次。
    // 这是为了处理那些“从第 0 个元素开始,刚好等于 k”的子数组。
    preSumMap[0] = 1; 

    int count = 0;   // 记录符合条件的子数组个数
    int preSum = 0;  // 记录当前的累加前缀和

    for (int i = 0; i < nums.size(); ++i) {
        preSum += nums[i]; // 累加当前数字

        // 核心逻辑:去哈希表里找,有没有历史前缀和等于 preSum - k 的?
        int target = preSum - k;
        if (preSumMap.count(target)) {
            // 如果有,说明我们找到了一段(或多段)和为 k 的子数组
            // 历史前缀和出现了几次,我们就加上几次
            count += preSumMap[target]; 
        }

        // 把当前的前缀和也存进哈希表,作为未来元素的“历史”
        preSumMap[preSum]=preSumMap[preSum] +1 ;
        //对 presum 这个 key 对应的 value 加1 
    }

    return count;
}
    };

五、普通数组 链接到标题

1.最大子数组和 1*

    class Solution {
public:
 int maxSubArray(vector<int>& nums) {
 // pre 记录“以当前数字结尾的子数组的最大和” (即当前的 current_sum)
 int pre = 0; 
// maxAns 记录整个遍历过程中见过的最大的和,初始化为第一个元素
 int maxans = nums[0];
    for (int i = 0; i < nums.size(); ++i) {
        // 核心状态转移方程:
        // 比较“我自己单干(nums[i])”和“加入前面的队伍(pre + nums[i])”哪个更大
        pre = max(nums[i], pre + nums[i]);

        // 每走一步,都更新一下我们见过的历史最高记录
        maxans = max(maxans, pre);
    }
    return maxans;
}
   };


 第二种暴力解法
    class Solution {
public:
    int maxSubArray(vector<int>& nums) {

    int n = nums.size();
    int maxSum = nums[0]; // 至少包含一个元素

    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = i; j < n; j++) {
            sum += nums[j];          // 累加,避免重复求和
            maxSum = max(maxSum, sum);
        }
    }
    return maxSum;
}
};

pre = max (pre,pre+nums[i])

六、矩阵 链接到标题

1.矩阵置零 2**

    class Solution {
public:
 void setZeroes(vector<vector<int>>& matrix) {
 int m = matrix.size();
 int n = matrix[0].size();
    // 准备两个“记仇本”,初始都没有仇 (false)
    vector<bool> row(m, false); 
    vector<bool> col(n, false); 

    // 第一遍扫描:记仇
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == 0) {
                row[i] = true; // 记下第 i 行有 0
                col[j] = true; // 记下第 j 列有 0
            }
        }
    }

    // 第二遍扫描:清算
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            // 只要行或者列在记仇本上,统统变成 0
            if (row[i] || col[j]) {
                matrix[i][j] = 0;
            }
        }
    }
}
    };

vector row(m, false);

七、链表 链接到标题

1.相交链表

  class Solution {
 public:
 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { 
if (headA == nullptr || headB == nullptr) return nullptr;

ListNode *pA = headA;
 ListNode *pB = headB;

// 只要两个指针没相遇,就一直走
 while (pA != pB) {
  // pA 走到底了,就切换到 headB;否则继续走 next
  pA = (pA == nullptr) ? headB : pA->next;
  // pB 走到底了,就切换到 headA;否则继续走 next
  pB = (pB == nullptr) ? headA : pB->next;
  if(pa == pb)
  {
      return pa;
  }
  }

// 相遇时,要么都在交点,要么都在 null
 return nullptr;
 }
 };

while (pA != pB)

2.反转链表 2*

/**

- 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* prev = nullptr; // 前一个节点,初始为 null
  ListNode* curr = head; // 当前节点,初始为头节点

  while (curr != nullptr) {
   // 1. 探子先去记住下一个节点,防止链表断裂丢失
   ListNode* tmp = curr->next;

  // 2. 核心反转:当前节点“背叛”,掉头指向前一个节点
   curr->next = prev;

  // 3. 准备下一步:prev 向前走一步
   prev = curr;

  // 4. 准备下一步:curr 向前走一步(走到刚才探子记住的位置)
   curr = tmp;
  }

  // 循环结束时,curr 变成了 null,而 prev 刚好停在原本的最后一个节点
  // 也就是新链表的头节点!
  return prev;
  }
  };

while (curr != nullptr)

return slow;          

八、二分查找 链接到标题

1.搜索插入位置

    class Solution {
public:
 int searchInsert(vector<int>& nums, int target) {
 int left = 0;
 int right = nums.size() - 1;
    // 当 left <= right 时,搜索区间依然有效
    while (left <= right) {
        // 计算中间位置 (防止直接相加导致整型溢出的标准写法)
        int mid = left + (right - left) / 2;

        if (nums[mid] == target) {
            return mid;       // 运气好,直接找到了
        } else if (nums[mid] < target) {
            left = mid + 1;   // 目标更大,去右半边找
        } else {
            right = mid - 1;  // 目标更小,去左半边找
        }
    }

    // 循环结束还没找到?没关系,left 当前的位置就是完美的插入点
    return left; 
}
    };

int mid = left + (right - left) / 2;

2.搜索二维矩阵

    class Solution {
public:
 bool searchMatrix(vector<vector<int>>& matrix, int target) {
 if (matrix.empty() || matrix[0].empty()) return false;
    int m = matrix.size();
    int n = matrix[0].size();

    // 想象成一维数组的左右边界
    int left = 0;
    int right = m * n - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;

        // 核心映射:将一维下标 mid 转换为二维坐标
        int mid_val = matrix[mid / n][mid % n];

        if (mid_val == target) {
            return true;       // 找到了!
        } else if (mid_val < target) {
            left = mid + 1;    // 去右半边找
        } else {
            right = mid - 1;   // 去左半边找
        }
    }

    return false; // 找遍了都没有
}
    };

int mid_val = matrix[mid / n][mid % n];

九、动态规划 链接到标题

十、贪心算法 链接到标题

十一、技巧 链接到标题

1.只出现一次的数字

int singleNumber(vector<int>& nums) {
    int res = 0;
    for (int num : nums) {
        res ^= num;
    }
    return res;
}

持续更新 链接到标题

我会定期更新这个博客,分享更多有趣的内容。

感谢您的访问!


同时在此声明 本次博客网址搭建感谢我的好朋友Gemini提供技术细节支持,感谢GLM与Claude Code提供技术实现支持,感谢王甫12135、刻舟求剑的人、男男等好友提供的GLM密匙与环境搭建。