为了字节实习-Day1

0 阅读9分钟

今日目标

日期算法训练(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)

算法训练二

第一版的错误

主要错误:

  1. 哈希表类型拼写错误unorder_map 应改为 unordered_map
  2. 赋值逻辑错误nums[i] = it; 试图将整个键值对赋值给整数元素
  3. 访问哈希表方式错误myMap[it] 应为 it.second
  4. 数组索引越界风险:清空后直接通过索引赋值,未调整数组大小
  5. 逻辑错误:题目要求保留唯一元素,但代码尝试保留所有元素
  6. 返回值错误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(); // 返回唯一元素的数量
    }
};

关键修改方法说明:

  1. 改正拼写:将 unorder_map 改为标准的 unordered_map
  2. 使用 push_back 动态添加元素:避免直接通过索引赋值导致越界(原来数组也可以用)
  3. 正确遍历哈希表:通过 it.second 访问值
  4. 调整逻辑:确保每个唯一元素只被添加一次
  5. 直接返回数组大小:符合题目要求的 "前 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;

    }

};
第二版的错误
  1. 逻辑错误:题目要求原地删除重复元素,而我的代码清空了整个数组后重新插入元素,这违反了 "原地" 的要求。
  2. 语法错误it.first() 和 it.second() 应该直接使用 it.first 和 it.second,因为 it 是一个键值对。
  3. 顺序问题:哈希表不保证元素顺序,因此插入回 nums 的元素可能不是原来的顺序。(这个我是真的没有意识到,以为是默认输入顺序来输出)
  4. 返回值问题:返回的 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的初始值改为0i改为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],直接跳过ifk=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!)
错误分析
  1. 哈希表键值对设计错误

    • 代码将索引i作为键,target-nums[i]作为值。正确做法应该是将nums[i]作为键,索引i作为值,以便快速查找是否存在目标值。
  2. 嵌套循环导致时间复杂度升高

    • 使用两层循环遍历数组,时间复杂度为 O (n²),完全浪费了哈希表的 O (1) 查找优势。
  3. 未找到解时返回原数组

    • 题目保证有且仅有一个解,因此函数必须返回包含两个索引的向量,而不是原数组。
  4. 错误的重复元素处理

    • 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 {}; // 根据题目要求写的,其实这里根本不会执行到
    }
};

修正说明

  1. 哈希表设计优化

    • 键存储数组中的数值nums[i],值存储对应的索引i
  2. 单次遍历实现

    • 遍历数组时,检查target-nums[i]是否已存在于哈希表中。
    • 如果存在且不是当前元素自身,则直接返回结果。
    • 否则将当前元素插入哈希表。
  3. 正确处理重复元素

    • 由于先检查再插入,当遇到重复元素时,哈希表中存储的是前一个元素的索引,避免了使用相同元素的问题。

修正后的代码时间复杂度为 O (n),空间复杂度为 O (n),是解决该问题的最优 解。

我非常不熟悉的点
  1. if (hashMap.find(complement) != hashMap.end()) 这里的find函数用于在哈希表中查找指定键值对应的元素,若找到则返回指向该元素的迭代器,否则返回end()迭代器,end()迭代器指向哈希表中最后一个元素之后的位置。
  2. 我没仔细看例子,导致理解题意出错了,我以为不能使用相同的值,但其实是不能使用相同的元素(指针/地址不能)相同~
  3. 数组应该{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)
              
} 

主要问题:

  1. 参数类型错误:Go 语言中 slice 类型声明应为[]int,而非int[]
  2. 比较语句语法错误:Go 语言使用{}而非:分隔代码块
  3. 内层循环起始条件错误:冒泡排序内层循环应从0开始而非i
  4. 缺少必要缩进: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了,先去洗漱了,明天继续! 还是先发布了~现在已经是第二天了,第二天好多东西捏