C++第五讲(2):STL--string--string的模拟实现+知识加餐

news2024/10/1 17:02:34

C++第五讲(2):STL--string--string的模拟实现+知识加餐

  • 1.string的模拟实现
    • 1.1string.h头文件 -- string类的声明
    • 1.2string.cpp源文件 -- string的具体实现
    • 1.3test.cpp源文件 -- string模拟实现的测试
  • 2.知识补充1:swap
  • 3.知识补充2:拷贝构造函数优化
  • 4.知识补充3:=运算符重载优化
  • 5.知识补充4:引用计数--了解
  • 6.知识补充5:为什么创建string模板
  • 7.string题目解答
    • 7.1仅仅反转字符
    • 7.2字符串中第一个唯一字符
    • 7.3字符串最后一个单词的长度
    • 7.4字符串相加

1.string的模拟实现

string的模拟实现,不再详细阐述,具体都在代码中,我们直接上原码

1.1string.h头文件 – string类的声明

#pragma once
#include <iostream>
using namespace std;
#include <string>
#include <assert.h>

//String的模拟实现
//为了与标准库中的string进行区分,我们可以定义一下属于自己的命名空间
namespace Mine
{
	//实行声明和定义分离
	class string
	{
	public:
		using iterator = char*;
		using const_iterator = const char*;

		构造函数
		//string();
		//析构函数
		~string();

		//传值构造函数
		string(const char* str = "");

		//string访问函数 -- []运算符重载
		char& operator[](size_t i);
		const char& operator[](size_t i) const;

		//c_str函数,因为代码较少,所以直接在声明位置写
		const char* c_str() const
		{
			return _str;
		}

		//预开辟空间函数
		void reserve(size_t n = 0);

		//尾插函数
		void push_back(char c);

		//追加函数
		void append(const char* s);

		//+=运算符重载
		//追加字符
		string& operator+=(char c);
		//追加字符串
		string& operator+=(const char* s);

		//插入函数
		void insert(size_t pos, char c);
		void insert(size_t pos, const char* s);

		//删除函数
		void erase(size_t pos, size_t len = npos);

		//查找函数
		//查找字符
		size_t find(char c, size_t pos = 0);
		//查找字符串
		size_t find(const char* s, size_t pos);

		//生成字串
		string substr(size_t pos = 0, size_t len = npos);
		//拷贝构造函数
		string(const string& str);

		//迭代器:实现了begin和end的功能之后,就可以使用迭代器了
		//using iterator = char*; //在最上面对迭代器返回值进行声明
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begint() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}

		//返回_size的函数
		size_t size() const
		{
			return _size;
		}

		//clear函数,不改变原来的空间大小
		void clear();


	private:
		//string中会存储空间指针、空间大小和实际使用的空间大小
		char* _str;
		int _size;
		int _capacity;

	public:
		对于静态const整数,编译器会做出一个特殊处理,使得它能够在声明时就初始化,但是尽量不要使用这种初始化,防止混乱
		//static const size_t npos = -1;
		static const size_t npos;
	};

	//运算符重载:运算符重载声明在类外面,称为全局函数,目的是为了匹配问题
	//因为重载方法左边必须是this变量,这个问题之前在流插入<<运算符中提过
	bool operator== (const string& s1, const string& s2);
	bool operator!= (const string& s1, const string& s2);
	bool operator>= (const string& s1, const string& s2);
	bool operator<= (const string& s1, const string& s2);
	bool operator> (const string& s1, const string& s2);
	bool operator< (const string& s1, const string& s2);

	//流插入和流提取运算符重载
	ostream& operator<< (ostream& os, string& str);
	istream& operator>> (istream& is, string& str);

	//getline函数
	istream& getline (istream& is, string& str, char delim = '\n');
}

1.2string.cpp源文件 – string的具体实现

#define _CRT_SECURE_NO_WARNINGS 1
#include "String.h"

//编译器会自动将命名空间名称相同的区域合并,所以我们可以在实现方法里也使用命名空间封装
namespace Mine
{
	//设置npos的值
	const size_t string::npos = -1;

	构造函数
	//string::string()
	//	//如果string初始话为空的话,打印string操作是会报错的,但是库里的string不会,就是初始化时给了一个\0
	//	:_str(new char[1]{'\0'})
	//	,_size(0)
	//	,_capacity(0)
	//{}
	//析构函数
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}

	//传值构造函数
	//string::string(const char* s)
	//	//这里三个strlen,如果构造一个string就使用三个strlen,消耗很大,尝试优化一下
	//	:_str(new char[strlen(s)+1])//因为\0,所以这里开辟的空间要+1
	//	,_size(strlen(s))
	//	,_capacity(strlen(s))
	//{}
	// 
	//string::string(const char* s)
	//	//这样写的话要注意:初始化列表初始化顺序是按照声明顺序来初始化的,但是一般不会改声明顺序,不太保险
	//	//因为会出现其它人因为不顺眼改声明顺序的情况
	//	:_size(strlen(s))
	//	,_str(new char[_size+1])
	//	,_capacity(_size)
	//{}
	//
	string::string(const char* str)
		//所以我们可以只初始化一个—_size
		:_size(strlen(str))
	{
		_str = new char[_size + 1];
		_capacity = _size;

		strcpy(_str, str);
	}

	//string访问函数
	char& string::operator[](size_t i)
	{
		//访问的必须是有效数据
		assert(i < _size);

		return _str[i];
	}
	const char& string::operator[](size_t i) const
	{
		//访问的必须是有效数据
		assert(i < _size);

		return _str[i];
	}

	//预开辟空间函数
	void string::reserve(size_t n)
	{
		//只有当需要开辟的空间大于原来的空间时,才需要开辟空间
		if (n > _capacity)
		{
			如果这样直接开辟空间的话,原本的空间得不到释放,会有问题
			//_str = new char[n + 1];
			//_capacity = n;
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;

			_capacity = n;
		}
	}

	//尾插函数
	void string::push_back(char c)
	{
		if (_size == _capacity)
		{
			//当空间不足时,需要开辟空间
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}

		//直接插入即可
		_str[_size] = c;
		_size++;
		_str[_size] = '\0';
	}

	//追加函数
	void string::append(const char* s)
	{
		//首先需要扩容,才能在后边追加
		size_t len = strlen(s);
		if (len + _size > _capacity)
		{
			size_t newcapacity = _capacity * 2;
			if (newcapacity < _size+len)
			{
				//如果二倍空间还不够,就需要多少开多少
				newcapacity = _size + len;
			}

			reserve(newcapacity);
			_capacity = newcapacity;
		}

		//此时应该要在str的后边进行追加
		strcpy(_str + _size, s);
		_size += len;
	}

	//+=运算符重载
	//追加一个字符
	string& string::operator+=(char c)
	{
		push_back(c);

		return *this;
	}
	//追加一个字符串
	string& string::operator+=(const char* s)
	{
		append(s);

		return *this;
	}

	//插入函数
	void string::insert(size_t pos, char c)
	{
		//插入的位置应该合法
		assert(pos <= _size);

		//插入方法和顺序表有相似之处,都要将数据向后移动
		//首先要检查空间是否充足
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);//reserve函数中存在着设置_capacity值的功能
		}
		//将pos位置之后的值都向后移动一位
		for (size_t i = _size + 1; i > pos; i--)
		{ 
			_str[i] = _str[i - 1];
		}
		_str[pos] = c;
		_size++;
	}
	void string::insert(size_t pos, const char* s)
	{
		//插入的位置应该合法
		assert(pos <= _size);

		//此时对于空间的检查应该加以改变
		size_t len = strlen(s);
		if (_size+len > _capacity)
		{
			size_t newcapacity = _capacity * 2;
			if (_size + len > newcapacity)
			{
				newcapacity = _size + len;
			}
			reserve(newcapacity);
		}
		//将pos位置之后的字符还要进行移动,但是这时需要考虑条件
		size_t end = _size + len;
		while (end > pos+len-1)//注意是pos+len-1
		{
			_str[end] = _str[end - len];
			--end;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = s[i];
		}
		_size += len;
	}

	//删除函数
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		//如果传入的参数特别大时,将pos之后的值都删了
		if (len >= _size - pos)
		{
			//直接修改\0的位置即可
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			//当传入的参数不大时,需要将数据向前移动
			size_t end = pos + len;
			while (end <= _size)
			{
				_str[end - len] = _str[end];
				++end;
			}

			_size -= len;
		}
	}

	//查找函数
	//查找字符
	size_t string::find(char c, size_t pos)
	{
		assert(pos < _size);

		//这里我们直接使用暴力查找方式
		//从pos位置开始查找
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}

		return npos;
	}
	//查找字符串
	size_t string::find(const char* s, size_t pos)
	{
		assert(pos < _size);

		//这里直接使用库里面的函数进行处理
		//strstr函数:成功返回指向第一个字符的指针,失败返回null
		const char* ptr = strstr(_str + pos, s);
		if (ptr == nullptr)
		{
			//如果没有找到,返回npos
			return npos;
		}
		else
		{
			//找到了,返回下标
			return ptr - _str;
		}
	}

	//生成字串
	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);

		//如果传入的len太大,那么直接取完字符串
		if (len > _size - pos)
		{
			len = _size - pos;
		}
		Mine::string ret;
		ret.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			//这里需要+=,因为+=才能够处理\0的情况
			ret += _str[i + pos];
		}

		//这里返回的是一个string类,传值返回会调用拷贝构造,如果不实现拷贝构造的话
		//自己提供的拷贝构造会进行浅拷贝,直接拷贝地址的行为是十分不正确的,所以如果
		//想要该函数正常运行,还需要自己实现一个拷贝构造函数
		return ret;
	}
	//拷贝构造函数
	string::string(const string& str)
	{
		//开辟空间 + 拷贝值
		_str = new char[str._capacity + 1];
		strcpy(_str, str._str);
		
		_size = str._size;
		_capacity = str._capacity;
	}

	//运算符重载
	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
	bool operator<=(const string& s1, const string& s2)
	{
		return (s1 < s2) || (s1 == s2);
	}
	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}
	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	//流插入和流提取运算符重载
	ostream& operator<< (ostream& os, string& str)
	{
		//os<< '"';//在这个函数中,我们可以自定义输出一些自己想要输出的东西
		//os<< "xx\"xx";
		
		//这里直接使用c_str()函数会出现一些问题,这个之后会说
		//所以这里我们需要逐个进行打印
		for (size_t i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		//os<< '"';

		return os;
	}
	//istream& operator>> (istream& is, string& str)
	//{
	//	//对于流提取,我们知道,如果遇到空格或者\n就会取消读取
	//	使用is进行输入会出现问题,因为cin遇到空格或\n就会取消读取,这就意味着ch一直无法读取空格和\n,那么之后检查
	//	ch是否是空格或\n就会出现一直查询的问题
	//	//char ch;
	//	//is >> ch;
	//	str.clear();
	//	char ch = is.get();//get函数:是流输入中的一个函数,该函数可以接受一切char字符,包括空格和\n
	//	while(ch != ' ' && ch != '\n')
	//	{
	//		//因为是+=,所以str中要清理一下内存,使用clear函数,防止输出时输出了原来的数据
	//		//而且+=就不用考虑开辟空间的问题了,+=实现时就有开辟空间的操作
	//		str += ch;
	//		ch = is.get();
	//	}

	//	return is;
	//}
	istream& operator>> (istream& is, string& str)
	{
		str.clear();
		//上面的流提取运算符重载,我们不够满意,因为+=需要一直开辟空间,效率很低,所以我们要进行优化:
		//创建一个数据buff,先将数据保存到buff中
		char buff[256];
		int i = 0;
		char ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 255)
			{
				//当buff数组满了时,才执行+=操作,然后让i=0,直到取完
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			//注意这里要存一个\0
			buff[i] = '\0';
			str += buff;
		}
		
		return is;
	}
	//clear函数,不改变原来的空间大小
	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

	//getline函数
	//与流提取运算符重载不同的时,该函数可以自己指定结束字符
	istream& getline(istream& is, string& str, char delim)
	{
		str.clear();
		//和上面的操作一样,只是while循环条件改变一下
		char buff[256];
		int i = 0;
		char ch = is.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}

		return is;
	}

}

1.3test.cpp源文件 – string模拟实现的测试

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
using namespace std;
#include <string>
#include "String.h"

//int main()
//{
//	string name;
//
//	//使用1:不指定结束字符
//	cout << "Please, enter your full name: ";
//	getline(std::cin, name);//hello world
//	cout << name << endl;;//hello world
//
//	//使用2:指定结束符
//	getline(cin, name, 'a');
//	cout << name << endl;
//	return 0;
//}

int main()
{
	//Mine::string s1("hello world");
	//cout << s1.c_str() << endl;

	//s1[0] = 'x';
	//cout << s1.c_str() << endl;

	//s1.reserve(20);
	//cout << s1.c_str() << endl;

	//s1.push_back('x');
	//s1.push_back('x');
	//s1.push_back('x');
	//s1.push_back('x');
	//cout << s1.c_str() << endl;

	//s1.append("hello xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1");
	//cout << s1.c_str() << endl;

	//s1 += 's';
	//cout << s1.c_str() << endl;
	//s1 += "hello";
	//cout << s1.c_str() << endl;

	//s1.insert(0, 'x');
	//cout << s1.c_str() << endl;
	//s1.insert(0, "hello");
	//cout << s1.c_str() << endl;

	//s1.erase(0, 7);
	//cout << s1.c_str() << endl;
	//s1.erase(27, 7);
	//cout << s1.c_str() << endl;

	查找一个字符
	//cout << s1.find('x', 0) << endl;
	查找字符串
	//Mine::string s2 = "https://legacy.cplusplus.com/reference/string/string/substr/";
	//size_t pos1 = s2.find(':');
	//size_t pos2 = s2.find('/', pos1 + 3);
	//if (pos1 != string::npos && pos2 != string::npos)
	//{
	//	Mine::string domain = s2.substr(pos1 + 3, pos2 - (pos1 + 3));//legacy.cplusplus.com
	//	cout << domain.c_str() << endl;

	//	Mine::string uri = s2.substr(pos2 + 1);//reference/string/string/substr/
	//	cout << uri.c_str() << endl;
	//}

	运算符重载
	//Mine::string s1("hello world");
	//Mine::string s2("hello wor");	
	//cout << (s1 == s2) << endl;//0
	//cout << (s1 != s2) << endl;//1
	//cout << (s1 > s2) << endl;//1
	//cout << (s1 < s2) << endl;//0

	Mine::string s1("hello world");
	cout << s1 << endl;

	cin >> s1;
	cout << s1 << endl;
	cin >> s1;
	cout << s1 << endl;

	Mine::getline(cin, s1, '!');
	cout << s1 << endl;

	return 0;
}

2.知识补充1:swap

在string类中,有着两个swap方法,而在C++标准库中,又有一个swap方法,实现这么多swap的用处是什么呢?

在这里插入图片描述
我们通过画图来直观地感受感受:

//这是下面画图时解析swap需要使用的代码:
Mine::string s1("hello world");
Mine::string s2("xxxxxxxxxxxxxxxxxxxxxx");
swap(s1, s2);

在这里插入图片描述
可以看出,C++库中的swap函数使用起来非常复杂,我们再看string类中swap的实现:

在这里插入图片描述
也就是说,实现方法为:

//交换函数
void string::swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

而最后一个swap为一个全局函数,它的声明和定义为:

//交换函数声明
void swap(string& s1, string& s2);

void swap(string& s1, string& s2)
{
	s1.swap(s2);
}

里面其实是对类swap的一个封装,这样实现的目的为:

int main()
{
	Mine::string s1("hello world");
	Mine::string s2("xxxxxxxxxxxxxxxxxxxxxx");

	s1.swap(s2);
	cout << s1 << endl;
	cout << s2 << endl;
	swap(s1, s2);//此时就会调用类里的swap函数,而不是标准库中的交换函数了,而且对于int或内置类型的交换,还会使用适合它的交换函数
	cout << s1 << endl;
	cout << s2 << endl;

	return 0;
}

3.知识补充2:拷贝构造函数优化

拷贝构造函数
传统写法:自己分配空间,自己赋值
//string::string(const string& str)
//{
//	//开辟空间 + 拷贝值
//	_str = new char[str._capacity + 1];
//	strcpy(_str, str._str);
//	
//	_size = str._size;
//	_capacity = str._capacity;
//}
//
//现代写法:
string::string(const string& str)
{
	string tmp(str.c_str());//通过字符串直接构造一个tmp
	swap(tmp);//然后将this与tmp进行交换,就实现了拷贝构造的功能
}

但是要注意的是:现代写法只是看起来变得简洁了,但是效率并没有提高

4.知识补充3:=运算符重载优化

传统写法
//string& string::operator=(const string& s)
//{
//	if (this != &s)//需要处理自己给自己赋值的情况
//	{
//		delete[] _str;
//		_str = new char[s._capacity + 1];
//		strcpy(_str, s._str);
//		_size = s._size;
//		_capacity = s._capacity;
//	}
//	return *this;
//}
//
优化写法
//string& string::operator=(string& s)
//{
//	if(this != &s)
//	{
//		//和上面一样,进行交换
//		string tmp(s.c_str());
//		swap(tmp);
//	}

//	return *this;
//}
//
//现代写法:
string& string::operator=(string s)//这里是拷贝构造接受string传入的值
{
	swap(s);//然后进行交换
	//这里不用考虑析构问题,因为交换了之后,s是拷贝构造,会自己析构

	return *this;
}

5.知识补充4:引用计数–了解

该方法在之前使用广泛,但是现在好像不怎么用了,所以作为了解
在这里插入图片描述

6.知识补充5:为什么创建string模板

计算机只能够识别0和1,那么计算机是如何将比特值转换成我们的文字符号的呢,其实是映射的关系,在计算机初期,美国计算机发展迅速,所以就针对于计算机编制了一套编码系统:ascii,英文字符较少,所以使用一个字节来存储:
在这里插入图片描述
后来随着计算机的推广,一个字节已经难以存储广大的字符了,比如汉字就有着10万余个,所以就推出了统一码(万国码)的概念,其中UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案,UTF后边跟的数字就是比特位,UTF-16就是两个字节存储,显然难以存储很多的汉字,而UTF-32能够存储亿单位的汉字,显然多了,所以我们一般使用UTF-8编码方案,UTF-8的好处也有:同时兼容了ascii,所以能够在编码英文的状态下同时编码中文,它的编码方式为:
在这里插入图片描述
在这里插入图片描述
而string其实是basic_string模板中char的实例化
在这里插入图片描述
如果使用其它编码格式的话,就需要实例化其它参数,所以说string有模板是很正确的~!

7.string题目解答

7.1仅仅反转字符

链接: link

class Solution {
public:
    bool Check(char ch)
    {
        if(ch >= 'a' && ch <= 'z')
            return true;
        if(ch >= 'A' && ch <= 'Z')
            return true;
        return false;
    }

    string reverseOnlyLetters(string s) {
        int left = 0;
        int right = s.size()-1;
        while(left < right)
        {
            while(left<right && !Check(s[left]))
            {
                left++;
            }
            while(left < right && !Check(s[right]))
            {
                right--;
            }
            if(left < right)
            {
                swap(s[left], s[right]);
                left++;
                right--;
            }
        }
        return s;
    }
};

7.2字符串中第一个唯一字符

链接: link

class Solution {
public:
    int firstUniqChar(string s) {
        //26个英文字母
        int arr[27] = {0};
        for(int i = 0; i<s.size(); i++)
        {
            int pos = s[i]-'a';
            arr[pos] += 1;
        }
        for(int i = 0; i<s.size(); i++)
        {
            int pos = s[i]-'a';
            if(arr[pos] == 1)
                return i;
        }
        return -1;
    }
};
class Solution {
public:
    int firstUniqChar(string s) {
        // 统计每个字符出现的次数
        int count[256] = { 0 };
        int size = s.size();
        for (int i = 0; i < size; ++i)
            count[s[i]] += 1;
        // 按照字符次序从前往后找只出现一次的字符
        for (int i = 0; i < size; ++i)
            if (1 == count[s[i]])
                return i;
        return -1;
    }
};

7.3字符串最后一个单词的长度

链接: link

int main() {
    string s;
    while (getline(cin, s)) {
        //最后一个单词前为空格,那么我们就可以寻找空格的位置
        size_t pos = s.rfind(' ');

        //假如s中没有空格,s=ABCDE,pos为size_t-1,而pos+1=0,所以返回的就是
        //s.size(),直接就将没有空格的情况进行了处理,十分方便!
        cout << s.size()-pos-1 << endl;
    }
}

7.4字符串相加

链接: link

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.size()-1;
        int end2 = num2.size()-1;
        int sum = 0;
        int next = 0;
        int valu1, valu2;
        string newnum;
        while(end1 >= 0 || end2 >= 0)
        {
            if(end1 >= 0)
                valu1 = num1[end1--]-'0';
            else
                valu1 = 0;
            
            if(end2 >= 0)
                valu2 = num2[end2--]-'0';
            else
                valu2 = 0;
            
            sum = valu1+valu2+next;
            if(sum >= 10)
            {    
                next = 1;
                sum -= 10;
            }
            else
            {
                next = 0;
            }
            newnum.insert(newnum.begin(), sum+'0');
        }
        if(next == 1)
        {
            newnum.insert(newnum.begin(), '1');
        }

        return newnum;
    }
};

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

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

相关文章

IDEA几大常用AI插件

文章目录 前言列表GPT中文版TalkXBito AIIDEA自带的AI 前言 最近AI、GPT特别火&#xff0c;IDEA里面又有一堆插件支持GPT&#xff0c;所以做个专题比较一下各个GPT插件 列表 先看idea的plugins里支持哪些&#xff0c;搜索“GPT”之后得到的&#xff0c;我用下来感觉第一第二和…

【Iceberg分析】调研Iceberg中表的原地演变

调研Iceberg中表的原地演变 文章目录 调研Iceberg中表的原地演变原生非分区表文件关系图表的原地演变之表schema演变新增字段new_column文件关系变化图为新增字段写入数据文件关系变化图删除新增字段文件关系变化图新增字段new_column2文件关系变化图删除数据文件关系变化图 原…

无人机之数据提取篇

一、无人机平台与传感器 无人机是进行数据采集的基础设施&#xff0c;其稳定性、可靠性、灵活性和负载能力直接影响到数据采集的效果。根据实际需求&#xff0c;需选择适合的无人机类型&#xff0c;如固定翼无人机适合大范围、长时间的数据采集&#xff0c;而多旋翼无人机则更适…

从Fast-UMI到Diff-Control:分别改进UMI的硬件、UMI所用的扩散策略

前言 24年9.28日下午&#xff0c;微信上的好友丁研究员和我说 hi 周总&#xff0c;我们基于umi改进了一下——弄了一个用户友好的采集系统(当然&#xff0c;现在这个版本还比较初级 后续不断迭代)项目网站为&#xff1a;https://fastumi.com/ 技术报告见&#xff1a;https://…

docker和kafka连接Topic失败处理措施

使用 docker 镜像部署一套单节点的 Zookeeper Kafka&#xff0c;但是一直Java却连不上一些处理思路。 为了提高部署效率&#xff0c;这里提供一个简单可启动的docker-compose。 version: "3.3" services:zookeeper:image: zookeeper:3.5.5restart: alwayscontainer…

Global Illumination_VXGI(VCT) Deep Optimizations

之前针对RSM和LPV优化技术介绍后&#xff0c;我们可以看出来一个大致的思路的&#xff1a;就是减少计算量提升最大&#xff0c;因此VXGI的优化思路和之前两种算法几乎一样&#xff0c;之前也实现过Global Illumination_Voxel Global Illumintaion (VXGI)有兴趣的可以去简单了解…

软件系统可视化建设方案书(word原件)

第 一 章 系统总体设计 1.1 总体架构 1.1.1 系统拓扑 1.1.2 系统组成 1.2 设计概述 1.3 平台系统功能 1.3.1 总部数据看板 1.3.2 项目部数据看板 1.3.3 视频联网系统 1.3.4 实名制考勤系统 1.3.5 安全生产系统 1.3.6 塔吊安全监控子系统 1.3.7 施工升降机安全监控管系统 1.3.8 …

videojs 播放mp4视频只有声音没有画面问题

在使用Video.js播放MP4视频时&#xff0c;如果遇到只有声音没有画面的情况&#xff0c;这通常与视频文件的编码格式、浏览器兼容性或Video.js的配置有关。以下是一些可能的解决步骤和原因分析&#xff1a; 1. 检查视频编码 MP4视频支持多种编码格式&#xff0c;但并非所有编码…

Python 如何使用 Pandas 进行数据分析

Python 如何使用 Pandas 进行数据分析 在数据分析领域&#xff0c;Python 是非常流行的编程语言&#xff0c;而 Pandas 是其中最重要的库之一。Pandas 提供了高效、灵活的数据结构和工具&#xff0c;专门用于处理和分析数据。对于数据分析新手来说&#xff0c;理解如何使用 Pa…

Java增强for循环遍历集合删除指定值不安全问题

在这里因为remove方法有两种参数&#xff0c;一种是对象&#xff08;删除此元素&#xff09;&#xff0c;一种是Integer &#xff08;删除此下标对应的元素&#xff09;。恰好我对象类型就是Integer&#xff0c;所以或默认为删除下标对应元素&#xff0c;造成下标越界不安全。可…

RSA算法模拟实验报告(后篇,非常感谢橘味小奶糖的反馈)

有朋友说代码运行不出来&#xff0c;因为我是平板上写的&#xff0c;没在电脑上运行过&#xff0c;这也算是我的疏忽吧&#xff0c;今天尝试了一下&#xff0c;刚开始运行出来是乱码&#xff0c;改了一些东西&#xff0c;还是运行出来了。 我用的devc。 首先是文字显示&#…

【数据结构】---图

图 前言 本篇作为图的基础概念篇&#xff0c; 了解图的离散数学定义&#xff0c; 图的分类&#xff0c; 图模型解决的问题&#xff08;图的应用&#xff09;&#xff0c; 图的相关算法&#xff08;仅仅介绍&#xff0c;具体不在此篇展开&#xff09;。 学习基本路线&#xff…

计算两点结构的斜率

在行列可自由变换的条件下&#xff0c;平面上的2点结构只有3个 (A,B)---6*n*2---(0,1)(1,0) 分类A和B&#xff0c;A是3个2点结构&#xff0c;让B全是0。当收敛误差为7e-4&#xff0c;收敛199次取迭代次数平均值。让隐藏层节点数n分别为10&#xff0c;15&#xff0c;20&#xf…

【漏洞复现】泛微OA E-Office do_excel.php 任意文件写入漏洞

》》》产品描述《《《 泛微0-0fice是一款标准化的协同 OA办公软件&#xff0c;泛微协同办公产品系列成员之一,实行通用化产品设计&#xff0c;充分贴合企业管理需求&#xff0c;本着简洁易用、高效智能的原则&#xff0c;为企业快速打造移动化、无纸化、数字化的办公平台。 》》…

C# 变量与常量

一 变量与常量 1.1 内存如何存放数据 计算机使用内存来记忆计算时所使用的数据 内存如何存储数据&#xff1f; 数据各式各样&#xff0c;要先根据数据的需求&#xff08;即类型&#xff09;为它申请一块合适的空间 内存像宾馆 1.2 为什么需要变量 内存地址不好记&#x…

索尼MDR-M1:超宽频的音频盛宴,打造沉浸式音乐体验

在音乐的世界里&#xff0c;每一次技术的突破都意味着全新的听觉体验。 索尼&#xff0c;作为音频技术的先锋&#xff0c;再次以其最新力作——MDR-M1封闭式监听耳机&#xff0c;引领了音乐界的新潮流。 这款耳机以其超宽频播放和卓越的隔音性能&#xff0c;为音乐爱好者和专…

【优选算法】(第十二篇)

目录 搜索旋转排序数组中的最⼩值&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 0〜n-1中缺失的数字&#xff08;easy&#xff09; 题目解析 讲解算法原理 编写代码 搜索旋转排序数组中的最⼩值&#xff08;medium&#xff09; 题目解析 1.题目链接…

【C++ STL】领略vector之美,熟练掌握vector的使用

vector容器详解 一.vector容器简单介绍二.vector的构造函数三.vector中与容量和大小相关操作3.1接口函数说明3.2使用时的性能优化 四.vector中的元素访问与修改五.vector迭代器与遍历5.1迭代器5.2迭代器失效问题5.2.1 扩容导致的迭代器失效问题5.2.2删除导致的迭代器失效问题 一…

MySQL安装与环境配置(Windows系统 MySQL8.0.39)

目录 MySQL8.0.39工具下载安装开启方式可视化开启命令方式开启 环境配置 MySQL8.0.39 工具 系统&#xff1a;Windows 11 参考视频&#xff1a; 黑马程序员 MySQL数据库入门到精通&#xff0c;从mysql安装到mysql高级、mysql优化全囊括 P3 https://www.bilibili.com/video/BV1…

如何在Python中计算移动平均值?

在这篇文章中&#xff0c;我们将看到如何在Python中计算移动平均值。移动平均是指总观测值集合中固定大小子集的一系列平均值。它也被称为滚动平均。 考虑n个观测值的集合&#xff0c;k是用于确定任何时间t的平均值的窗口的大小。然后&#xff0c;移动平均列表通过最初取当前窗…