【C++技能树】String类解析与模拟实现

news2024/11/22 13:25:26

在这里插入图片描述

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我bua!

终于放假啦,停更了一个月的博客也要重新拾起来了。近一个月都没怎么好好写代码,现在好多都看不懂了。在接下的时间里,会更新:算法题,ROS,C++,Linux相关内容。
在这里插入图片描述

String

  • 1.String常用接口
    • 1.1构造函数
    • 1.2 容量操作
      • 1.2.1 size()与length()
      • 1.2.2 capacity()
      • 1.2.3empty()
      • 1.2.4clear()
      • 1.2.5 reserve()与resize()
    • 1.2 类对象的访问及遍历操作
    • 1.3 string对象的修改操作
      • 1.3.1 push_back()
      • 1.3.2 append()与 operator+=
      • 1.3.3 c_str()
      • 1.3.4find()
      • 1.3.5 substr()
  • 2. String的模拟实现
    • 2.1 私有属性
    • 2.2构造函数 析构函数 拷贝构造函数
    • 2.3 c_str()与size()
    • 2.4 operator[]
    • 2.5 iterator
    • 2.6 reserve()
    • 2.7 push_back()
    • 2.8 append()
    • 2.9 operator +=
    • 2.10 insert()
    • 2.11 erase()
    • 2.12 find()
    • 2.13 substr()
    • 2.14 resize()
    • 2.15 clear()
    • 2.16 clear()
    • 2.17 swap()
    • 2.18 opeartor比较
    • 2.19 operator=
    • 2.20 operator<<
    • 2.21 operator>>

1.String常用接口

String字符串是标准命名空间std的下的一个类。头文件为。是一个表示字符序列的类。学习STL模板库可以参考这个网站,自行查看传入参数以及返回参数等相关内容

1.1构造函数

constructor(构造函数),用于初始化string类对象。有以下几种函数重载:

default (1)string();
copy (2)string (const string& str);
substring (3)string (const string& str, size_t pos, size_t len = npos);
from c-string (4)string (const char* s);
from sequence (5)string (const char* s, size_t n);
fill (6)string (size_t n, char c);

我们仅介绍常用的,也就是:默认构造(1),拷贝构造(2)从const char*中构造string类,FILL(6)

  1. 先来看看默认构造函数:
string s1;

​ 这样就直接创建了一个空的String类对象s1,之后可以再用s1调用string类的接口

  1. 之后是拷贝构造函数:

    cpp中类进行拷贝构造需要调用拷贝构造函数,传入参数都是const class&(具体为什么已经在之前介绍过了,忘记的可以看看这篇文章: 拷贝构造).

    string (const string& s);
    string s3(s2);
    

    这里用s2对s3进行初始化.

  2. 从const char*中构造string类:

    string (const char*);
    string s2("hello world");
    

    out:hello world

  3. string类中包含n个字符c:

    string (count,char);
    string s4(5,'c');
    

    out:ccccc

  4. 截取一定长度的字符:

    第三个和第五个其实大差不差,虽然相较于第三个,第五个参数少了一个.但可以理解为,第五个是从0位置开始截取,而第三个是从pos位置开始截取.也可以说第三个是第五个构造函数的升级.(顺带一提在STL中,POS表示位置:position,LEN表示长度:length).

    当然第五个只适用于char*,而第三个适用于string.有点绕,就是这两个没有什么必然的联系,理解起来可以按上面的来理解.

    第三个:

    string (const string& str, size_t pos, size_t len = npos);
    string s2("hello world");
    string s6(s2,6,-1);
    

    out:world

    第五个:

    string (const char* s,size_t n);
    string s5("hello world",5);
    

    out:world

    代码总览:

    void constructor()
    {
        string s1;
        string s2("hello world");
        string s3(s2);
        string s4(5,'c');
        string s5("hello world",5);
        string s6(s2,6,-1);
        cout<<"s1:"<<s1.c_str()<<endl;
        cout<<"s2:"<<s2.c_str()<<endl;//hello world
        cout<<"s3:"<<s3.c_str()<<endl;//hello world
        cout<<"s4:"<<s4.c_str()<<endl;//ccccc
        cout<<"s5:"<<s5.c_str()<<endl;//hello
        cout<<"s6:"<<s6.c_str()<<endl;//world
    }
    

    1.2 容量操作

接下来我们来看看,有关String类的相关的容量操作.这里先提几个点:

  1. size_t为无符号整数,在32位系统下为unsigned int.在64位系统下为unsigned long.
  2. npos为-1,在其与size_t比较时,进行整形提升,表示为size_t最大的位置.所以通常情况下npos表示一个不存在的位置
  3. 与C语言不同,C中的字符串char*以’\0’来判断结束,而string类以当前pos与size的关系(==)来判断该字符串是否结束

1.2.1 size()与length()

size_t size() const;
size_t length() const;

Return length of string 返回字符串长度

这两个函数完全相同,引入size只是为了和其他STL容器接口保持一致.所以我们看size即可.

string s1("hello");
cout<<s1.size()<<endl;
cout<<s1.length()<<endl;

out:5

这里回顾下之前提到的第三点:'\0’的问题

string s1("hello");
s1+='\0';
s1+="!!!!!!!!";
cout<<"s1.size():"<<s1.size()<<endl;
const char * ch="hello'\0'!!!!!!!!";
cout<<"const char *:"<<strlen(ch)<<endl;

out:s1.size():14

out:const char* len:6

可以明显看出区别,这在之后模拟实现string会有大问题.

1.2.2 capacity()

size_t capacity() const;

Return size of allocated storage

每一段字符串系统都会为他提前开辟好一段空间,而capacity则是返回这段空间的大小.

string s1("hello");
cout<<"s1.capacity():"<<s1.capacity()<<endl;

out:15

表示最开始时系统为s1这个字符串分配了15bit的空间

在不同的平台下,系统初始分配空间与每次扩容的大小是不同的.

1.2.3empty()

bool empty() const;

Returns whether the string is empty (i.e. whether its length is 0).

若string为空则返回true,否则返回false

string s1;
cout<<"s1.empty()"<<s1.empty()<<endl;
s1+="hello";
cout<<"s1.empty()"<<s1.empty()<<endl;

out:s1.empty():1
out:s1.empty():0

1.2.4clear()

void clear();

无返回值,清楚string中的所有字符,令其size=0,但不改变已经分配的capacity

string s1("hello");
cout<<"s1:"<<s1<<endl;
cout<<"s1.size:"<<s1.size()<<endl;
cout<<"s1.capacity:"<<s1.capacity()<<endl;
s1.clear();
cout<<"s1:"<<s1<<endl;
cout<<"s1.size:"<<s1.size()<<endl;
cout<<"s1.capacity:"<<s1.capacity()<<endl;

out: s1:hello
s1.capacity:15
s1:
s1.size:0
s1.capacity:15

1.2.5 reserve()与resize()

reserve():

void reserve (size_t n = 0);

改变capacity的大小,若n小于capacity则不改变,大于则不改变有效元素的个数仅扩充空间

string s1("hello");
cout<<"s1.capacity:"<<s1.capacity()<<endl;
s1.reserve(s1.capacity()-2);
cout<<"s1.capacity:"<<s1.capacity()<<endl;
s1.reserve(s1.capacity()+2);
cout<<"s1.capacity:"<<s1.capacity()<<endl;

out: s1.capacity:15
s1.capacity:15
s1.capacity:30

至于为什么最后一次不是17而是30,是这样解释的:

If n is greater than the current string capacity, the function causes the container to increase its capacity to n characters (or greater).

也就是会扩充到更大,至于大多少是由操作系统/编码平台来决定的.

resize():

void resize (size_t n);
void resize (size_t n, char c);

Resizes the string to a length of n characters.调整string长度为n的有效字符

有两个函数重载:当n<size时,则两个函数相同都为缩减size=n,当size>n时,第一个函数会用0来填充多出来的长度.而第二个函数会用c来填充.

string s1("hello");
cout<<"s1.size:"<<s1.size()<<endl;
s1.resize(4);
cout<<"s1:"<<s1<<endl;
s1.resize(3,'c');
cout<<"s1:"<<s1<<endl;
s1.resize(8);
cout<<"s1:"<<s1<<endl;
s1.resize(16,'c');
cout<<"s1:"<<s1<<endl;

out: s1.size:5
s1:hell
s1:hel
s1:hel
s1.length():8
s1:helcccccccc

填充后的0是不显示的,但是存在的.

reserve与resize的区别:

先来看看下面这个代码有什么问题:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1;
    s1.reserve(10);
    for(int i=0;i<10;i++)
    {
        s1[i]='a';
    }
}

这个问题之前提到过:reserve是改变capacity,而通过[]去访问string需要size在这个范围里,也就是有效字符数在这个范围里.

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1;
    s1.resize(10);
    for(int i=0;i<10;i++)
    {
        s1[i]='a';
    }
}

1.2 类对象的访问及遍历操作

支持[]访问、迭代器(begin,end)与反向迭代器(rebegin,rend)

迭代器在这里可以看成一个指针,重点在实现string类中才会涉及。所以我们简单演示一下.

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello");
    string::iterator begin=s1.begin();
    string::iterator end=s1.end();
    for(;begin!=end;begin++)
    {
        cout<<*begin;
    }

}

out:hello

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string s1("hello");
    string::reverse_iterator rbegin=s1.rbegin();
    string::reverse_iterator rend=s1.rend();
    for(;rbegin!=rend;rbegin++)
    {
        cout<<*rbegin;
    }

}

out:olleh

1.3 string对象的修改操作

push_back()尾插一个字符
append()尾插
operator+=尾插
c_str返回C格式的字符串
find+npospos开始往后找字符,返回位置
rfind从pos往前找字符,返回位置
substr从pos开始截取n个字符,然后返回

1.3.1 push_back()

void push_back (char c);

Appends character c to the end of the string, increasing its length by one.

将一个字符插入到队尾,并将其长度+1

string s1;
s1.push_back('a');
cout<<"s1:"<<s1;

**out: **s1:a

1.3.2 append()与 operator+=

append:

string (1)string& append (const string& str);
substring (2)string& append (const string& str, size_t subpos, size_t sublen);
c-string (3)string& append (const char* s);
buffer (4)string& append (const char* s, size_t n);
fill (5)string& append (size_t n, char c);

其函数重载较多,我们也不都演示了.之前有过看源码的经历,所以较为轻松的能看出来每一个在干嘛:

(1) string

Appends a copy of str.

(2) substring

Appends a copy of a substring of str. The substring is the portion of str that begins at the character position subpos and spans sublen characters (or until the end of str, if either str is too short or if sublen is string::npos).

(3) c-string

Appends a copy of the string formed by the null-terminated character sequence (C-string) pointed by s.

(4) buffer

Appends a copy of the first n characters in the array of characters pointed by s.

(5) fill

Appends n consecutive copies of character c.

  1. 将一个string对象追加到队尾
  2. 将一个string对象从pos位置开始截取len个长度追加到队尾
  3. 将C格式的字符追加到队尾
  4. 将C格式的字符从0开始截取n个字符追加到队尾
  5. 将string队尾追加n个c字符
string s1;
s1.append("hello");
string s2;
s2.append(s1,1,-1);
cout<<"s1:"<<s1<<endl;
cout<<"s2:"<<s2<<endl;
string s3;
s3.append("const char *");
cout<<"s3:"<<s3<<endl;
string s4;
s4.append("const char *",5);
cout<<"s4:"<<s4<<endl;

out: s1:hello
s2:ello
s3:const char *
s4:const

operator+=:

是push_back与append的结合,既能追加单个字符也可以是一个string类型或是const char*

string s5;
s5+="h";
s5+="ello";
string s6(" world");
s5+=s6;
cout<<"s5:"<<s5<<endl;

out:s5:hello world

1.3.3 c_str()

const char* c_str() const;

将string对象转换成const char*并返回

    string s1("hello");
    cout<<s1.c_str();

out:hello

这个函数的意义是:C++需要兼容C语言,很多接口只能读取C的char*类型,例如ROS中的消息传递,所以需要这么个函数.函数不复杂,但意义重大

1.3.4find()

string (1)size_t find (const string& str, size_t pos = 0) const noexcept;
c-string (2)size_t find (const char* s, size_t pos = 0) const;
buffer (3)size_t find (const char* s, size_t pos, size_type n) const;
character (4)size_t find (char c, size_t pos = 0) const noexcept;

也是拥有多个重载:

  1. 在string中寻找与之匹配的string,从pos开始寻找
  2. string变为了const char* 其余与(1)相同
  3. 在(2)的基础上截取const char*的前n个作为查找对象
  4. 在string中寻找char,从pos位置开始寻找

若找到则返回对应位置,未找到则返回npos

string s1("hello hello world");
string s2("hello");
size_t pos=s1.find(s2);
cout<<"first s2 at:"<<pos<<endl;
pos=s1.find(s2,pos+1);
cout<<"second s2 at:"<<pos<<endl;   
pos=s1.find("hello");
cout<<"first hello at:"<<pos<<endl;
pos=s1.find("hello",pos+1);
cout<<"second hello at:"<<pos<<endl;   
pos=s1.find("hello",0,1);
cout<<"first h at:"<<pos<<endl;

out: first s2 at:0
second s2 at:6
first hello at:0
second hello at:6
first h at:0

rfind是从pos位置往前找:

string (1)size_t rfind (const string& str, size_t pos = npos) const noexcept;
c-string (2)size_t rfind (const char* s, size_t pos = npos) const;
buffer (3)size_t rfind (const char* s, size_t pos, size_t n) const;
character (4)size_t rfind (char c, size_t pos = npos) const noexcept;

除了默认参数不同,其他都是相同的.这里就不演示了

1.3.5 substr()

string substr (size_t pos = 0, size_t len = npos) const;

Returns a newly constructed string object with its value initialized to a copy of a substring of this object.

返回一个对原字符串截取完的拷贝字符串.(所以是不会改变原字符串)

从pos位置开始截取len个长度

string s1("hello world");
string s2(s1.substr(6,-1));
cout<<"s2:"<<s2<<endl;

out: world

2. String的模拟实现

为了避免与库函数的string产生命名冲突,我们需要新建一个命名空间,在新的命名空间中模拟String:

namespace H{
    class mystring{
        
    }
}

2.1 私有属性

private:
			char* _str;
			size_t _size;
			size_t _capacity;
			const static size_t npos;

		const size_t mystring::npos = -1;

其有四个私有属性:

  1. _str表示字符串的内容
  2. _size表示当前字符串有效长度
  3. _capacity表示当前字符串所占空间大小
  4. npos表示size_t的最大值

2.2构造函数 析构函数 拷贝构造函数

构造函数:

mystring(const char* str="")
				:_str(new char[strlen(str) + 1])
				, _size(strlen(str))
				, _capacity(_size)
			{
				strcpy(_str, str);
			}

注意:使用初始化列表需要与私有属性声明顺序要保持一致

拷贝构造函数:

mystring(const mystring& str)
			{
				_str = new char[str._capacity + 1];
				//strcpy(_str, str._str);
				memcpy(_str, str._str, str._size + 1);
				_capacity = str._capacity;
				_size = str._size;
			}

这里不用strcpy,因为新加入的字符可能是’\0’,用strcpy会导致在’\0处’就停止.

析构函数:

~mystring() {
				delete[]_str;
				_size = _capacity = 0;
			}

2.3 c_str()与size()

const char* c_str()const
{
    return _str;
}
size_t size()const
{
    return _size; 
}

2.4 operator[]

char& operator[](size_t pos)
{
    assert(_size > pos);
    return _str[pos];
}
const char& operator[](size_t pos)const
{
    assert(_size > pos);
    return _str[pos];
}

2.5 iterator

在这里 迭代器iterator可以直接看成一个指针,所以使用之前我们要进行一个typedef:

typedef char* iterator;
typedef const char* const_iterator;

之后就是正常的指针用法

iterator begin()
{
    return _str;
}
iterator end()
{
    return _str + _size;
}
const_iterator begin() const
{
    return _str;
}
const_iterator end()const
{
    return _str + _size;
}

写完迭代器后范围for就可以使用啦~

2.6 reserve()

void reserve(size_t n)
{
    if (n > _capacity)
    {
        char* tmp = new char[n + 1];
        memcpy(tmp, _str, _size + 1);
        delete[]_str;
        _str = tmp;
        _capacity = n;

    }
}

这里也不能用strcpy.

2.7 push_back()

void push_back(char ch)
{
    if (_size == _capacity)
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _str[_size] = ch;
    _str[++_size] = '\0';
}

2.8 append()

void append(const char* str)
{
    size_t len = strlen(str);
    if (_size + len >= _capacity)
    {
        reserve(_size+len);
    }
    strcpy(_str + _size, str);
    _size += len;  
}

先判断下是否会超过当前的capacity

2.9 operator +=

mystring& operator+=(const char str)
{
    push_back(str);
    return *this;
}
mystring& operator+=(const char* str)
{
    append(str);
    return *this;
}

2.10 insert()

void insert(size_t pos, size_t n, char ch)
{
    assert(pos <= _size);
    if (_size + n > _capacity)
    {
        reserve(_size + n);
    }
    int  end = _size;
    while (end >= pos&&end!=npos)
    {
        _str[end + n] = _str[end];
        end--;
    }
    for (size_t i = 0; i < n; i++)
    {
        _str[pos + i] = ch;
    }
    _size += n;
}
void insert(size_t pos, const char * ch)
{
    size_t len = strlen(ch);
    assert(pos<=_size);
    if (_size+len > _capacity)
    {
        reserve(_size +len);
    }
    int  end = _size;
    while (end >= pos && end != npos)
    {
        _str[end + len] = _str[end];
        end--;
    }
    for (size_t i = 0; i < len; i++)
    {
        _str[pos + i] = ch[i];
    }
    _size += len;
}

在这里插入图片描述

2.11 erase()

void erase(size_t pos, size_t len = npos)
{
    assert(pos <= _size);
    if (len == npos || pos + len >= _size)
    {
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        size_t end = pos + len;
        while (end <= _size)
        {
            _str[pos++] = _str[end];
            end++;
        }
        _size -= len;
    }
}

在这里插入图片描述

2.12 find()


    size_t find(char ch,size_t pos=0)
    {
        for (size_t i = pos; i < _size; i++)
        {
            if (_str[i] == ch)
            {
                return i;
            }
        }
        return npos;
    }
    size_t find(const char* str, size_t pos = 0)
    {
        assert(pos < _size);
        const char* ptr = strstr(_str+pos, str);
        if (ptr)
        {
            return ptr - _str;
        }
        else
            return npos;
    }

2.13 substr()

mystring substr(size_t pos = 0, size_t len = npos)
{
    size_t n = len;
    if (len == npos || pos + len > _size)
    {
        n = _size - pos;
    }
    mystring tmp;
    tmp.reserve(n);
    for (size_t i = pos; i < n+pos; i++)
    {
        tmp += _str[i];
    }
    return tmp;
}

2.14 resize()

void resize(size_t n,char ch='0')
{
    if (n < _size)
    {
        _size = n;
        _str[_size] = '\0';
    }
    else
    {
        reserve(n);
        for (size_t i = _size; i < n; i++)
        {
            _str[i] = ch;
        }
        _size = n;
        _str[_size] = '\0';
    }
}

2.15 clear()

void resize(size_t n,char ch='0')
{
    if (n < _size)
    {
        _size = n;
        _str[_size] = '\0';
    }
    else
    {
        reserve(n);
        for (size_t i = _size; i < n; i++)
        {
            _str[i] = ch;
        }
        _size = n;
        _str[_size] = '\0';
    }
}

2.16 clear()

void clear()
{
    _str[0] = '\0';
    _size = 0;
}

2.17 swap()

void swap(mystring& s)
{
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capacity, s._capacity);

}

2.18 opeartor比较

提供两种写法 :

bool operator<(const mystring s1)const
{
    size_t _i = 0;
    size_t i = 0;
    while (_i < _size && i < s1._size)
    {
        if (_str[_i] < s1._str[i])
        {
            return true;
        }
        else if (_str[_i] > s1._str[i])
        {
            return false;
        }
        else
        {
            _i++, i++;
        }
    }
    if (_i == _size && i != s1._size)
        return true;
    return false;
}
bool operator<(const mystring s1)const
{
    int ret = memcmp(_str, s1._str, _size < s1._size ? _size : s1._size);
	return ret == 0 ? _size < s1._size : ret < 0;
}

之后所有的都可以由这个来改进

bool operator==(const mystring s)const
{
    return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool operator<=(const mystring s)const
{
    return (*this < s) || (*this == s);
}
bool operator>=(const mystring s)const
{
    return !(*this < s);
}
bool operator>(const mystring s)const
{
    return !(*this <= s);
}
bool operator!=(const mystring s)const
{
    return !(*this == s);
}

2.19 operator=

mystring& operator=( mystring tmp)
{
    swap(tmp);
    return *this;
}

这里很妙,先是调用拷贝构造,构造一个tmp对象.之后直接交换即可

2.20 operator<<

ostream& operator<<(ostream& out,const H::mystring& s)
{
	out << s.c_str();
	return out;
}

2.21 operator>>

istream& operator>>(istream& in, H::mystring& s)
{
	s.clear();
	char ch = in.get();
	//处理buff之前的缓冲区
	while (ch == ' ' || ch == '\n')
	{
		ch = in.get();
	}
	char buff[128];
	int b = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[b++] = ch;
		if (b == 127) 
		{
			buff[b] = '\0';
			s += buff;
			b = 0;
		}
		ch = in.get();
	}
	if (b != 0)
	{
		buff[b] = '\0';
		s += buff;
	}
	
	return in;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/753013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

京东内部 Spring Boot 全解笔记,精髓!

在使用传统的 Spring 去做 Java EE&#xff08;Java Enterprise Edition&#xff09;开发中&#xff0c;大量的 XML 文件存在于项目之中&#xff0c;导致 JavaEE 项目变得慢慢笨重起来&#xff0c;&#xff0c;繁琐的配置和整合第三方框架的配置&#xff0c;导致了开发和部署效…

Deepin/UOS 装机 手动安装 分区 注意事项

以下3个分区必须 efi 分区 boot 分区 / (根) 分区 我们下期见&#xff0c;拜拜&#xff01;

kaggle学习笔记-情感和地理空间分析

背景 秘鲁食品评论中的情绪和地理空间分析 自然语言处理 (NLP) 是人工智能的一个分支&#xff0c;致力于让计算机能够像人类一样理解文本和口语单词。 另一方面&#xff0c;地理空间分析是对图像、GPS、卫星摄影和历史数据的收集、显示和操作&#xff0c;这些数据以地理坐标明…

TensorFlow模块简介

TensorFLow框架内构建了很多高层次的API&#xff0c;可以显著减少编写程序的代码量&#xff0c;其中包含众多网络结构相关函数和数据载入、数据处理的方法。 tf.data.Dataset tf.data.Dataset是TensorFlow内置的数据输入模块&#xff0c;提供了专门用于数据输入的多种方法&am…

Android Vector(矢量图)介绍

Android Vector(矢量图)介绍 1、vector的宽高属性 <vector xmlns:android"http://schemas.android.com/apk/res/android"android:width"24dp"android:height"24dp"android:viewportWidth"24"android:viewportHeight"24"…

控制台警告:alue of key ‘route.角色管理‘ is not a string!

控制台警告&#xff1a;Value of key route.角色管理 is not a string! 具体情况如下图所示&#xff1a; 解决办法&#xff1a; 先找到要修改的配置文件&#xff0c;先在src文件下&#xff0c;locale文件中找index.js。 如果src下没有locale文件可以找lang文件下的index.js文…

DAY46:动态规划(八)01背包应用2:一和零(二维容量01背包)

文章目录 474.一和零思路为什么不是多重背包而是01背包与之前01背包问题的区别 DP数组的含义递推公式&#xff08;也是求最大值&#xff09;初始化遍历顺序完整版总结 474.一和零 本题属于 装满背包最多能有多少个物品 类型本题是容量有两个维度的背包&#xff0c;背包容量是m…

【数据结构】树与二叉树(上)

目录 前言&#xff1a; 一、树&#xff1a; 1.树的概念&#xff1a; 2.树的相关概念&#xff1a; 3.树的表示&#xff1a; 4.书的实际使用场景&#xff1a; 二、二叉树&#xff1a; 1.二叉树的概念&#xff1a; 2.两种特殊二叉树&#xff1a; ①.满二叉树&#xff1a;…

力扣每日一题2023.7.13

题目: 示例: 分析: 给我们一个矩阵,我们需要找出一条路径从矩阵第一层(索引为0)到达矩阵最后一层,并且使得路径上的数值之和最小. 如果是老手,那么应该一眼就能看出来可以使用动态规划,如果看不出来,那我们接下来一起分析分析. 首先我们先不要搞这么复杂,以示例1为例,我们就…

Redis数据结构 — QuickList

目录 quicklist 结构设计 总结 quicklist 结构设计 quicklistNode 结构体里包含了前一个节点和下一个节点指针&#xff0c;这样每个 quicklistNode 形成了一个双向链表。但是链表节点的元素不再是单纯保存元素值&#xff0c;而是保存了一个压缩列表&#xff0c;所以 quickli…

JSP基础

javaWeb项目目录 src&#xff1a;存放Java源文件 src/main/java/com&#xff1a;存放相应的Java代码&#xff0c;并根据对应的Java项目继续分层架构&#xff0c;主要有以下几个软件包。 bean包&#xff1a;封装类&#xff0c;一般是按照数据库的来写内容的&#xff0c;一个表…

Sentry 监控部署与使用(详细流程)

一、简介 Sentry 是一个开源的实时错误监控的项目&#xff0c;它支持很多端的配置&#xff0c;包括 web 前端、服务器端、移动端、游戏端。 基于 Django 构建的现代化的实时事件日志监控、记录和聚合平台&#xff0c;主要用于如何快速的发现故障。更快地解决错误和性能问题&…

vue——自定义指令,实现拖拽改变元素的宽度——技能提升

最近在看企业微信时&#xff0c;发现tapd的需求部分&#xff0c;分为左右两部分&#xff0c;左侧可以实现拖拽改变宽度。 感觉这样的实现效果比较好&#xff0c;再联想到之前写绩效结构时&#xff0c;也是同样的左右布局。 如果能实现同样的效果&#xff0c;则算是很好一种体…

【方法】公众号上传的视频不能横屏播放,如何解决?

目录 说明 解决步骤 步骤一&#xff1a;开启微信横屏模式 步骤二&#xff1a;打开手机自动旋转功能 说明 直接用手机打开公众号文章上内嵌的视频&#xff0c;发现只能横屏播放&#xff0c;无法全屏查看。 这个时候学习&#xff0c;尤其是看视频课程的时候无法看清楚全图。…

华为发布大模型时代AI存储新品

7月14日&#xff0c;华为发布大模型时代AI存储新品&#xff0c;为基础模型训练、行业模型训练&#xff0c;细分场景模型训练推理提供存储最优解&#xff0c;释放AI新动能。 企业在开发及实施大模型应用过程中&#xff0c;面临四大挑战&#xff1a; 首先&#xff0c;数据准备时…

数据结构:第五章 树

文章目录 一、树的基本概念1.1树的定义1.2树的基本用语1.2.1结点之间的关系描述1.2.2结点、树的属性描述1.2.3有序树、无序树1.2.4森林1.2.5小结 1.3树的性质 二、二叉树的概念2.1二叉树的定义和基本术语2.2二叉树的性质2.2.1二叉树常考性质2.2.2完全二叉树常考性质 2.3二叉树的…

使用chatgpt过funcaptcha验证码3个人学习记录

funcaptcha 验证码3 通过记录 往期验证码&#xff1a;http://t.csdn.cn/ulgXY funcaptcha1 往期验证码&#xff1a;http://t.csdn.cn/3xMnZ funcaptcha2 funcaptcha 那个公司开发的简要介绍&#xff1a; Funcaptcha是由hCaptcha公司开发的一种人机验证系统。hCaptcha是一家位…

快速排序的非递归实现、归并排序的递归和非递归实现、基数排序、排序算法的时间复杂度

文章目录 快速排序的非递归三数取中法选取key快速排序三路划分 归并排序的递归归并排序的非递归计数排序稳定性排序算法的时间复杂度 快速排序的非递归 我们使用一个栈来模拟函数的递归过程&#xff0c;这里就是在利用栈分区间。把一个区间分为 [left,keyi-1][key][keyi1,right…

机器学习(13)--支持向量机

目录 一、支持向量机概述 二、Sklearn中的SVM概述 三、线性SVM损失函数 四、sklearn中进行可视化 1、导入模块 2、实例化数据集&#xff0c;可视化 3、网格点制作 4、建立模型并绘制决策边界和超平面 5、常用接口 6、如果数据是环形呢&#xff1f; 五、核函数 1、…

emacs下相对行号的设置

全局设置 全局开启行号显示&#xff1a;global-display-line-numbers-mode t 并设置 display-line-numbers-type的样式: relative 相对 配置代码如下: (use-package emacs:ensure t:config (setq display-line-numbers-type relative) (global-display-line-numbers-mode t)…