【C++】list的使用和模拟实现

一、list的使用

1. 介绍

image-20220328224742710

双向链表+双向迭代器


2. 迭代器

因为是双向迭代器,试试正反遍历

  • 正向遍历
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <list>
using namespace std;

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

	list<int>::iterator it = lt.begin();

	while (it != lt.end())
	{
		cout << *it;
		it++;
	}
	cout << endl;

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

}

测试结果:

image-20220329161604897


  • 反向遍历
//反向遍历
void test2()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

	list<int>::reverse_iterator it = lt.rbegin();

	while (it != lt.rend())
	{
		cout << *it;
		it++;
	}
	cout << endl;


}

测试结果:

image-20220329161935370


3. remove查找移除

image-20220329163048186

找到对应数字并且删除


4. 删除重复

去重之前先排序,只会把连续重复的删除

image-20220329163130894


5. 排序

但是因为是链表效率不高

image-20220329163223650


6. 逆序

image-20220329163314746


7. 粘合

image-20220329163806135


二、模拟实现list

1. 准备框架

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

namespace wyj
{
    //提供每个节点的属性
    //类模板
	template<class T>
	struct ListNode
	{
        //双向循环链表
        //包含元素,前一个节点地址,后一个节点地址
		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _date;
	};
    
	template<class T>
	class list
	{
        //在我的这个类(list)里面换个称呼
		typedef ListNode<T> Node;

	public:

	private:
		Node* _head;
	};


}

2. 构造/拷贝构造/赋值重载

  • 节点的构造函数
template<class T>
struct ListNode
{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;
//链表的初始化,先创建一个节点,头和尾都是自己
		ListNode(const T& date = T())//T的匿名对象
			:_next(nullptr)
			, _prev(nullptr)
			, _data(data)
		{}
};

  • list构造函数
list()
{
		_head = new Node();
		_head->_next = _head;
		_head->_prev = _head;
}

  • 迭代器区间构造
// list<Date> lt1(5, Date(2022, 3, 15));
// list<int> lt2(5, 1);优先和Input匹配,因为类型相同

//list(size_t n, const T& val = T())
list(int n, const T& val = T())
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	for (size_t i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

template<class InputIterator>
list(InputIterator first, InputIterator last)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	while (first != last)
    {
		push_back(*first);
		++first;
	}
}

  • 拷贝构造
		// lt2(lt1)
		//list(const list<T>& lt)
		//{
		//	_head = new Node();
		//	_head->_next = _head;
		//	_head->_prev = _head;

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

		 lt2 = lt1
		//list<T>& operator=(const list<T>& lt)
		//{
		//	if (this != &lt)
		//	{
		//		clear();
		//		for (auto e : lt)
		//		{
		//			push_back(e);
		//		}
		//	}

		//	return *this;
		//}


// lt2(lt1)
list(const list<T>& lt)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
    //直接swap会把随机值给lt,在clear会出问题
	list<T> tmp(lt.begin(), lt.end());
	std::swap(_head, tmp._head);//这里需要深拷贝
}

// lt2 = lt1
list<T>& operator=(list<T> lt)
{
	std::swap(_head, lt._head);
	return *this;
}

3. push_back

image-20220329173522863

//插入
void push_back(const T& x)
{
	Node* newnode = new Node(x);
	Node* tail = _head->_prev;
	tail->_next = newnode;
	newnode->_prev = tail;
	_head->_prev = newnode;
	newnode->_next = _head;
}

测试前问题

image-20220329193128137

没有迭代器,后面会解释


4. 迭代器

迭代器需要这样的模板

list<int>::iterator it = lt.begin();

while(it != lt.end())
{
    cout << *it;
    it++;
}

这里因为是链表,没法实现++

但我们可以重载运算符

可以看的出来 iteration 是一个自定义类型

template<class T>
struct __list_iterator
{
	typedef ListNode<T> Node;
	typedef __list_iterator<T> iterator;

	//成员
	Node* _node;

	//构造函数
	__list_iterator(Node* x)
		:_node(x)
	{}

	//运算符重载
	//++it
	iterator& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	//it++
	iterator operator++(int)//占位参数
	{
		iterator tmp(*this);
		_node = _node->_next;
		return tmp;
	}

	T& operator*()
	{
		return _node->_data;
	}
    
    bool operator!=(const iterator& x)
	{
		return x._node != _node;
	}
};
  • list里迭代器
//begin
iterator begin()
{
	return iterator(_head->_next);
}
//不是->prev是因为,迭代器是右边是开区间,不能是最后一个数
//end
iterator end()
{
	return iterator(_head);
}

最终测试:

image-20220329211405086


这里不需要拷贝构造、赋值重载、析构函数

因为默认生成的就可以。


5. 迭代器优化

当遇到

void print(const list<int> v)
{
    //如何去用迭代器
}

需要迭代器访问只可读不可写


观察迭代器,看哪里需要修改,

list<int>::iterator it = lt.begin();

while(it != lt.end())
{
    cout << *it;
    it++;
}

image-20220329221836532

出现权限放大问题


注意区分:self是类模板(迭代器(类)),T、Ref是(元素的)类型

//两个不同的模板参数
template<class T, class Ref>
struct __list_iterator
{
	typedef ListNode<T> Node;
	typedef __list_iterator<T, Ref> self;
	//成员
	Node* _node;

	//构造函数
	__list_iterator(Node* x)
		:_node(x)
	{}

	//运算符重载
	//++it
	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	//it++
	self operator++(int)//占位参数
	{
		self tmp(*this);
		_node = _node->_next;
		return tmp;
	}

	//--it
	self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	//it--
	self operator--(int)//占位参数
	{
		self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}

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

	bool operator!=(const self& x)
	{
		return x._node != _node;
	}

	bool operator==(const self& x)
	{
		return !(*this != x);
	}
};

template<class T>
class list
{
	typedef ListNode<T> Node;
		
public:
	//外面使用的时候分普通和const的迭代器,所以需要区分两个类型,但T是必须有的类型,无论用或者不用const的迭代器
	typedef __list_iterator<T, T&> iterator;
	typedef __list_iterator<T, const T&> const_iterator;
	//begin
	iterator begin()
	{
		return iterator(_head->_next);
	}

	const_iterator begin()const
	{
		return const_iterator(_head->_next);
	}
		//不是->prev是因为,迭代器是右边是开区间,不能是最后一个数
		//end
	iterator end()
	{
		return iterator(_head);
	}

	const_iterator end()const
	{
		return const_iterator(_head);
	}
	
private:
	Node* _head;
};

测试:

image-20220330135513520


6. 流提取对于自定义类型不支持问题

struct Date
{
	Date(int year = 1, int month = 2, int day = 3)
		:_year(year),
		_month(month),
		_day(day)
	{}

	int _year;
	int _month;
	int _day;
};

void test1()
{
	list<Date> lt;
	lt.push_back(Date(1, 2, 3));
	lt.push_back(Date(1, 2, 3));
	lt.push_back(Date(1, 2, 3));
	lt.push_back(Date(1, 2, 3));

	list<Date>::iterator it = lt.begin();

	while (it != lt.end())
	{
		cout << *it;
		it++;
	}
}

image-20220330141604459


除了直接访问

image-20220330141821273

还可以通过

it->_day;
//但目前还不支持,我们自己写一个

//->前面是指针
T* operator->()
{
	return &_node->_data;
}
本来这里_node->_date,已经是_date,再取地址,返回的是date*
外面 it->_day; 按道理说应该是 it.operator->()->_day
    it->->_day;但编译器合二为一优化了

7. 插入相关函数

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

void push_back(const T& x)
{
	/*Node* tail = _head->_prev;
	Node* newnode = new Node(x);
	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = _head;
	_head->_prev = newnode;*/

    insert(end(), x);
}

void pop_back()
{
	erase(--end());
}

void pop_front()
{
	erase(begin());
}
// 这里insert以后,pos是否失效?不失效
iterator insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* newnode = new Node(x);

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

	return iterator(newnode);
}

// 这里erase以后,pos是否失效?一定失效
iterator erase(iterator pos)
{
	assert(pos != end());

	Node* prev = pos._node->_prev;
	Node* next = pos._node->_next;
	delete pos._node;
	prev->_next = next;
	next->_prev = prev;

	return iterator(next);
}


8. 析构函数

~list()
{
	clear();

	delete _head;
	_head = nullptr;
}

void clear()
{
	/*iterator it = begin();
	while (it != end())
	{
		iterator del = it++;
		delete del._node;
	}

	_head->_next = _head;
	_head->_prev = _head;*/
    iterator it = begin();
    while (it != end())
    {
		erase(it++);
	}
}

9. 反向迭代器

反向迭代器实现,重新写一个反向迭代器的类

它的成员是一个正向的迭代器,通过使用正向迭代器的某些功能完成反向迭代器的功能。

#pragma once

namespace bit
{
	// Iterator是哪个容器的迭代器,reverse_iterator<Iterator>就可以
	// 适配出哪个容器的反向迭代器。复用的体现
	template <class Iterator, class Ref, class Ptr>
	class reverse_iterator
	{
		typedef reverse_iterator<Iterator, Ref, Ptr> self;
	public:
		reverse_iterator(Iterator it)
			:_it(it)
		{}

		Ref operator*()
		{
			//return *_it;
			Iterator prev = _it;
			return *--prev;
		}

		Ptr operator->()
		{
			return &operator*();
		}

		self& operator++()
		{
			--_it;
			return *this;
		}

		self& operator--()
		{
			++_it;
			return *this;
		}

		bool operator!= (const self& rit) const
		{
			return _it != rit._it;
		}

	private:
		Iterator _it;
	};
}

template<class T>
class list
{
		typedef ListNode<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
//反向迭代器的typedef
    //这里const和普通的反向迭代器typedef反过来会报错
		typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
		typedef reverse_iterator<iterator, T&, T*> reverse_iterator;
    
    	reverse_iterator rbegin()
		{
		return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

}

三、list和vector、string作对比

vector缺陷:
连续的物理空间,是优势,也是劣势。
优势:支持高效随机访问。
劣势:
1、空间不够要增容,增容代价比较大。
2、可能存在一定空间浪费。按需申请,会导致频繁
增容,所以一般都会2倍左右扩容。
3、头部或者中部插入删除需要挪动数据,效率低下

list很好的解决vector的以上问题:
1、按需申请释放空间。
2、list任意位置支持O(1)插入删除。
本质vector和list是互补两个数据结构

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
引用和提供了关于实现vector的两种方法。其中,引用展示了一个使用reserve和push_back方法的示例,而引用展示了一个使用new和memcpy函数的示例。这两种方法都是常见的实现vector的方式。 在第一种方法中,通过reserve函数可以预留足够的内存空间,然后使用push_back函数逐个将元素添加到vector中。这种方法的好处是可以避免不必要的内存重分配,提高了效率。 而第二种方法使用new操作符在堆上分配内存空间,并使用memcpy函数将已有的vector对象的数据复制到新的内存空间中。通过这种方式,可以实现深拷贝,即两个vector对象拥有独立的内存空间。这种方法的好处是可以在不修改原始vector对象的情况下创建一个新的vector对象。 除了以上两种方法,还可以使用其他方式实现vector类。例如,可以使用动态数组来实现vector的底层数据结构,然后通过成员函数实现vector的各种操作,如增加、删除、查找等。 总结来说,c语言模拟实现vector的关键是动态内存管理和对元素的增删改查操作。可以使用预留空间和逐个添加元素的方式,也可以使用动态数组和复制数据的方式来实现vector类。具体的实现方式可以根据需求和实际情况选择。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++——vector模拟实现](https://blog.csdn.net/weixin_49449676/article/details/126813526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凛音Rinne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值