力扣 无重复字符的最长字串-3

无重复字符的最长字串-3

cpp 复制代码
class Solution {
public:
    // 解决方法:双指针
    int lengthOfLongestSubstring(string s) { 
        // 如果字符串为空,直接返回0
        if (s.length() == 0)
            return 0;
        // 如果字符串不为空,字符串每个字符都不同的情况下,最长字串的长度为1,所以Max初始化为1
        int left = 0, right = 0, Max = 1;
        //字符变量t用来存放每次两个指针范围内的后一个字符,用于与两个指针范围内的所有元素判断是否相同
        char t = 0;
        while (left < s.length()) {
            while (right < s.length() - 1) {
                //flag初始化为1用于标记,方便后续判断
                int flag = 1;
                //t赋值为两个指针范围内的后一个字符
                t = s[right + 1];
                //循环判断t是否与两个指针内有相同的字符
                for (int f = left; f <= right; f++) {
                //如果有相同字符flag标记为0,并break结束循环
                    if (s[f] == t) {
                        flag = 0;
                        break;
                    }
                }
                /*如果flag为零,说明双指针范围的后一个字符与双指针范围内有相同字符,后续的字符也没有必要判断了,break结束循环
                因为此情况下无论范围再怎么扩大(right++),双指针范围内总是有这个的重复字符,不符合题意。*/
                if (flag == 0)
                    break;
                /*如果flag为1,说明双指针范围的后一个字符与双指针范围内没有相同字符,则把双指针范围的后一个字符括入到双指针范围内(right++)
                并与Max比较找到最大字串*/
                if (flag) {
                    right++;
                    Max = max(Max, right - left + 1);
                }
            }
            //内层while循环结束说明,字符串从头到尾已经遍历完,或者找到了相同字符break结束循环了
            //那么继续缩小左指针的范围(left),继续从左值针的位置开始查找最长字串,一直找到结尾
            left++;
            right = left;
        }
        return Max;
    }
};

每日问题

什么是 C++ 中的移动语义?它的作用是什么?

C++中的移动语义

移动语义是C++11中引入的一种特性,通过引入右值引用(T&&),支持资源的高效转移,而无需昂贵的拷贝操作。移动语义的核心作用是通过移动而不是复制的方式管理资源,从而提升程序性能,特别是在处理动态分配的资源(如内存、文件句柄等)时。、

移动语义的关键概念

1.右值引用(T&&):

是一个能够绑定到右值(临时对象)的引用类型。

它允许开发者直接访问右值对象的资源,并安全地将资源转移到其他对象中。

2.移动构造函数和移动赋值运算符:

是实现移动语义的两大工具。

它们负责接收一个右值引用参数,从而将资源从一个对象移动到另一个对象。

作用

1.提高性能:

在传递或返回大型对象时,通过移动语义可以避免昂贵的拷贝操作,直接转移资源的所有权。

常用于容器类(如std:vector)在插入、删除或交换元素时。

2.避免资源浪费:

临时对象通常会在作用域结束时被销毁,移动语义使得这些资源可以被重复利用,而不是浪费掉。

3.支持现代化C++编程:

与标准库(如std:move)结合,为开发者提供了更多灵活性和效率。

移动语义的实现

移动构造函数
cpp 复制代码
class MyClass {
private:
    int* data;

public:
    // 构造函数
    MyClass(int value) : data(new int(value)) {}

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr; // 资源转移,确保原对象不再持有资源
    }

    // 析构函数
    ~MyClass() {
        delete data; // 释放资源
    }
};
  • 这是一个移动构造函数,接受一个右值引用 other(右值或临时对象)。
  • 实现了资源转移:
    • 将 other.data 的值转移给当前对象的 data。
    • 将 other.data 置为 nullptr,避免原对象在销毁时重复释放资源。
  • noexcept 表示此操作不会抛出异常,确保移动操作在某些场景(如标准容器的优化)中安全进行。
移动赋值运算符
cpp 复制代码
class MyClass {
private:
    int* data;

public:
    // 构造函数
    MyClass(int value) : data(new int(value)) {}

    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {         // 防止自我赋值
            delete data;              // 释放当前资源
            data = other.data;        // 转移资源
            other.data = nullptr;     // 确保原对象不再持有资源
        }
        return *this;
    }

    ~MyClass() {
        delete data;
    }
};
配合标准库使用
1.std::move

用于将一个左值显示转换为右值,从而启用移动语义。

常见用法时将一个临时对象的资源转移到另一个对象中。

示例:

cpp 复制代码
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1 的资源转移到 v2,避免拷贝
2.std::forward

通常用于完美转发函数模板参数,确保参数的值类别(左值或右值)正确传递。

使用场景

1.容器优化:

C++标准库容器(如std::vector、std::string)在插入或删除元素时,利用移动语义大幅减少不必要的拷贝。

2.返回值优化:

函数返回临时对象时,可以直接移动对象资源,避免临时对象的构造和销毁。

示例:

cpp 复制代码
MyClass createObject() {
    MyClass obj(42);
    return obj; // 编译器自动应用移动语义
}

3.避免多余的动态分配:

动态资源管理类(如文件句柄、数据库连接)可以通过移动语义避免资源的重复分配和释放。

移动语义是 C++11 的重要特性,专注于高效的资源管理和性能优化。它通过右值引用、移动构造函数和移动赋值运算符,避免了不必要的拷贝操作,特别适合处理动态资源密集型任务。在现代 C++ 编程中,移动语义和标准库(如 std::move)的结合已经成为编写高效代码的重要工具。

右值引用是什么?如何使用右值引用实现移动语义?

右值引用是什么?

右值引用(T&&)是C++11引入的一种新类型引用,允许程序员直接操作右值(临时对象)。右值引用的主要目的是支持移动语义和完美转发。

右值与左值的区别

左值:表示一个内存中有确定存储地址的对象,可以被持久引用或操作。

例如:变量名、数组元素。

int a = 5;中的a是左值。

右值:表示临时对象或者表达式结果,通常无法直接获取地址。

例如:字面值、临时对象、函数返回的非引用置。

5、a + b的结果是右值。

右值引用可以绑定到右值上,与普通左值引用(T&)不同。

右值引用的语法

cpp 复制代码
int&& r = 10;  // r 是右值引用,绑定到字面值 10
int x = 20;
int&& r2 = x + 5;  // r2 是右值引用,绑定到临时值 25

右值引用允许我们直接获取右值的资源,并通过移动语义避免拷贝操作。

右值引用和移动语义

移动语义的核定是将资源从一个对象"移动"到另一个对象,而不是"拷贝"它们。右值引用在实现移动语义时扮演了关键角色。

传统拷贝vs移动

1.传统拷贝:

创建一个对象的完整副本,可能会涉及动态资源的重新分配。

成本较高,特别是对动态内存的管理类(如vector)。

例如:

cpp 复制代码
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = v1; // 复制 v1 的内容,可能导致多次内存分配
2.移动:

通过右值引用,将一个对象的资源直接转移到另一个对象。

避免了不必要的内存分配和数据复制,提高性能。

示例:

cpp 复制代码
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1 的资源转移到 v2,v1 被置为"空"

如何使用右值引用实现移动语义

为了实现移动语义,需要提供移动构造函数和移动赋值运算符。

1.移动构造函数

移动构造函数接受一个右值引用作为参数,将资源从右值对象转移到当前对象。

示例:

cpp 复制代码
class MyClass {
private:
    int* data;

public:
    // 构造函数
    MyClass(int value) : data(new int(value)) {}

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr; // 将原对象的资源置为空
    }

    // 析构函数
    ~MyClass() {
        delete data; // 释放资源
    }
};
2.移动赋值运算符

移动赋值运算符转移资源时需要释放当前对象已有的资源(避免内存泄漏),然后接管右值对象的资源。

示例:

cpp 复制代码
class MyClass {
private:
    int* data;

public:
    // 构造函数
    MyClass(int value) : data(new int(value)) {}

    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {      // 避免自我赋值
            delete data;           // 释放当前对象的资源
            data = other.data;     // 转移资源
            other.data = nullptr;  // 将原对象的资源置为空
        }
        return *this;
    }

    // 析构函数
    ~MyClass() {
        delete data;
    }
};

完整示例:

cpp 复制代码
#include <iostream>
#include <utility> // for std::move

class MyClass {
private:
    int* data;

public:
    // 构造函数
    MyClass(int value) : data(new int(value)) {
        std::cout << "Constructing: " << *data << std::endl;
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Moving: " << *data << std::endl;
    }

    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete data;
            data = other.data;
            other.data = nullptr;
            std::cout << "Assigning: " << *data << std::endl;
        }
        return *this;
    }

    // 析构函数
    ~MyClass() {
        if (data) {
            std::cout << "Destructing: " << *data << std::endl;
            delete data;
        } else {
            std::cout << "Destructing: nullptr" << std::endl;
        }
    }
};

int main() {
    MyClass obj1(10);              // 调用构造函数
    MyClass obj2 = std::move(obj1); // 调用移动构造函数
    MyClass obj3(20);
    obj3 = std::move(obj2);        // 调用移动赋值运算符
    return 0;
}

输出:

cpp 复制代码
Constructing: 10
Moving: 10
Constructing: 20
Assigning: 10
Destructing: nullptr
Destructing: nullptr
Destructing: 10

右值引用允许我们绑定到右值(临时对象),直接操作其资源。

移动语义通过右值引用实现,主要通过 移动构造函数 和 移动赋值运算符,实现高效的资源转移。

使用右值引用和 std::move 可以显著提升程序性能,特别是当类中包含动态资源(如内存、文件句柄)时。

相关推荐
阿史大杯茶1 小时前
CodeTON Round 9 (Div. 1 + Div. 2, Rated, Prizes! ABCDE题) 视频讲解
数据结构·c++·算法
_extraordinary_1 小时前
LeetCode刷题--位运算
leetcode·哈希算法·散列表
赔罪2 小时前
C 语言变量说明符
c语言·开发语言·c++·学习·算法·objective-c
A Man Of Mould2 小时前
【数据结构】—— 堆
数据结构·算法
LNTON羚通2 小时前
视频智能分析软件LiteAIServer摄像机实时接入分析平台噪声监测算法介绍
人工智能·算法·音视频
佑冰2 小时前
C++ 矩阵旋转
数据结构·c++·算法·c
qystca2 小时前
蓝桥杯不知道叫什么题目
数据结构·算法
St_Ludwig2 小时前
C语言小撰特殊篇-assert断言函数
c语言·c++·后端·算法
益达爱喝芬达2 小时前
力扣11.23
数据结构·算法·leetcode
飞滕人生TYF2 小时前
java 排序 详解
java·算法·排序算法·方法