c++学习(list)[9]

list的使用

在这里插入图片描述
头文件:

 #inliude<list>

C++标准库(STL)中的std::list是一个双向链表容器,它提供了一些有用的操作和功能。与std::vector相比,std::list具有以下特点:

  1. 链表结构:std::list使用链表结构实现,每个元素都包含指向前一个和后一个元素的指针,因此在插入和删除元素时,不会涉及内存的重新分配和移动,相对于std::vector,插入和删除操作的时间复杂度为O(1)。

  2. 无连续内存:由于std::list使用链表实现,它的元素在内存中是不连续存储的。这意味着std::list不支持随机访问,不能像std::vector那样使用索引访问元素,只能通过迭代器进行遍历。

  3. 动态大小:std::list的大小可以动态增长和收缩,不需要预先指定容器的大小。

  4. 插入和删除效率高:由于链表结构的特性,std::list在任意位置插入和删除元素的效率都很高,不会引起其他元素的移动。

  5. 不支持快速随机访问:由于没有连续的内存存储,std::list不支持像std::vector那样的快速随机访问。如果需要通过索引访问元素,应该使用std::vector

std::list提供了一系列成员函数和算法,可以方便地对链表进行操作,例如push_back()push_front()pop_back()pop_front()insert()erase()等。此外,std::list还提供了size()empty()front()back()等成员函数来获取链表的大小和访问元素。

需要注意的是,由于std::list的元素在内存中不是连续存储的,因此在访问元素时,性能可能相对较低。如果需要频繁的随机访问和修改元素,std::vector可能是更好的选择。而std::list在需要频繁插入和删除元素的场景下,具有更好的性能。

listd的尾插和遍历举例

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

void test_list1()
{
	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;

	it = lt.begin();        //迭代器修改值
	while (it != lt.end())
	{
		*it *= 2;
		++it;
	}
	cout << endl;

	//遍历:范围for
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

}

int main()
{
	test_list1();
	return 0;
}

在这里插入图片描述

头插和尾部弹出

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

void test_list1()
{
	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;

	it = lt.begin();        //迭代器修改值
	while (it != lt.end())
	{
		*it *= 2;
		++it;
	}
	cout << endl;

	//遍历:范围for
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//头插和尾部弹出
	lt.push_front(10);
	lt.push_front(10);
	lt.push_front(10);

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

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

int main()
{
	test_list1();
	return 0;
}

在这里插入图片描述

insert和erase的指针失效问题

在使用std::listinsert()erase()函数时,insert()不会出现指针失效的问题。这是因为std::list使用链表结构实现,插入和删除元素时只需要调整节点的指针,不会涉及内存的重新分配和移动。

具体来说,当调用insert()函数插入一个元素时,它会在指定位置之前插入一个新的节点,并调整前一个节点和后一个节点的指针,使得链表仍然保持正确的连接关系。插入操作后,原来存在的节点和迭代器仍然有效。

当调用erase()函数删除一个元素时,它会删除指定位置的节点,指针的位置会失效。

void test_list2()
{
	list<int> lt;
	//插入数据(尾插)
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	auto pos = find(lt.begin(), lt.end(), 3);
	if (pos != lt.end())     //pos不会失效
	{
		lt.insert(pos, 30);
		*pos *= 100;
	}

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

	pos = find(lt.begin(), lt.end(), 4);
	if (pos != lt.end())     //pos会失效
	{
		lt.erase(pos);
		//cout << *pos << " ";   此时的pos已经被删除  成了野指针  
	}

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main()
{
	test_list2();
	return 0;
}

自定义实现list

在这里插入图片描述

基本框架

#pragma once

namespace b
{
	template<class T>
	class list_node
	{
		T _data;             //存放数据
		list_node<T>* _next; //指向下一个结点
		list_node<T>* _prev; //指向前一个结点
		
		list_node(const T& x = T())   //构造函数
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

	template<class T>
	class list
	{
		typedef; list_node<T> Node;
	public:
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->prev = _head;
		}

	private:
		Node* _head;  //头结点
	};
}

实现push_back()

将结点信息传入push_back()函数,然后将头结点 尾部结点 和新节点的指针做一一对应即可

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

iterator


	template<class T>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		Node* _node;
		
		_list_iterator(Node* node)
			:_node(node)
		{}
#pragma once

namespace b
{
	template<class T>
	class list_node
	{
		T _data;             //存放数据
		list_node<T>* _next; //指向下一个结点
		list_node<T>* _prev; //指向前一个结点

		list_node(const T& x = T())   //构造函数
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

	template<class T>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		Node* _node;
		
		_list_iterator(Node* node)
			:_node(node)
		{}
	};
//
//
//对list中的迭代器进行补充
	template<class T>
	class list
	{
		typedef; list_node<T> Node;
	public:
		typedef _list_iterator<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* tail = _head->_prev;
			Node* newnode = new Node(x);
			//_head tail newnode
			tail->_next = newnode;
			newnode->prev = tail;
			newnode->_next = _head;
			_head->_next = newnode;
		}

	private:
		Node* _head;  //头结点
	};
}

iterator中的运算符重载

	template<class T>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> iterator;
		Node* _node;
		
		_list_iterator(Node* node)
			:_node(node)
		{}

		bool operator!=(const iterator& it) const
		{
			return _node != it._node;
		}
		bool operator==(const iterator& it)const
		{
			return _node == it.node;
		}
		//*it it.operator*()  解引用
		T& operator*()
		{
			return _node->next;
		}

      T* operator->() { return &(operator*()); }
		// ++it
		iterator& operator++()
		{
			return _node->_next;
		}
	};

在使用T* operator->() { return &(operator*()); }输出cout<<it->?时实际上有两个->-> 为it->->? 编译器优化了一个
在这里插入图片描述
std::list中,运算符重载->的作用是用于访问链表中节点的成员。当我们使用->操作符访问链表中的节点时,实际上是访问了节点的指针,并通过该指针来访问节点的成员。

例如,假设有一个std::list对象myList,其中包含了若干个节点,每个节点都有一个成员变量data。我们可以通过迭代器来遍历链表,并使用->操作符访问每个节点的data成员。示例如下:

#include <iostream>
#include <list>

struct Node {
    int data;
    Node(int d) : data(d) {}
};

int main() {
    std::list<Node> myList;
    myList.push_back(Node(1));
    myList.push_back(Node(2));
    myList.push_back(Node(3));

    for (auto it = myList.begin(); it != myList.end(); ++it) {
        std::cout << it->data << " ";
    }
    std::cout << std::endl;

    return 0;
}

在上述示例中,我们定义了一个结构体Node,其中包含一个整型成员变量data。然后,我们创建了一个std::list对象myList,并向其中插入了三个节点。接下来,我们使用迭代器遍历链表,并通过->操作符访问每个节点的data成员,将其输出到控制台。

编译器在优化代码时,可能会对->操作符进行一些优化,例如将其转换为直接访问成员的方式,从而提高代码的执行效率。这种优化是由编译器自动完成的,我们无需手动干预。

需要注意的是,->操作符的优化可能因编译器和编译选项的不同而有所差异。在实际使用中,我们应该根据具体情况进行测试和评估,以确保代码的正确性和性能。

const iterator

此时我们的iterator并不能处理const类型

#pragma once

namespace b
{
	template<class T>
	class list_node
	{
		T _data;             //存放数据
		list_node<T>* _next; //指向下一个结点
		list_node<T>* _prev; //指向前一个结点

		list_node(const T& x = T())   //构造函数
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

	//template<class T>
	//为处理const类型   改为像指针一样的对象
	template<class T,class Ref,class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> iterator;
		Node* _node;
		
		_list_iterator(Node* node)
			:_node(node)
		{}

		bool operator!=(const iterator& it) const
		{
			return _node != it._node;
		}
		bool operator==(const iterator& it)const
		{
			return _node == it.node;
		}
		//*it it.operator*()  解引用
		Ref operator*()
		{
			return _node->next;
		}
		
		Ref operator->() { return &(operator*()); }

		// ++it
		iterator& operator++()
		{
			return _node->_next;
		}
	};

	template<class T>
	class list
	{
		typedef; list_node<T> Node;
	public:
		typedef _list_iterator<T> iterator;
		typedef _list_const_iterator<T,const T&,const T*> const_iterator;
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->prev = _head;
		}

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

	private:
		Node* _head;  //头结点
	};
}

insert

思路和push_back类似

		void insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);
			//prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->next = cur;
			cur->_prev = newnode;
			
			return iterator(newnode);
		}

push_front

利用insert实现

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

erease

void erase(iterator pos)
		{
			assert(pos != end());

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

			prev->_next = next;
			next->prev = prev;
			delete cur;

			return iterator(next);
		}

pop_front

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

编译器报错

为不发生报错 要在自定义结构体iterator中加入

		typedef biddirectional_iterator_tag iterator_category;
		typedef T value_type;
		typedef Ptr pointer;
		typedef Ref reference;
		typedef ptrdiff_t difference_type;

具体原因未知?

完整代码

#pragma once

namespace b
{
	template<class T>
	class list_node
	{
		T _data;             //存放数据
		list_node<T>* _next; //指向下一个结点
		list_node<T>* _prev; //指向前一个结点

		list_node(const T& x = T())   //构造函数
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

	//template<class T>
	//为处理const类型   改为像指针一样的对象
	template<class T,class Ref,class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> iterator;

		typedef biddirectional_iterator_tag iterator_category;
		typedef T value_type;
		typedef Ptr pointer;
		typedef Ref reference;
		typedef ptrdiff_t difference_type;

		Node* _node;
		
		_list_iterator(Node* node)
			:_node(node)
		{}

		bool operator!=(const iterator& it) const
		{
			return _node != it._node;
		}
		bool operator==(const iterator& it)const
		{
			return _node == it.node;
		}
		//*it it.operator*()  解引用
		Ref operator*()
		{
			return _node->next;
		}
		
		Ref operator->() { return &(operator*()); }

		// ++it
		iterator& operator++()
		{
			return _node->_next;
		}
	};

	template<class T>
	class list
	{
		typedef; list_node<T> Node;
	public:
		typedef _list_iterator<T> iterator;
		typedef _list_const_iterator<T,const T&,const T*> const_iterator;
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->prev = _head;
		}

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

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

		void insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);
			//prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->next = cur;
			cur->_prev = newnode;
			
			return iterator(newnode);
		}
		
		void pop_front()
		{
			erase(begin());
		}

		void erase(iterator pos)
		{
			assert(pos != end());

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

			prev->_next = next;
			next->prev = prev;
			delete cur;

			return iterator(next);
		}
	private:
		Node* _head;  //头结点
	};
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值