IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【C++】list的使用和模拟实现 -> 正文阅读

[数据结构与算法]【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是互补两个数据结构

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:38:43  更:2022-04-01 23:40:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:01:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码