算法训练01 | 数组part01_数组理论基础、704. 二分查找、27. 移除元素

本文介绍了数组理论基础,重点讲解了二分查找算法的原理和实现,以及如何在有序数组中移除元素的两种方法——暴力解法和双指针优化。通过实例演示,帮助读者掌握数组操作技巧。

参考文章

算法记录 | Day1数组基础 - 掘金

代码随想录算法训练营第一天 | 704. 二分查找、27. 移除元素-优快云博客

今日任务

第一章 数组part01

数组理论基础,704. 二分查找,27. 移除元素

详细内容:  

 数组理论基础  

文章链接:代码随想录

题目建议: 了解一下数组基础,以及数组的内存空间地址,数组也没那么简单;

 704. 二分查找 

题目建议: 大家能把 704 掌握就可以,35.搜索插入位置 和 34. 在排序数组中查找元素的第一个和最后一个位置 ,如果有时间就去看一下,没时间可以先不看,二刷的时候在看。

先把 704写熟练,要熟悉 根据 左闭右开,左闭右闭 两种区间规则 写出来的二分法

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

前提条件

  • 有序数组
  • 数组中无重复元素

middle的选取

参考文章:算法记录 | Day1数组基础 - 掘金

  • mid = (right + left) // 2这种写法会发生上溢问题。int 占用 4 字节,32 比特,数据范围为:-2147483648 ~ 2147483647 [-2^31 ~ 2^31-1]

    那么对于两个都接近 2147483647的数字而言,它们相加的结果将会溢出,变成负数。所以,为了避免溢出情况的发生,我们不采用这种写法。

  • mid = left + (right - left) // 2 从左指针到右指针的一半距离 + 左指针的下标 才是符合题的下标位置。

代码

左闭右闭的区间
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;                  // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) {                       // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2); // 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else {               // nums[middle] == target
                return middle;     // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

int main() {
    vector<int> nums = {-1, 0, 3, 5, 9, 12};
    int target = 9;
    Solution solution;
    cout << solution.search(nums, target) << endl;

    cin.get();
    return 0;
}
左闭右开的区间
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) {   // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else {               // nums[middle] == target
                return middle;     // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

int main() {
    vector<int> nums = {-1, 0, 3, 5, 9, 12};
    int target = 9;
    Solution solution;
    cout << solution.search(nums, target) << endl;

    cin.get();
    return 0;
}

27. 移除元素

题目建议:  暴力的解法,可以锻炼一下我们的代码实现能力,建议先把暴力写法写一遍。 双指针法 是本题的精髓,今日需要掌握,至于拓展题目可以先不看。 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili

双指针

  • 快指针先移动,如果不是目标元素,快指针指向就赋值给慢指针指向,慢指针移动

  • 如果是目标元素,则慢指针不操作,快指针继续移动。

  • 慢指针最后指向的下标,就是新数组中的大小

代码

暴力解法
#include <iostream>
#include <vector>
using namespace std;

// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for (int i = 0; i < size; i++) {
            if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
                for (int j = i + 1; j < size; j++) {
                    nums[j - 1] = nums[j];
                }
                i--;    // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                size--; // 此时数组的大小-1
            }
        }
        return size;
    }
};

int main() {
    vector<int> nums = {0, 1, 2, 2, 3, 0, 4, 2};
    int val = 2;
    Solution solution;
    int newSize = solution.removeElement(nums, val);

    // 输出修改后的 nums 数组
    cout << "Modified nums array: ";
    for (int i = 0; i < newSize; i++) {
        cout << nums[i] << " ";
    }
    cout << endl;

    // 输出修改后的数组大小
    cout << "New size of nums: " << newSize << endl;

    cin.get();
    return 0;
}
双指针
#include <iostream>
#include <vector>
using namespace std;

// 时间复杂度:O(n)
// 空间复杂度:O(1)
// 双指针方法
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

int main() {
    vector<int> nums = {0, 1, 2, 2, 3, 0, 4, 2};
    int val = 2;
    Solution solution;
    int newSize = solution.removeElement(nums, val);

    // 输出修改后的数组大小
    cout << "New size of nums: " << newSize << endl;

    // 输出修改后的 nums 数组
    cout << "Modified nums array: ";
    for (int i = 0; i < newSize; i++) {
        cout << nums[i] << " ";
    }
    cout << endl;

    cin.get();
    return 0;
}

### 如何使用字符模拟七段显示器显示时间 要通过字符模拟七段显示器来显示时间(小时、分钟和秒),可以通过定义每一段的亮灭状态来构建数字形状。以下是具体方法: #### 字符表示七段数码管 七段数码管由七个线段组成,分别标记为 `a` 到 `g`,以及一个小数点 `dp` 表示分隔标识。这些线段的状态决定了最终显示的数字。 | 数字 | a | b | c | d | e | f | g | |------|---|---|---|---|---|---|---| | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | | 2 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | | ... |...|...|...|...|...|...|...| 对于每个数字,可以根据其对应的二进制编码决定哪些段应该点亮。例如,数字 `0` 的二进制编码为 `0b0111111`,意味着除了 `g` 段外其他都应被点亮[^3]。 #### 显示逻辑设计 为了动态更新时间和显示效果,可以按照以下方式处理: - 使用字符串数组存储每个数字对应于七段码的形式。 - 定义一个函数用于绘制当前的时间值。 - 添加小数点作为分隔符,在特定位置控制它的显示与否。 下面是一个 Python 实现的例子: ```python import time # 定义7段数码管各数字对应的开关模式 SEGMENTS = { '0': " __ \n| |\n|__|", '1': " \n |\n |", '2': " __ \n __|\n|__ ", '3': " __ \n __|\n __|", '4': " \n|__|\n |", '5': " __ \n|__ \n __|", '6': " __ \n|__ \n|__|", '7': " __ \n |\n |", '8': " __ \n|__|\n|__|", '9': " __ \n|__|\n __|" } def draw_time(hour, minute, second): """ 绘制给定时间 """ h1, h2 = divmod(hour, 10) m1, m2 = divmod(minute, 10) s1, s2 = divmod(second, 10) lines = ["", "", ""] segments = [h1, h2, m1, m2, s1, s2] for i in range(6): # 遍历六个数字的位置 digit_str = SEGMENTS[str(segments[i])] part_lines = digit_str.split("\n") if i % 2 != 0 and i < 4: # 如果是偶数位且不是最后一位,则加个小数点 part_lines[1] += "." for j in range(len(part_lines)): lines[j] += (" " + part_lines[j]) if (i > 0) else part_lines[j] return "\n".join(lines) while True: current_time = time.localtime() formatted_time = draw_time(current_time.tm_hour%12 or 12, current_time.tm_min, current_time.tm_sec) print(formatted_time) time.sleep(1) ``` 此脚本会持续刷新屏幕上的时间展示,并利用 ASCII 图形近似呈现实际硬件中的 LED 效果[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值