今日目标
日期 | 算法训练(2h) | Go语言与Gin(2h) | 网络/系统(1h) | 项目实践(1h) |
---|---|---|---|---|
Day1 | 数组:LC26(去重)、LC1(两数之和) | Go环境搭建 + 实现冒泡排序(带单元测试) | 图解HTTP第1章 + Wireshark抓包分析HTTP请求 | GitHub创建仓库,提交冒泡排序代码 |
今日成果
算法训练一
数组-26:删除有序数组中的重复项
题目链接:leetcode.cn/problems/re…
第一版(给以后的自己看一看以前的我有多菜)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k=0;
// 使用迭代器遍历,做一个哈希表,key是按顺序的数字,value是次数
std::unorder_map<int,int> myMap;
for(auto it : nums){
myMap[it]++;
}
// 清空nums的值
nums.clear();
// 然后在遍历哈希表,写回数组nums,并且记录value==1的,k++
int i = 0;
for(auto it : myMap){
nums[i] = it;
if (myMap[it] == 1){
k++;
}
}
// k的值为nums大小
return k;
}
};
// 时间复杂度:只有两个 for 循环,应该是O(n)
// 空间复杂度:只有一个一维数组nums和哈希表myMap,应该是O(n)
算法训练二
第一版的错误
主要错误:
- 哈希表类型拼写错误:
unorder_map
应改为unordered_map
- 赋值逻辑错误:
nums[i] = it;
试图将整个键值对赋值给整数元素 - 访问哈希表方式错误:
myMap[it]
应为it.second
- 数组索引越界风险:清空后直接通过索引赋值,未调整数组大小
- 逻辑错误:题目要求保留唯一元素,但代码尝试保留所有元素
- 返回值错误:
k
被错误地用于记录出现一次的元素,而非唯一元素总数
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.empty()) return 0;
// 用于记录每个数字出现的次数
std::unordered_map<int, int> countMap;
// 统计每个数字的出现次数
for (int num : nums) {
countMap[num]++;
}
// 清空原数组
nums.clear();
// 将唯一的数字按原顺序添加回数组
for (int num : nums) {
if (countMap[num] > 0) {
nums.push_back(num);
countMap[num] = 0; // 标记已添加,避免重复
}
}
return nums.size(); // 返回唯一元素的数量
}
};
关键修改方法说明:
- 改正拼写:将
unorder_map
改为标准的unordered_map
- 使用
push_back
动态添加元素:避免直接通过索引赋值导致越界(原来数组也可以用) - 正确遍历哈希表:通过
it.second
访问值 - 调整逻辑:确保每个唯一元素只被添加一次
- 直接返回数组大小:符合题目要求的 "前 k 个元素包含所有唯一元素"
第二版(根据AI指出的错误尝试修改,但是我还没看AI优化的代码)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k=0;
// 使用迭代器遍历,做一个哈希表,key是按顺序的数字,value是次数
std::unordered_map<int,int> myMap;
for(auto it : nums){
myMap[it]++;
}
// 清空nums的值
nums.clear();
// 然后在遍历哈希表,写回数组nums,并且记录value==1的,k++
int i = 0;
for(auto it : myMap){
nums.push_back(it.first());
if (it.second() == 1){
k++;
}
}
// k的值为nums大小
return k;
}
};
第二版的错误
- 逻辑错误:题目要求原地删除重复元素,而我的代码清空了整个数组后重新插入元素,这违反了 "原地" 的要求。
- 语法错误:
it.first()
和it.second()
应该直接使用it.first
和it.second
,因为it
是一个键值对。 - 顺序问题:哈希表不保证元素顺序,因此插入回
nums
的元素可能不是原来的顺序。(这个我是真的没有意识到,以为是默认输入顺序来输出) - 返回值问题:返回的
k
是只出现一次的元素数量,而题目通常要求返回去重后的数组长度。
修正后的代码
以下是修正后的代码,使用双指针法原地删除重复元素:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.empty()) return 0;
int k = 1; // 初始长度为1,因为至少有一个元素
for (int i = 1; i < nums.size(); i++) {
if (nums[i] != nums[i-1]) {
nums[k] = nums[i]; // 移动到下一个位置
k++;
}
}
return k;
}
};
代码解释
-
双指针法:使用
k
作为慢指针,记录去重后的数组长度。 -
遍历数组:当遇到不同的元素时,将其移动到
k
位置,并增加k
。 -
返回值:
k
即为去重后的数组长度。
这个解法满足题目要求的 O (1) 额外空间和 O (n) 时间复杂度。
第三版(因为我不会双指针法,所以我先详细看了AI的代码)
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 1;
if(nums.empty()){
return 0;
} else {
for(int i = 1; i < nums.size(); i++){
if(nums[i]!=nums[i-1]){
nums[k] = nums[i];
k++;
}
}
}
return k;
}
};
这是在查看了AI代码后写出来的,其中我有一个疑惑:for循环里面的if为什么是判断 nums[i]
和 nums[i-1]
的是否相等,而不是 nums[i]
和 nums[i+1]
是否相等,于是我就把k
的初始值改为0
,i
改为0
开始,赋值改为nums[k]=nums[i]
,return改为k+1
;但这样运行到第50个样例[1,1,2]
就出错了,因为这样做的话,数组nums
是不会收录他的最后一个单独的unique
元素,所以我们要用判断语句if(nums[i]!=nums[i-1])
除非我们手动增加nums[k] = nums[nums.size() - 1]
,k++
(这里主要看倒数第一和倒数第二:如果它们相等(比如[1,1]
,直接跳过if
,k=0
,然后进行下一步,如果它们不相等,那么后面的那一步就可以处理了。所以
nums[k] = nums[nums.size() - 1];
k++;
叫做处理最后一个元素~
算法训练二
数组-1:两数之和:
题目链接:leetcode.cn/problems/tw…
第一版
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map<int,int> myMap;
for(int i = 0; i < nums.size(); i++){
// 去除使用相同元素的情况
if(target == 2 * nums[i]){
continue;
}
myMap[i] = target-nums[i]; // 错误1:哈希表键值对设计错误
}
for(int i = 0; i < nums.size(); i++){
for(int j = 0; j < nums.size(); j++){ // 错误2:嵌套循环导致O(n²)复杂度
if(myMap[i]){
if(nums[j] == myMap[i]){
return {i,j};
}
}
}
}
return nums; // 错误3:未找到解时返回原数组,逻辑错误
}
};
第一版错题(我以前做过一次了,可恶,还是错了,而且还想不起来应该在这道题合理运用哈希表来减少时间复杂度了,damn!)
错误分析
-
哈希表键值对设计错误:
- 代码将索引
i
作为键,target-nums[i]
作为值。正确做法应该是将nums[i]
作为键,索引i
作为值,以便快速查找是否存在目标值。
- 代码将索引
-
嵌套循环导致时间复杂度升高:
- 使用两层循环遍历数组,时间复杂度为 O (n²),完全浪费了哈希表的 O (1) 查找优势。
-
未找到解时返回原数组:
- 题目保证有且仅有一个解,因此函数必须返回包含两个索引的向量,而不是原数组。
-
错误的重复元素处理:
if(target == 2 * nums[i])
的判断是错误的。例如当 target=4,数组为[2,2]
时,正确答案是两个索引 0 和 1,但代码会跳过所有重复元素。
AI的正确答案
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map<int, int> hashMap; // 键:数值,值:索引
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums[i];
// 检查哈希表中是否存在complement且不是当前元素自身
if (hashMap.find(complement) != hashMap.end()) {
return {hashMap[complement], i};
}
// 将当前元素插入哈希表
hashMap[nums[i]] = i;
}
return {}; // 根据题目要求写的,其实这里根本不会执行到
}
};
修正说明
-
哈希表设计优化:
- 键存储数组中的数值
nums[i]
,值存储对应的索引i
。
- 键存储数组中的数值
-
单次遍历实现:
- 遍历数组时,检查
target-nums[i]
是否已存在于哈希表中。 - 如果存在且不是当前元素自身,则直接返回结果。
- 否则将当前元素插入哈希表。
- 遍历数组时,检查
-
正确处理重复元素:
- 由于先检查再插入,当遇到重复元素时,哈希表中存储的是前一个元素的索引,避免了使用相同元素的问题。
修正后的代码时间复杂度为 O (n),空间复杂度为 O (n),是解决该问题的最优 解。
我非常不熟悉的点
if (hashMap.find(complement) != hashMap.end())
这里的find
函数用于在哈希表中查找指定键值对应的元素,若找到则返回指向该元素的迭代器,否则返回end()
迭代器,end()
迭代器指向哈希表中最后一个元素之后的位置。- 我没仔细看例子,导致理解题意出错了,我以为不能使用相同的值,但其实是不能使用相同的元素(指针/地址不能)相同~
- 数组应该
{hashMap[complement], i}
这样返回,而不是[1,2]
Go语言
Go环境搭建
这个在mac上很简单,直接问一下豆包就好了
实现冒泡排序(带单元测试)
第一次用 go 写的冒泡算法,我写错了可恶
package main;
import "fmt";
func bubbleSort(arr int[]){
n := len(arr)
for i := 0; i < n - 1; i++ {
for j := i; j < n - 1; j++ {
if arr[j] > arr[j+1]:
temp := arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
func main(){
arr := []int{12,423,3554,25,6,23,5,36,7,764,1}
bubbleSort(arr)
fmt.Println("The array after sorting:", arr)
}
主要问题:
- 参数类型错误:Go 语言中 slice 类型声明应为
[]int
,而非int[]
- 比较语句语法错误:Go 语言使用
{}
而非:
分隔代码块 - 内层循环起始条件错误:冒泡排序内层循环应从
0
开始而非i
- 缺少必要缩进:Go 语言对代码格式有严格要求
优化建议:
可以添加提前终止条件,当某轮循环没有发生交换时,说明数组已完全有序:
第二版(优化版)
package main
import (
"fmt"
)
func bubbleSort(arr []int){
n := len(arr)
for i := 0; i < n; i++ {
flag := true
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
flag = false
}
}
if flag {
break
}
}
}
func main() {
arr := []int{12,12423,43,452,3,453,3241,6,23,621,23,0,-234,23,-2}
bubbleSort(arr)
fmt.Println("The array after up_sorting:", arr)
}
还没有弄完单元测试呜呜呜,但现代已经第二天00:15
了,先去洗漱了,明天继续!
还是先发布了~现在已经是第二天了,第二天好多东西捏