C++实现双向链表

本文介绍了如何使用C++实现双向链表,包括链表的节点类和链表类的设计,详细说明了节点的结构(数据、next指针、prev指针和节点类型)以及链表的初始化、添加节点(头部和尾部)、删除节点和迭代操作。代码示例展示了链表的增删及遍历功能。
摘要由CSDN通过智能技术生成

C++ 实现双向链表

简介

功能:实现环形链表

工程环境:CMake

文件目录介绍:

  • 结点类 Node 位于 LinkedList/Node.hpp
  • 链表类 LinkedList 位于 LinkedList/LinkedList.hpp
  • CMakeProject_2_List.cpp 用于测试

双向链表简介

该项目实现的双向链表示意图如下
双向链表示意图

每一个结点都由四个部分组成(这里只画出了三个),包括:数据、next 指针、prev 指针以及结点类型。

  • next 指针用来指向下一个结点,prev 指针用来指向前一个结点;
  • 结点类型包括:头部结点类型(HEAD)、尾部结点类型(TAIL)、普通结点类型(NORMAL),头部结点就是头部结点类型,尾部结点就是尾部结点类型,其他结点就是普通结点类型。这样做是为了方便区分头尾结点和其他结点(节点类型定义为枚举类)。

初始状态有一个头部结点和尾部结点,即 headtail,这两个结点中没有数据,只有 prev 指针和 next 指针。头部结点和尾部结点虽然在链表中,但是不计入总长度(因为这不是使用者创建的,而是编码者创建的)。即初始状态时链表长度为 0,当且仅当使用者添加结点后,长度才会大于 0。

初始状态时 head->next 指向 tailtail->prev 指向 headhead->prev 指向 tailtail->next 指向 head

对于链表的增删操作,具体如下:

添加结点时,分为尾部添加和头部添加两种,设新添加的结点为 newNode

  • 尾部添加:先获取最后一个结点 latestNode,将 newNodeprev 指向 latestNode,将 newNodenext 指向 tail;将 latestNodenext 指向 newNode,将 tailprev 指向 newNode
  • 头部添加:先获取第一个结点 firstNode,将 newNodenext 指向 firstNode,将 newNodeprev 指向 head;将 firstNodeprev 指向 newNode,将 headnext 指向 newNode
  • 一定要注意设置指针指向的顺序,因为 latestNode 是通过 tail->prev 获取的,firstNode 是通过 head->next 获取的。如果在此之前就设置了“将 tailprev 指向 newNode”或“将 headnext 指向 newNode”的话,我们获取到的就不是真正的 latestNodefirstNode 了。

删除结点时,先锁定要删除的结点,设为 node,然后将 node->prevnext 指向 node->next,将 node->nextprev 指向 node->prev,这样 node 就被剥离下来了,达到了删除的效果

对于链表的迭代,主要有 hasNext,hasPrevnext,prev 操作:

  • hasNext 判断是否有下一个结点(首尾节点不算),只需要判断当前节点的下一个结点类型是否为 NORMAL
  • hasPrev 判断是否有删一个结点(首尾节点不算),只需要判断当前节点的上一个结点类型是否为 NORMAL
  • next 是当前指针指向下一个结点
  • prev 是当前指针指向上一个结点

代码

设计时考虑到可扩展性,节点类和链表类都是模板类(因此后缀需要是 .hpp

项目地址:数据结构 链表 gitee

节点类

#pragma once

#include <iostream>

using namespace std;

#define NULL 0


enum NodeType
{
	HEAD,
	TAIL,
	NORMAL
};

template<typename D>
class Node
{
public:
	Node(NodeType nodeType, D data) {
		setNodeType(nodeType);
		setData(data);
		prev = NULL;
		next = NULL;
	};
	Node(NodeType nodeType, D data, Node* prev, Node* next) {
		setNodeType(nodeType);
		setData(data);
		setPrev(prev);
		setNext(next);
	};
	~Node() {}

	D getData() { return data; }
	void setData(D data) { this->data = data; }
	Node* getPrev() { return prev; }
	void setPrev(Node* prev) { this->prev = prev; }
	Node* getNext() { return next; }
	void setNext(Node* next) { this->next = next; }
	NodeType getNodeType() { return nodeType; }
	void setNodeType(NodeType nodeType) { this->nodeType = nodeType; }

	void printString() {
		cout << "Node {" << endl;
		cout << "\tNodeType : " << reflectNodeType(nodeType) << endl;
		cout << "\tdata = " << data << endl;
		cout << "\tprev = " << prev->getData() << endl;
		cout << "\tnext = " << next->getData() << endl;
		cout << "}" << endl;
	}

	void printLine() {
		cout << "Node {";
		cout << " NodeType : " << reflectNodeType(nodeType);
		cout << " data = " << data;
		cout << " prev = " << prev->getData();
		cout << " next = " << next->getData();
		cout << " }" << endl;
	}

private:
	Node* prev;
	Node* next;
	D data;
	NodeType nodeType;

	string reflectNodeType(NodeType nodeType) {
		switch (nodeType)
		{
		case HEAD:
			return "HEAD";
			break;
		case TAIL:
			return "TAIL";
			break;
		case NORMAL:
			return "NORMAL";
			break;
		default:
			return "NULL";
			break;
		}
	}
};

链表类

#pragma once
#include "Node.hpp"

using namespace std;

/**
 * @brief 链表,双向环形,初始时有首尾定位结点(不纳入总结点计数),模型为:head-->结点-->tail-->head.
 */
template<typename T>
class LinkedList
{
public:
	LinkedList() {
		head = new Node<T>(HEAD, 0);
		tail = new Node<T>(TAIL, 0);
		head->setNext(tail);
		head->setPrev(tail);
		tail->setNext(head);
		tail->setPrev(head);
		size = 0;
		node = head;
	}
	~LinkedList() {};


	void addToTail(Node<T>* node);
	void addToTail(T data);
	void addToHead(Node<T>* node);
	void addToHead(T data);

	void remove(Node<T>* node);
	void remove(T data);

	Node<T>* selectFirstNode(T data);
	Node<T>* get(int index);

	Node<T>* getLastNode() { if (size > 0) return tail->getPrev(); else return head; }
	Node<T>* getFirstNode() { if (size > 0) return head->getNext(); else return head; }


	Node<T>* getHead() { return head; }
	Node<T>* getTail() { return tail; }
	int getSize() { return size; }


	void printString() {
		cout << "LinkedList (size = " << size << ") {" << endl;
		cout << "   head ";
		head->printLine();
		while (hasNext())
		{
			cout << "-> node ";
			next()->printLine();
		}
		cout << "-> tail ";
		tail->printLine();
		cout << "}" << endl;
	}
public:
	// 该部分迭代用

	bool hasNext() { return node->getNext()->getNodeType() == NORMAL; };
	bool hasPrev() { return node->getPrev()->getNodeType() == NORMAL; };
	Node<T>* next() {
		node = node->getNext();
		return node;
	}
	Node<T>* prev() {
		node = node->getPrev();
		return node;
	}
private:
	Node<T>* head;
	Node<T>* tail;
	int size;

	/* 当前节点指针 */
	Node<T>* node;
};


/**
 * @brief 尾部添加结点.
 * @param node 结点
 */
template <typename T>
void LinkedList<T>::addToTail(Node<T>* node)
{
	node->setNext(tail);
	node->setPrev(getLastNode());
	getLastNode()->setNext(node);
	tail->setPrev(node);
	//node->printLine();
	size++;
}

/**
 * @brief 尾部添加节点.
 * @param 结点对应的数据
 */
template <typename T>
void LinkedList<T>::addToTail(T data)
{
	Node<T>* node = new Node(NORMAL, data, getLastNode(), tail);
	getLastNode()->setNext(node);
	tail->setPrev(node);
	//node->printLine();
	size++;
}

/**
 * @brief 头部添加结点.
 * @param node 结点
 */
template<typename T>
void LinkedList<T>::addToHead(Node<T>* node)
{
	node->setPrev(tail);
	node->setnext(getFirstNode());
	getFirstNode()->setPrev(node);
	head->setNext(node);
	//node->printLine();
	size++;
}

/**
 * @brief 头部添加节点.
 * @param 结点对应的数据
 */
template<typename T>
void LinkedList<T>::addToHead(T data)
{
	Node<T>* node = new Node(NORMAL, data, head, getFirstNode());
	getFirstNode()->setPrev(node);
	head->setNext(node);
	//node->printLine();
	size++;
}

/**
 * @brief 移除 node 结点.
 * @param node 结点
 */
template<typename T>
void LinkedList<T>::remove(Node<T>* node) {
	remove(node->getData());
}

/**
 * @brief 移除数据为 data 结点.
 * @param data 节点数据
 */
template<typename T>
void LinkedList<T>::remove(T data) {
	node = head;
	if (size > 0)
	{
		while (hasNext())
		{
			if (next()->getData() == data)
			{
				node->getPrev()->setNext(node->getNext());
				node->getNext()->setPrev(node->getPrev());
				node = head;
				size--;
				return;
			}
			else continue;
		}
		return;
	}
	else return;
}

/**
  * @brief 找到第一个值为 data 的结点,要求 data 的类必须可以被比较,如果是自定义类,建议重载 == 运算符. 如果链表没有结点,则只返回头部结点;如果找不到,就返回尾部结点
  * @return Node<T>*
  */
template<typename T>
Node<T>* LinkedList<T>::selectFirstNode(T data)
{
	node = head;
	if (size > 0)
	{
		while (hasNext())
		{
			if (next()->getData() == data) return node;
			else continue;
		}
		return tail;
	}
	else return head;
}

/**
 * @brief 根据索引所在位置找到对应的结点,0 代表 head,size + 1 代表 tail,其余的代表中间结点. 如果链表没有结点,则只返回头部节点
 * @return Node<T>*
 */
template<typename T>
Node<T>* LinkedList<T>::get(int index)
{
	node = head;
	if (size > 0 || index <= 0)
	{
		if (index > size) return tail;
		else
		{
			int count = 0;
			while (count < index)
			{
				next();
				count++;
			}
			return node;
		}

	}
	else return head;
}

测试

#include "CMakeProject_2_List.h"

using namespace std;

void testNode();
void testLinkedList();

int main()
{
	/* Node test */
	/* head->node->tail->head */

	testLinkedList();
	//cout << "Hello CMake." << endl;
	return 0;
}

void testNode() {
	/* Node test */
	/* head->node->tail->head */

	Node<int>* head = new Node(HEAD, 0);
	Node<int>* tail = new Node(TAIL, 0);
	Node<int>* node = new Node(NORMAL, 1, head, tail);
	head->setNext(node);
	head->setPrev(tail);
	tail->setNext(head);
	tail->setPrev(node);

	/* test success ! */

	node->printString();

	node->~Node();
}

void testLinkedList() {
	LinkedList<int>* linkedList1 = new LinkedList<int>();
	LinkedList<int>* linkedList2 = new LinkedList<int>();
	for (int i = 0; i < 10; i++)
	{
		linkedList1->addToTail(i + 1);
		linkedList2->addToHead(i + 1);
	}
	cout << "linkedlist1" << endl;
	linkedList1->printString();
	linkedList1->getHead()->printString();
	linkedList1->getTail()->printString();

	cout << endl << endl;
	cout << "linkedlist2" << endl;
	linkedList2->printString();
	linkedList2->getHead()->printString();
	linkedList2->getTail()->printString();

	cout << endl << endl;
	cout << "node1" << endl;
	Node<int>* node1 = linkedList1->get(4);
	node1->printString();

	cout << endl << endl;
	cout << "node2" << endl;
	Node<int>* node2 = linkedList1->selectFirstNode(5);
	node2->printString();

	cout << endl << endl;
	cout << "remove node 1" << endl;
	linkedList1->remove(node1);
	linkedList1->printString();
}

测试结果

linkedlist1
LinkedList (size = 10) {
   head Node { NodeType : HEAD data = 0 prev = 0 next = 1 }
-> node Node { NodeType : NORMAL data = 1 prev = 0 next = 2 }
-> node Node { NodeType : NORMAL data = 2 prev = 1 next = 3 }
-> node Node { NodeType : NORMAL data = 3 prev = 2 next = 4 }
-> node Node { NodeType : NORMAL data = 4 prev = 3 next = 5 }
-> node Node { NodeType : NORMAL data = 5 prev = 4 next = 6 }
-> node Node { NodeType : NORMAL data = 6 prev = 5 next = 7 }
-> node Node { NodeType : NORMAL data = 7 prev = 6 next = 8 }
-> node Node { NodeType : NORMAL data = 8 prev = 7 next = 9 }
-> node Node { NodeType : NORMAL data = 9 prev = 8 next = 10 }
-> node Node { NodeType : NORMAL data = 10 prev = 9 next = 0 }
-> tail Node { NodeType : TAIL data = 0 prev = 10 next = 0 }
}
Node {
        NodeType : HEAD
        data = 0
        prev = 0
        next = 1
}
Node {
        NodeType : TAIL
        data = 0
        prev = 10
        next = 0
}


linkedlist2
LinkedList (size = 10) {
   head Node { NodeType : HEAD data = 0 prev = 1 next = 10 }
-> node Node { NodeType : NORMAL data = 10 prev = 0 next = 9 }
-> node Node { NodeType : NORMAL data = 9 prev = 10 next = 8 }
-> node Node { NodeType : NORMAL data = 8 prev = 9 next = 7 }
-> node Node { NodeType : NORMAL data = 7 prev = 8 next = 6 }
-> node Node { NodeType : NORMAL data = 6 prev = 7 next = 5 }
-> node Node { NodeType : NORMAL data = 5 prev = 6 next = 4 }
-> node Node { NodeType : NORMAL data = 4 prev = 5 next = 3 }
-> node Node { NodeType : NORMAL data = 3 prev = 4 next = 2 }
-> node Node { NodeType : NORMAL data = 2 prev = 3 next = 1 }
-> node Node { NodeType : NORMAL data = 1 prev = 2 next = 0 }
-> tail Node { NodeType : TAIL data = 0 prev = 0 next = 0 }
}
Node {
        NodeType : HEAD
        data = 0
        prev = 1
        next = 10
}
Node {
        NodeType : TAIL
        data = 0
        prev = 0
        next = 0
}


node1
Node {
        NodeType : NORMAL
        data = 4
        prev = 3
        next = 5
}


node2
Node {
        NodeType : NORMAL
        data = 5
        prev = 4
        next = 6
}


remove node 1
LinkedList (size = 9) {
   head Node { NodeType : HEAD data = 0 prev = 0 next = 1 }
-> node Node { NodeType : NORMAL data = 1 prev = 0 next = 2 }
-> node Node { NodeType : NORMAL data = 2 prev = 1 next = 3 }
-> node Node { NodeType : NORMAL data = 3 prev = 2 next = 5 }
-> node Node { NodeType : NORMAL data = 5 prev = 3 next = 6 }
-> node Node { NodeType : NORMAL data = 6 prev = 5 next = 7 }
-> node Node { NodeType : NORMAL data = 7 prev = 6 next = 8 }
-> node Node { NodeType : NORMAL data = 8 prev = 7 next = 9 }
-> node Node { NodeType : NORMAL data = 9 prev = 8 next = 10 }
-> node Node { NodeType : NORMAL data = 10 prev = 9 next = 0 }
-> tail Node { NodeType : TAIL data = 0 prev = 10 next = 0 }
}

N:\ANotes\计算机\数据结构\Code\CMakeProject_2_List\out\build\x64-debug\CMakeProject_2_List\CMakeProject_2_List.exe (进程 10392)已退出,代码为 0。
按任意键关闭此窗口. . .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

HiUniverse_zyfstep

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

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

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

打赏作者

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

抵扣说明:

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

余额充值