手写简单STL标准库——List的实现

list

std::list 和 std::vector 是两种不同的数据结构,std::vector 是基于数组的动态数组,而 std::list 是基于双向链表的数据结构。

list适用于需要在序列中执行频繁插入和删除操作的场景。


list的特性

双向链表:list是双向链表,允许在序列的两端和中间执行搞笑的插入和删除操作。

不支持随机访问:与vector和deque不同,list不支持通过索引进行常量时间内的随机访问。要访问list的元素,必须通过迭代器进行。

动态内存管理:list的内部实现使用节点,每个节点都包含一个元素和指向前后节点的指针。这种结构使得list在执行插入和删除操作时能够更好地管理内存。

保持迭代器有效性: list在进行插入和删除操作时,能够更好地保持迭代器的有效性。这意味着在进行这些操作后,不会导致所有迭代器失效。

高效的插入和删除操作: 由于list是双向链表,插入和删除操作在两端和中间都是常量时间的,使其成为处理这类操作的理想容器。


list的性能考虑

插入和删除操作: 如果主要进行频繁的插入和删除操作,并且不需要随机访问元素,list可能比vector和deque更为高效。

随机访问: 如果需要通过索引进行随机访问元素,使用vector可能更为合适,因为它提供了常量时间的随机访问。

内存使用: 由于list使用了链表结构,可能引入一些额外的内存开销。在内存使用方面,vector和deque可能更为紧凑


C++标准库中list的基本用法

//要使用list,首先需要包含相关的头文件:
#include <list>
//接下来,可以声明一个list对象,并开始使用它:

std::list<int> myList;
//可以使用push_front和push_back在list的前端和后端插入元素:

myList.push_front(1);
myList.push_back(2);
//使用pop_front和pop_back从list的前端和后端删除元素:

myList.pop_front();
myList.pop_back();

list工作原理

蓝色矩形框:堆内存

红色矩形块:栈内存

红色箭头:next指针

蓝色箭头:prev指针

如图展示了一个list从初始化到完成多次插入的流程。List类也有一个控制结构, 包含headtailsize三个成员, 分别控制链表的头尾指针和大小, 让我们梳理其生命流程:

  1. 初始时刻, 其链表为空, size=0, 均为空指针, 只有这些控制结构存储在栈上
  2. 执行push_back(1), 在堆内存中分配了一个Node(参考后文实现, 其实就是个双向链表节点), 将headtail指向这个节点, 更新size=1
  3. 执行push_back(2), 在堆内存中分配了一个Node, 需要注意的时, 这个新的节点不需要和旧的节点内存连续, 更新原tail指向节点的next, 将tail指向这个节点, 并且将新的节点的prev指向原来的tail, 更新size=2
  4. 执行push_back(3), 操作与3相同, 此过程完成后, size=3
  5. 执行push_front(0), 在堆内存中分配了一个Node, 需要注意的时, 这个新的节点不需要和旧的节点内存连续, 此时更新原head指向节点的prev, 将head指向这个节点, 并且将新的节点的next指向原来的head, 更新size=4

实现list

#pragma once

#include <iostream>
#include <stdexcept>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>

template <typename T>
class List
{
public:
public:
	template <typename L>
	friend std::ostream& operator<<(std::ostream& os, const List<L>& pt);
private:
	//定义节点结构
	struct Node
	{
		T data;
		Node* next;//指向下一个节点的指针
		Node* prev;//指向前一个节点的指针

		//构造函数
		Node(const T& value, Node* nextNode = nullptr, Node* prevNode = nullptr)
			:data(value), next(nextNode), prev(prevNode) {}
	};

	Node *head;//头节点指针
	Node* tail;//尾节点指针
	size_t size;//链表中节点的数量

public:
	//构造函数
	List()
		:head(nullptr)
		,tail(nullptr)
		,size(0)
	{}

	//析构函数
	~List() {clear();}

	//在链表末尾添加元素
	void push_back(const T& value)
	{
		//创建新的节点
		Node* newNode = new Node(value, nullptr, tail);

		if (tail)
		{
			//如果链表非空,将尾节点的next指针指向新节点
			tail->next = newNode;
		}
		else
		{
			//如果链表为空,新节点同时也是头节点
			head = newNode;
		}

		//更新尾节点指针和链表大小
		tail = newNode;
		++size;
	}

	//在链表开头添加元素
	void push_front(const T& value)
	{
		//创建新的节点
		Node* newNode = new Node(value, head, nullptr);

		if (head)
		{
			//如果链表非空,将头节点的prev 指针指向新节点
			head->prev = nextNodel;
		}
		else
		{
			//如果链表为空,新节点同时也是尾节点
			tail = newNode;
		}

		//更新头节点指针和链表大小
		head = newNode;
		++size;
	}

	//获取链表中节点的数量
	size_t getSize() const { return size; }

	//访问链表中的元素
	T& operator[](size_t index)
	{
		//从头节点开始遍历链表,找到第index个节点
		Node* current = head;
		for (size_t i = 0; i < index; ++i)
		{
			if (!current)
			{
				//如果index 超出链表长度,则抛出异常
				throw std::out_of_range("Index out of range");
			}
			current = current->next;
		}

		//返回节点中的数据
		return = current->next;
	}

	//删除链表末尾的元素
	void pop_back()
	{
		if (size > 0)
		{
			//获取尾节点的前一个节点
			Node *newTail = tail->prev;

			//删除尾节点
			delete tail;

			//更新尾节点指针和链表大小
			tail = newTail;
			if (tail)
			{
				tail->next = nullptr;
			}
			else
			{
				head = nullptr;//如果链表为空,头节点也置为空
			}

			--size;
		}
	}

	//删除链表开头元素
	void pop_front()
	{
		if (size > 0)
		{
			//获取头节点的下一个节点
			Node* newHead = head->next;

			//删除头节点
			delete;

			//更新头节点指针和链表大小
			head = newHead;
			if (head)
			{
				head->prev = nullptr;
			}
			else
			{
				tail = nullptr;//如果链表为空,尾节点也置为空
			}
			--size;
		}
	}

	//获取指定节点
	Node* getNode(const T &val)
	{
		Node* node = head;
		while (node != nullptr && node->val != val)
		{
			node = node->next;
		}
		return node;
	}

	T* find(const T& val)
	{
		Node* node = getNode(val);
		if (node == nullptr)
		{
			return nullptr;
		}
		return &node->val;
	}

	//删除指定节点
	void remove(const T &val)
	{
		Node* node = head;
		while (node != nullptr && node->val != val)
		{
			node = node->next;
		}
		if (node == nullptr)
		{
			//没有找到
			return;
		}
		else if (node != head && node != tail)
		{
			//既不是头节点也不是尾节点
			node->pre->next = node->next;
			node->next->pre = node->pre;
		}
		else if (node == head && node == tail)
		{
			//既是头节点又是尾节点
			head = nullptr;
			node = nullptr;
		}
		else if (node == head)
		{
			//是头节点
			head = node->next;
			head->pre = nulllptr;
		}
	}

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

	//清空链表
	void clear()
	{
		while (head)
		{
			//从头结点开始,依次删除节点
			Node* temp = head;
			head = head->next;
			delete temp;
		}

		//更新尾节点指针和指针大小
		tail = nullptr;
		size = 0;
	}

	//使用迭代器遍历链表的开始位置
	Node* begin() { return head; }

	//使用迭代器遍历链表的位置
	Node* end() { return nullptr; }

	// 使用迭代器遍历链表的开始位置(const版本)
	const Node* begin() const { return head; }

	// 使用迭代器遍历链表的结束位置(const版本)
	const Node* end() const { return nullptr; }

	// 打印链表中的元素
	void printElements() const
	{
		for (Node* current = head; current; current = current->next)
		{
			std::cout << current->data << " ";
		}
		std::cout << std::endl;
	}
};


// 重载 << 运算符
template <typename T>
std::ostream& operator<<(std::ostream& os, const List<T>& pt)
{
	for (typename List<T>::Node* current = pt.head; current;
		current = current->next)
	{
		os << " " << current->data;
	}
	os << std::endl;
	return os;
}



int main() {
	// 创建一个 List 对象
	List<int> myList;

	// N 行,每行包含一个命令,命令格式为 [operation] [parameters]
	int N;
	std::cin >> N;
	// 读走回车
	getchar();
	std::string line;
	// 接收命令
	for (int i = 0; i < N; i++) {
		std::getline(std::cin, line);
		std::istringstream iss(line);
		std::string command;
		iss >> command;
		int value;

		if (command == "push_back") {
			iss >> value;
			myList.push_back(value);
		}

		if (command == "push_front") {
			iss >> value;
			myList.push_front(value);
		}

		if (command == "pop_back") {
			myList.pop_back();
		}

		if (command == "pop_front") {
			myList.pop_front();
		}

		if (command == "remove") {
			iss >> value;
			myList.remove(value);
		}

		if (command == "clear") {
			myList.clear();
		}

		if (command == "size") {
			std::cout << myList.getSize() << std::endl;
		}

		if (command == "get") {
			iss >> value;
			std::cout << myList[value] << std::endl;
		}

		if (command == "print") {
			if (myList.getSize() == 0) {
				std::cout << "empty" << std::endl;
			}
			else {
				myList.printElements();
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值