算法(第一周)

一周周五,总结一下本周的算法学习,从本周开始重新学习许久未见的算法,当然不同于大一时使用的 C 语言以及做过的简单题,现在是每天一题 C++ 和 JavaScript(还在学,目前只写了一题)

题单是代码随想录(JS)和面试 150 题(C++), 括号中是我所用的解题语言。

令我印象最深的不是题目的难度,而是 leecode 的提交方式与我所用过的学校 oj 还有洛谷都不同,它所提交的是核心代码,一开始我都是使用 ai 帮我浓缩成核心代码样式,后面发现其实只需要补充 public 中的函数就可以了...... 我还一直在想 leecode 的输入格式到底是什么样的,话不多说开始总结:

代码随想录
移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

示例 1:

复制代码
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

复制代码
输入:head = [], val = 1
输出:[]

示例 3:

复制代码
输入:head = [7,7,7,7], val = 7
输出:[]

提示:

  • 列表中的节点数目在范围 [0, 104]
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

这题是我觉得比较简单的一道题,复习一下链表知识就好

javascript 复制代码
var removeElements = function (head, val) {
  // 创建一个虚拟头节点
  const dummy = new ListNode(0);
  dummy.next = head;

  let current = dummy; // 从虚拟头节点开始遍历

  while (current.next !== null) {
    if (current.next.val === val) {
      // 跳过当前值等于 val 的节点
      //跳过了,就再也不存在这个地址,相当于删除这个元素
      current.next = current.next.next;
    } else {
      // 否则,继续移动到下一个节点
      current = current.next;
    }
  }

  // 返回新的头节点
  return dummy.next;
};

// 定义链表节点的结构
class ListNode {
  constructor(val = 0, next = null) {
    this.val = val;
    this.next = next;
  }
}
面试 150
多数元素

给定一个大小为 n的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

复制代码
输入:nums = [3,2,3]
输出:3

示例 2:

复制代码
输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109

**进阶:**尝试设计时间复杂度为 O (n)、空间复杂度为 O (1) 的算法解决此问题。

这题看上去很难,想明白了就很简单,简单来说是要找到出现最多的那个元素,但是要想明白怎么找最省力:我们假设找出一种生物,用一个计数器为 0,当这个生物遇到同类 + 1,遇到其他生物就会 - 1,当 cnt 为 0 时,说明这个生物数量不够,自然就 dead 了,以此类推,如果走到最后这个生物的数量没有为 0,则证明它是最多的一类,如果前面都不存在这种生物,那留在最后的自然而然就是最多的

cpp 复制代码
class Solution {
public:
          int majorityElement(vector<int>& nums) {
        int temp;
        int cnt = 0;
            for(int i = 0; i < nums.size();i++){
                if(cnt  == 0)
                temp = nums[i];

                cnt+= (nums[i] == temp) ? +1 : -1;
            }
        return temp;
    }
};
合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意: 最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n

示例 1:

复制代码
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

复制代码
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

复制代码
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[j] <= 109

这题也简单,只需要从后往前查找,比较 nums1 和 nums2 的大小,如果 nums1 大于 nums2 就往后放,反之就往前放,如果有多,nums1 未放完的自然都在前面,nums2 潍坊玩的只需要接着仿就行

cpp 复制代码
#include <vector>
using namespace std;

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i = m - 1; // nums1 的有效元素索引
        int j = n - 1; // nums2 的有效元素索引
        int k = m + n - 1; // 合并后数组的最后一个索引

        // 从数组的最后一位开始查找
        while (i >= 0 && j >= 0) {
            if (nums1[i] > nums2[j]) {
                // 将较大的放到合并数组的末尾
                nums1[k] = nums1[i];
                i--;
            } else {
                nums1[k] = nums2[j];
                j--;
            }
            k--;
        }

        // 将 nums2 剩余的元素复制到 nums1 中
        while (j >= 0) {
            nums1[k] = nums2[j];
            j--;
            k--;
        }
        // nums1 的剩余元素不需要复制,因为它们已经在正确的位置上
    }
};
罗马数字转整数

罗马数字包含以下七种字符: IVXLCDM

复制代码
字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

示例 1:

复制代码
输入: s = "III"
输出: 3

示例 2:

复制代码
输入: s = "IV"
输出: 4

示例 3:

复制代码
输入: s = "IX"
输出: 9

示例 4:

复制代码
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

复制代码
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= s.length <= 15
  • s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
  • 题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999]
  • 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
  • IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
  • 关于罗马数字的详尽书写规则,可以参考 罗马数字 -- 百度百科

这题需要学习一下关于 unordered_map,这里简单讲一下就是他可以存放名字+value,这样方便查找数字。思路是使用 for 循环遍历,使用 current 存储当前字母的数字,再用 next 去判断是否存在下一数字,只要存在就存入下一个字符的数字,不存在就入 0;然后进行比较,如果当前数字小于下一数字就减去当前数字,否则就加上当前数字,相等也要加上当前数字(IIII 这种情况),最后直接返回就行

cpp 复制代码
class Solution {
public:
    int romanToInt(string s) {
         // 罗马数字字符与整数的映射
    unordered_map<char, int> romanMap = {
        {'I', 1}, {'V', 5}, {'X', 10}, {'L', 50},
        {'C', 100}, {'D', 500}, {'M', 1000}
    };

    int total = 0;
    int n = s.length();

    for (int i = 0; i < n; ++i) {
        // 获取当前字符和下一个字符的数字
        int current = romanMap[s[i]];
        int next = (i + 1 < n) ? romanMap[s[i + 1]] : 0;

        // 如果当前数字小于下一个数字,执行减法
        if (current < next) {
            total -= current;
        } else {
            // 否则,执行加法
            total += current;
        }
    }

    return total;
    }
};
买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

复制代码
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

复制代码
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

这题假设第一天是最低买入价格,将最大利润设置成 0(不能为负,不然不是利润),遍历数组,持续更新最低买入价格,要是比这个最低价格高就说明可能会存在最大利润,使用 max 函数比较最大利润(用当前价钱减去最低价格)

cpp 复制代码
class Solution {
public:
   
 
 int maxProfit(vector<int>& prices) {
    if (prices.empty()) return 0;  // 防止空数组访问

    int minPrice = prices[0];  // 最低买入价格
    int maxProfit = 0;         // 最大利润

    for (int i = 1; i < prices.size(); i++) {
        if (prices[i] < minPrice) {
            minPrice = prices[i];  // 更新最低买入价格
        } else {
            maxProfit = max(maxProfit, prices[i] - minPrice);  // 计算当前利润
        }
    }

    return maxProfit;
}

};
删除有序数组中的重复项

给你一个 非严格递增排列 的数组 nums ,请你**原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k

判题标准:

系统会用下面的代码来测试你的题解:

复制代码
int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

示例 1:

复制代码
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 `2` ,并且原数组 nums 的前两个元素被修改为 `1`, `2 `

|---|-----|
| | |

不需要考虑数组中超出新长度后面的元素。

示例 2:

复制代码
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 `5` , 并且原数组 nums 的前五个元素被修改为 `0`, `1`, `2`, `3`, `4` 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 非严格递增 排列

这题开始时我没想清楚从前开始算,这个比较应该是后一位与前一位比较,如果不相同,就将该元素赋值到一个新数组,再 cnt++,cnt 就是新数组长度(新数组是在原数组基础上添加,如果不相同那么添加的位置与原位置也相同,如果元素相同则删除一个元素,不管怎么样在原数组基础上增删都不会超过原数组长度,而且节省了空间)

cpp 复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.empty()) return 0;  // 如果数组为空,返回 0

        int count = 1;  // count 用来追踪唯一元素的个数

        for (int i = 1; i < nums.size(); i++) {
            if (nums[i] != nums[i - 1]) {  // 如果当前元素和前一个不同
                nums[count++] = nums[i];   // 将当前元素移动到不重复部分的末尾
            }
        }

        return count;  // 返回不重复元素的个数
    }
};
移除元素

给你一个数组 nums和一个值 val,你需要 原地 移除所有数值等于 val的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

  • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
  • 返回 k

用户评测:

评测机将使用以下代码测试您的解决方案:

复制代码
int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
                            // 它以不等于 val 的值排序。

int k = removeElement(nums, val); // 调用你的实现

assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有的断言都通过,你的解决方案将会 通过

示例 1:

复制代码
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

示例 2:

复制代码
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意这五个元素可以任意顺序返回。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

与代码随想录题目相同,从 js 改成了 c++

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int newLength = 0;  // 用于记录新数组的有效长度
        
        // 遍历 nums 数组
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != val) {
                nums[newLength] = nums[i];  // 将非 val 元素放到前面
                newLength++;  // 更新新数组的长度
            }
        }
        return newLength;  // 返回新数组的长度
    }
};

本周题解就到这里结束,下周见。

相关推荐
地平线开发者1 分钟前
CPU& 内存加压工具 stress-ng 介绍
算法·自动驾驶
暮志未晚Webgl3 分钟前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
XY.散人4 分钟前
初识算法 · 分治(2)
算法
DanielYQ5 分钟前
LCR 001 两数相除
开发语言·python·算法
yngsqq9 分钟前
037集——JoinEntities连接多段线polyline和圆弧arc(CAD—C#二次开发入门)
开发语言·c#·swift
冉佳驹11 分钟前
数据结构 ——— 希尔排序算法的实现
c语言·数据结构·算法·排序算法·希尔排序
Zԅ(¯ㅂ¯ԅ)12 分钟前
C#桌面应用制作计算器进阶版01
开发语言·c#
过期的H2O213 分钟前
【H2O2|全栈】JS进阶知识(七)ES6(3)
开发语言·javascript·es6
一路冰雨24 分钟前
Qt打开文件对话框选择文件之后弹出两次
开发语言·qt
St_Ludwig26 分钟前
C语言 蓝桥杯某例题解决方案(查找完数)
c语言·c++·后端·算法·游戏·蓝桥杯