【C++】学习笔记——list

news2024/11/15 1:42:33

文章目录

  • 八、list
    • 1. list的介绍
    • 2. list的使用
    • 3. list的模拟实现
    • 4. list模拟实现的代码整合
      • 1. list.h
      • 2. test.cpp
  • 未完待续


八、list

list链接

1. list的介绍

在这里插入图片描述
是的, list 就是带头双向循环链表。

2. list的使用

通过 stringvector 的学习,我们差不多已经很了解容器的使用了,接下来要非常快速的过一遍了。

#include<iostream>
#include<list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

在这里插入图片描述
简简单单。
在这里插入图片描述
链表嘛,有头插、头删、尾插、尾删。也非常好使用,跟 push_back() 和 pop_back() 一样的用法。看到了 inserterase ,我们知道,vectorinserterase 可能会发生迭代器失效的问题,那 list 会出现吗?答案是不会发生,毕竟是链表嘛。
list 的大部分接口我们都了解了,我们就来看看一些之前没见过的东西。

在这里插入图片描述
首先我们来看看 reverse 。注意!是 reverse ,不是 reserve ,前者是逆置的意思,后者是保留,扩容的意思。sort 是排序的意思。
在这里插入图片描述
在这里插入图片描述

非常的简洁明了啊。

#include<iostream>
#include<list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.reverse();

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.sort();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

在这里插入图片描述
sort 默认排的都是升序,若是想要排降序,需要用到 仿函数 ,这里先不过多介绍。listsort 效率比较低,当数据量大的时候,经量不要用 listsort 排序。

merge 是合并的意思,用的比较少。
在这里插入图片描述

unique 的作用是去重,去除重复元素,要求是必须先有序。
在这里插入图片描述

#include<iostream>
#include<list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(2);
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(5);
	lt.push_back(1);
	lt.push_back(5);
	lt.push_back(4);
	lt.push_back(4);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(1);
	lt.push_back(2);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
	
	// 先排序
	lt.sort();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
	
	// 再去重
	lt.unique();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

在这里插入图片描述
remove 相当于 find + erase ,给你一个值,找到了就删,找不到就不删。

splice 这个接口很怪,它的作用是 转移
在这里插入图片描述
它可以将某一个链表,或者某个链表的某个值,或者某个链表的某一块区间转移到 position 位置 。也可以自己转移自己。

#include<iostream>
#include<list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	// 将 lt 链表的 begin() 转移到 lt 链表的 end()
	lt.splice(lt.end(), lt, lt.begin());

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

在这里插入图片描述
确实将头部的 1 转移到 尾部的 5 后面了。转移的本质是改变节点的指向,并不是删除或创建节点

3. list的模拟实现

我们快速的搭一个 list 框架出来, 这个框架大致基于 STL库里的 list 。注意理解。

#pragma once

#include<assert.h>

namespace my
{
	// struct 成员默认全公开   节点结构体
	template<class T>
	struct ListNode
	{
		// 后继指针
		ListNode<T>* _next;
		// 前驱指针
		ListNode<T>* _prev;
		// 数据域
		T _data;
		
		// 构造函数
		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		// 默认构造
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
	private:
		// 指向头节点的指针
		Node* _head;
	};
}

写一个尾插操作:

// list类的 public域
void push_back(const T& x)
{
	// 实例化新节点
	Node* newnode = new Node(x);
	// 尾部节点
	Node* tail = _head->_prev;

	// 修改新节点的指向
	newnode->_next = _head;
	newnode->_prev = tail;

	// 修改指向新节点的指针
	tail->_next = newnode;
	_head->_prev = newnode;
}

有了尾插,我们就想去测试一下,但是测试需要访问,list 本身不支持 下标 + [] 访问,只能通过迭代器访问。我们之前说过 迭代器是一个行为像指针的东西,但不一定是指针 。那么 list 这里的迭代器可以使用原生指针吗,我们发现,链表本身是不连续的,我们 ++指针 无法访问到到下一个元素,解引用指针 也访问不到元素的数据。
因此,这里的迭代器并不能使用原生指针来代替。我们知道,可以通过重载一系列运算符来改变类的行为 ,就像日期类一样,++日期 可以得到下一天的日期,那么我们也可以将迭代器封装成一个类,然后通过重载运算符来改变其行为。

// my命名空间内
// 迭代器模板
template<class T>
struct ListIterator
{
	typedef ListNode<T> Node;

	// 指向节点的指针
	Node* _node;
	
	// 构造函数
	ListIterator(Node* node)
		:_node(node)
	{}
};

我们只写了迭代器类的构造函数,并没有写迭代器的拷贝构造函数。我们没写,编译器会自动生成,编译器生成的拷贝构造是浅拷贝,但恰巧我们要的就是浅拷贝(新的节点也指向某个节点)。析构函数需要写吗?也不需要,因为在这个迭代器类里,只有一个指针而已,节点的空间资源并不属于这个类,所以也不需要这个类来释放。

先来控制迭代器的行为:

// 前置++
Self& operator++()
{
	_node = _node->_next;

	return *this;
}

// 后置++
Self operator++(int)
{
	// 拷贝构造
	Self tmp(*this);
	_node = _node->_next;

	return tmp;
}

// 前置--
Self& operator--()
{
	_node = _node->_prev;

	return *this;
}

// 后置--
Self operator--(int)
{
	Self tmp(*this);
	_node = _node->_prev;

	return tmp;
}

接着是迭代器的解引用:迭代器解引用我们要得到什么?得到节点的值,所以要返回数据

// 可读可写
T& operator*()
{
	return _node->_data;
}

判断相等:

bool operator!=(const Self& it)
{
	return _node != it._node;
}

bool operator==(const Self& it)
{
	return _node == it._node;
}

现在我们要实现 beginend 。请问这两个函数在哪里实现?在迭代器类里面实现吗?不对,迭代器类根本不知道链表的组成,这俩函数只能在 list类 里面实现。

// list类里面
iterator begin()
{
	// 匿名对象
	return iterator(_head->_next);
}

iterator end()
{
	// 匿名对象 _head是最后一个节点的下一个节点
	return iterator(_head);
}

完事儿,迭代器的行为也写完了,我们把当前的完整代码给出来:

#pragma once

#include<assert.h>

namespace my
{
	// struct 成员默认全公开   节点结构体
	template<class T>
	struct ListNode
	{
		// 后继指针
		ListNode<T>* _next;
		// 前驱指针
		ListNode<T>* _prev;
		// 数据域
		T _data;

		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

	// 迭代器模板
	template<class T>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		// self : 自己
		typedef ListIterator<T> Self;

		// 指向节点的指针
		Node* _node;

		ListIterator(Node* node)
			:_node(node)
		{}

		// 没写拷贝构造,因为这是内置类型,默认生成的拷贝构造是浅拷贝,我们要的就是浅拷贝

		// 也不需要写析构函数,迭代器只有访问权限,资源不属于迭代器

		// 前置++
		Self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		// 后置++
		Self operator++(int)
		{
			// 拷贝构造
			Self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		// 前置--
		Self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

		// 后置--
		Self operator--(int)
		{
			// 拷贝构造
			Self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		// 可读可写,引用返回
		T& operator*()
		{
			return _node->_data;
		}

		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
	};

	// 链表类
	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T> iterator;

		iterator begin()
		{
			// 匿名对象
			return iterator(_head->_next);
		}

		iterator end()
		{
			// 匿名对象
			return iterator(_head);
		}

		// 默认构造
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		void push_back(const T& x)
		{
			// 实例化新节点
			Node* newnode = new Node(x);
			// 尾部节点
			Node* tail = _head->_prev;

			// 修改新节点的指向
			newnode->_next = _head;
			newnode->_prev = tail;

			// 修改指向新节点的指针
			tail->_next = newnode;
			_head->_prev = newnode;
		}
	private:
		// 指向头节点的指针
		Node* _head;
	};
	// 测试区
}

我们来测试一下:

// 测试区
void test01()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		std::cout << *it << " ";
		++it;
	}
	std::cout << std::endl;
}
// test.cpp
#include<iostream>
#include"list.h"

int main()
{
	my::test01();
	return 0;
}

在这里插入图片描述
成功运行,我们的最基础的 list 框架可算是搭建好了。
接下来是 insert 。在 pos 位置前插入 val
在这里插入图片描述

void insert(iterator pos, const T& val)
{
	// 目标节点
	Node* cur = pos._node;
	// 待插入的节点
	Node* newnode = new Node(val);
	// 目标节点的上一个节点
	Node* prev = cur->_prev;

	newnode->_prev = prev;
	newnode->_next = cur;

	prev->_next = newnode;
	cur->_prev = newnode;
}

还有 erase
在这里插入图片描述

iterator erase(iterator pos)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->_next;

	prev->_next = next;
	next->_prev = prev;

	delete cur;
	// 返回下一个节点的迭代器,避免迭代器失效
	return iterator(next);
}

有了 inserterase ,我们就可以把 push_back 给复用一下,然后我们再将其他插入删除给顺便完成了。

void push_back(const T& x)
{
	insert(end(), x);
}

void push_front(const T& x)
{
	insert(begin(), x);
}

void pop_back()
{
	// 必须是 -- ,不能是 - 1,因为迭代器不支持 - ,支持 --
	erase(--end());
}

void pop_front()
{
	erase(begin());
}

来测试一下:

// 测试区
void test02()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_front(3);
	lt.push_front(4);
	lt.push_back(5);

	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		std::cout << *it << " ";
		++it;
	}
	std::cout << std::endl;

	lt.pop_back();
	lt.pop_front();

	for (auto e : lt)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;
}
#include<iostream>
#include"list.h"

int main()
{
	my::test02();
	return 0;
}

在这里插入图片描述
同样符合预期。
size :遍历一遍即可。

size_t size() const
{
	Node* begin = _head;
	size_t num = 0;
	while (begin->_next != _head)
	{
		++num;
		begin = begin->_next;
	}
	return num;
}

list类 实现的差不多了,我们接着完善 迭代器类
我们新增一个自定义类型 A

struct A
{
	int _a1;
	int _a2;

	A(int a1 = 0, int a2 = 0)
		:_a1(a1)
		,_a2(a2)
	{}
};
//测试区
void test03()
{
	list<A> lt;
	A aa1(1, 1);
	// 有名对象
	lt.push_back(aa1);
	// 匿名对象
	lt.push_back(A(2, 2));
	// 多参数的隐式类型转换 {}
	lt.push_back({ 3,3 });

	for (auto e : lt)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;
}

我们会发现不能运行。
在这里插入图片描述
因为内置类型可以使用流插入,自定义类型没写重载流插入就用不了,所以该怎么遍历呢?由于这里的成员变量都是共有的,所以直接访问成员变量就可以了。

// 测试区
void test03()
{
	list<A> lt;
	A aa1(1, 1);
	// 有名对象
	lt.push_back(aa1);
	// 匿名对象
	lt.push_back(A(2, 2));
	// 多参数的隐式类型转换 {}
	lt.push_back({ 3,3 });

	list<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		std::cout << (*it)._a1 << (*it)._a2 << " ";
		++it;
	}
	std::cout << std::endl;
}
#include<iostream>
#include"list.h"

int main()
{
	my::test03();
	return 0;
}

在这里插入图片描述
就可以遍历了。但是,既然可以 (*it)._a1 那就要可以 it->_a1 。所以我们还需要给迭代器类再重载一个运算符。

T* operator->()
{
	return &_node->_data;
}

有人会说,欸?这里怎么返回的是data的地址啊,那咋能访问到 _a1 啊?

void test04()
{
	list<A> lt;
	A aa1(1, 1);
	// 有名对象
	lt.push_back(aa1);
	// 匿名对象
	lt.push_back(A(2, 2));
	// 多参数的隐式类型转换 {}
	lt.push_back({ 3,3 });

	list<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		std::cout << it->_a1 << it->_a2 << " ";
		++it;
	}
	std::cout << std::endl;
}

在这里插入图片描述

但确实能跑了。其实是这里编译器做了相关的优化。咱们写的 it->_a1 编译器自动识别成 it->->_a1 ,也就是 it.operator->()->_a1 ,就是 &_data->_a1 。这下终于理解了,原理编译器帮我们省略了一个 ->
我们来实现一个 const迭代器 。最简单粗暴的方式就是 再定义一个const迭代器类 然后将其 解引用重载函数->重载函数 前面加上 const 就能够完成任务,但是如果就这两处不同的话,那普通迭代器和const迭代器代码相似度也太高了,有点冗余,该怎么合并呢?可以用类模板。

// Ref就是引用返回,Ptr就是指针返回
template<class T, class Ref, class Ptr>
struct ListIterator
{
	//
};

// list类里
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;

这就相当于:我们写了一个类模板,编译器帮我们实例化生成了普通迭代器类和const迭代器类

list 到这里基本差不多了,我们最后收尾一下。
list 的清理:

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
}

~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

判空:

bool empty()
{
	return size() == 0;
}

拷贝构造:

list(const list<T>& lt)
{
	// 初始的头结点
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;

	for (auto& e : lt)
	{
		push_back(e);
	}
}

复制重载:

void swap(list<T> lt)
{
	std::swap(_head, lt._head);
}

list<T>& operator=(list<T> lt)
{
	swap(lt);
	return *this;
}

现在大部分接口都已经手到擒来了。

4. list模拟实现的代码整合

1. list.h

#pragma once

#include<assert.h>

namespace my
{
	// struct 成员默认全公开   节点结构体
	template<class T>
	struct ListNode
	{
		// 后继指针
		ListNode<T>* _next;
		// 前驱指针
		ListNode<T>* _prev;
		// 数据域
		T _data;

		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

	// 迭代器模板
	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		// self : 自己
		typedef ListIterator<T, Ref, Ptr> Self;

		// 指向节点的指针
		Node* _node;

		ListIterator(Node* node)
			:_node(node)
		{}

		// 没写拷贝构造,因为这是内置类型,默认生成的拷贝构造是浅拷贝,我们要的就是浅拷贝

		// 也不需要写析构函数,迭代器只有访问权限,资源不属于迭代器

		// 前置++
		Self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		// 后置++
		Self operator++(int)
		{
			// 拷贝构造
			Self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		// 前置--
		Self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

		// 后置--
		Self operator--(int)
		{
			// 拷贝构造
			Self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		// 可读可写
		//T& operator*()
		Ref operator*()
		{
			return _node->_data;
		}

		//T* operator->()
		Ptr operator->()
		{
			return &_node->_data;
		}

		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
	};

	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		iterator begin()
		{
			// 匿名对象
			return iterator(_head->_next);
		}

		iterator end()
		{
			// 匿名对象
			return iterator(_head);
		}

		// 迭代器指向的内容不能修改,const iterator不是我们要的const迭代器

		const_iterator begin() const
		{
			return iterator(_head->_next);
		}

		const_iterator end() const
		{
			return iterator(_head);
		}

		// 默认构造
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list(const list<T>& lt)
		{
			// 初始的头结点
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;

			for (auto& e : lt)
			{
				push_back(e);
			}
		}

		void swap(list<T> lt)
		{
			std::swap(_head, lt._head);
		}

		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		bool empty()
		{
			return size() == 0;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		//void push_back(const T& x)
		//{
		//	// 实例化新节点
		//	Node* newnode = new Node(x);
		//	// 尾部节点
		//	Node* tail = _head->_prev;

		//	// 修改新节点的指向
		//	newnode->_next = _head;
		//	newnode->_prev = tail;

		//	// 修改指向新节点的指针
		//	tail->_next = newnode;
		//	_head->_prev = newnode;
		//}

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			// 必须是 -- ,不能是 - 1,因为迭代器不支持 - ,支持 --
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		iterator insert(iterator pos, const T& val)
		{
			// 目标节点
			Node* cur = pos._node;
			// 待插入的节点
			Node* newnode = new Node(val);
			// 目标节点的上一个节点
			Node* prev = cur->_prev;

			newnode->_prev = prev;
			newnode->_next = cur;

			prev->_next = newnode;
			cur->_prev = newnode;

			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev; 
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;

			delete cur;
			// 返回下一个节点的迭代器,避免迭代器失效
			return iterator(next);
		}

		size_t size() const
		{
			Node* begin = _head;
			size_t num = 0;
			while (begin->_next != _head)
			{
				++num;
				begin = begin->_next;
			}
			return num;
		}
	private:
		// 指向头节点的指针
		Node* _head;
	};

	struct A
	{
		int _a1;
		int _a2;

		A(int a1 = 0, int a2 = 0)
			:_a1(a1)
			,_a2(a2)
		{}
	};

	void test01()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			std::cout << *it << " ";
			++it;
		}
		std::cout << std::endl;
	}

	void test02()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_front(3);
		lt.push_front(4);
		lt.push_back(5);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			std::cout << *it << " ";
			++it;
		}
		std::cout << std::endl;

		lt.pop_back();
		lt.pop_front();

		for (auto e : lt)
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;

		std::cout << lt.size();
	}

	void test03()
	{
		list<A> lt;
		A aa1(1, 1);
		// 有名对象
		lt.push_back(aa1);
		// 匿名对象
		lt.push_back(A(2, 2));
		// 多参数的隐式类型转换 {}
		lt.push_back({ 3,3 });

		list<A>::iterator it = lt.begin();
		while (it != lt.end())
		{
			std::cout << (*it)._a1 << (*it)._a2 << " ";
			++it;
		}
		std::cout << std::endl;
	}

	void test04()
	{
		list<A> lt;
		A aa1(1, 1);
		// 有名对象
		lt.push_back(aa1);
		// 匿名对象
		lt.push_back(A(2, 2));
		// 多参数的隐式类型转换 {}
		lt.push_back({ 3,3 });

		list<A>::iterator it = lt.begin();
		while (it != lt.end())
		{
			std::cout << it->_a1 << it->_a2 << " ";
			++it;
		}
		std::cout << std::endl;
	}
}

2. test.cpp

#include<iostream>
#include"list.h"

int main()
{
	my::test03();
	return 0;
}

未完待续

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

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

相关文章

Mybatis逆向工程笔记小结

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1.前言 2.实现方案 2.1. mybatis-generator生成 2.1.1. 环境说明 2.1.2. 数…

Infuse for Mac激活版:多媒体播放器

Infuse Pro 是一款非常强大的视频播放器&#xff0c;它被誉为 Apple TV 上最强的播放器。它支持广泛的视频格式和解码器&#xff0c;包括 DTS、DTS-HD、AC3、E-AC3 等高清视频的音频播放任务。此外&#xff0c;Infuse Pro 还支持所有常见的高清格式。 Infuse Pro 的特点之一是其…

软件设计师-应用技术-数据库设计题2

基础知识及技巧&#xff1a; 1. 数据库设计过程&#xff1a; 四个阶段&#xff1a;需求分析、概念结构设计、逻辑结构设计、物理设计。每个阶段的产物&#xff1a; 需求分析&#xff1a;数据流图、数据字典、需求说明书。概念结构设计&#xff1a;ER模型逻辑机构设计&#xf…

低功耗UPF设计的经典案列分享

案例1 分享个例子&#xff0c;景芯A72低功耗设计&#xff0c;DBG domain的isolation为何用VDDS_maia_noncpu供电而不是TOP的VDD&#xff1f; 答&#xff1a;因为dbg的上一级是noncpu&#xff0c;noncpu下面分成dbg和两个tbnk。 案例2 景芯A72的低功耗&#xff0c;请问&#…

clang:在 Win10 上编译 MIDI 音乐程序(一)

先从 Microsoft C Build Tools - Visual Studio 下载 1.73GB 安装 "Microsoft C Build Tools“ 访问 Swift.org - Download Swift 找到 Windows 10&#xff1a;x86_64 下载 swift-5.10-RELEASE-windows10.exe 大约490MB 建议安装在 D:\Swift\ &#xff0c;安装后大约占…

一文读懂Vue生命周期(Vue2)

一文读懂Vue生命周期&#xff08;Vue2&#xff09; 目录 一文读懂Vue生命周期&#xff08;Vue2&#xff09;1 前言2 Vue生命周期2.1 基本生命周期2.1.1 8个生命周期2.1.2 案例 2.2 组件生命周期2.2.1 父子生命周期2.2.2 案例 2.3 keep-alive生命周期2.3.1 案例 2.4 其他 3 总结…

【Linux 基础 IO】文件系统

文章目录 1.初步理解文件2. fopen ( )的详解 1.初步理解文件 &#x1f427;① 打开文件&#xff1a; 本质是进程打开文件&#xff1b; &#x1f427;②文件没有被打开的时候在哪里呢&#xff1f; ----- 在磁盘中&#xff1b; &#x1f427;③进程可以打开很多个文件吗&#xff…

鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据

基本概念 队列又称消息队列&#xff0c;是一种常用于任务间通信的数据结构。队列接收来自任务或中断的不固定长度消息&#xff0c;并根据不同的接口确定传递的消息是否存放在队列空间中。 任务能够从队列里面读取消息&#xff0c;当队列中的消息为空时&#xff0c;挂起读取任务…

408数据结构-树与森林 自学知识点整理

前置知识&#xff1a;树的基本概念与性质 树的存储结构 树既可以采用顺序存储结构&#xff0c;又可采用链式存储结构。但无论采取哪种方式&#xff0c;都要求能够唯一地反映树中各结点之间的逻辑关系。 1. 双亲表示法 这种存储结构采用一组连续空间来存储每个结点&#xff0…

模拟电路设计与分析——电流

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 原子电流的产生电流的单位电流的方向直流电交流电直流&交流对比开路&闭路 原子 世界万物都是由原子构成&#xff0c;那么…

Pytorch实现图片异常检测

图片异常检测 异常检测指的是在正常的图片中找到异常的数据&#xff0c;由于无法通过规则进行识别判断&#xff0c;这样的应用场景通常都是需要人工进行识别&#xff0c;比如残次品的识别&#xff0c;图片异常识别模型的目标是可以代替或者辅助人工进行识别异常图片。 AnoGAN…

P8800 [蓝桥杯 2022 国 B] 卡牌

P8800 [蓝桥杯 2022 国 B] 卡牌 分析 “最多” -- 二分 1.二分区间&#xff08;凑齐的卡牌套数&#xff09;&#xff1a; l&#xff1a;a[]min&#xff1b;r&#xff1a;(a[]b[])max 2.check(x)&#xff1a; &#xff08;1&#xff09;for循环内&#xff1a; 判断x - a[i…

ComfyUI 基础教程(十三):ComfyUI-Impact-Pack 面部修复

SD的WebUI 中的面部修复神器 ADetailer,无法在ComfyUI 中使用。那么如何在ComfyUI中进行面部处理呢?ComfyUI 中也有几个面部修复功能,比如ComfyUI Impact Pack(FaceDetailer),以及换脸插件Reactor和IPAdapter。 ComfyUI-Impact-Pack 是一个功能强大的插件,专为 ComfyUI …

矩池云jupyter运行opengait代码 未完成版

文章目录 前言——矩池云的使用技巧1.切换源 一、下载数据集二、下载模型三、环境配置1.查看python、torch、torchvision版本2.查看一些包版本是否过高3.下载包 四、开始训练1.设置环境变量2.遇到的问题&#xff08;1&#xff09;torch.cuda.is_available()返回false&#xff0…

《第一行代码》第二版学习笔记(8)——网络技术

文章目录 一、Http1、HttpURLConnection2、OKHttp 二、解析JSON格式数据1、使用JSONObject2、使用GSON解析JSON数据 一、Http 1、HttpURLConnection public void run() {HttpURLConnection connection null;BufferedReader reader null;try {URL url new URL("http://…

springboot版本升级,及解决springsecurity漏洞问题

背景&#xff1a; 项目中要解决 Spring Security RegexRequestMatcher 认证绕过漏洞&#xff08;CVE-2022-22978&#xff09; 漏洞问题&#xff0c;并且需要将项目的版本整体升级到boot版本2.1.7&#xff0c;升级改造过程非常的痛苦&#xff0c;一方面对整个框架的代码不是很熟…

嵌入式复习重点

嵌入式系统有多种表现形式&#xff0c;包括计算机MCU、SOC片上系统、SOPC片上系统、GPU和FPGA等。 MCU(微控制器): 是最基本也是最常见的嵌入式系统形式,是集成了CPU、ROM、RAM、IO口、定时器、中断控制器等组件的单一芯片。MCU广泛用于电器电子产品的控制。SoC(系统片上芯片):…

vivado UltraScale 比特流设置

下表所示 UltraScale ™ 器件的器件配置设置可搭配 set_property <Setting> <Value> [current_design] Vivado 工具 Tcl 命令一起使用。

地下管线管网三维参数化建模软件MagicPipe3D V3.5

经纬管网建模系统MagicPipe3D&#xff08;www.magic3d.net&#xff09;自主安全可控&#xff0c;本地离线参数化构建三维管网模型&#xff08;管道、接头、附属物等&#xff09;&#xff0c;输出标准3DTiles、Obj等格式&#xff0c;支持Cesium、Unreal、Unity等引擎可视化查询分…

09_电子设计教程基础篇(电阻)

文章目录 前言一、电阻原理二、电阻种类1.固定电阻1、材料工艺1、线绕电阻2、非线绕电阻1、实心电阻1、有机实心电阻2、无机实心电阻 2、薄膜电阻&#xff08;常用&#xff09;1、碳膜电阻2、合成碳膜电阻3、金属膜电阻4、金属氧化膜电阻5、玻璃釉膜电阻 3、厚膜电阻&#xff0…