C++类实现双链表

先直接上代码:

#include <iostream>

using std::cout;
using std::endl;

struct Node {
	Node(int data = 0, Node *pre = nullptr, Node *next = nullptr)
		: _data(data)
		, _pre(pre)
		, _next(next) {
		cout << "Node(int  = 0, Node * = nullptr, Node * = nullptr) " << endl;
	}

	~Node() {
		cout << "~Node()" << endl;
	}
	int _data;
	Node *_pre;
	Node *_next;
};

class List {
	public:
		List()
			: _head(new Node())
			, _tail(new Node())
			, _size(0) {
			cout << "List()" << endl;
			_head->_next = _tail;
			_tail->_pre = _head;
		}
		//在头部进行插入
		void push_front(int data) {
			Node *newNode = new Node(data);
			newNode->_pre = _head;
			newNode->_next = _head->_next;
			_head->_next->_pre = newNode;
			_head->_next = newNode;

			++_size;
		}
		//在尾部进行插入
		void push_back(int data) {
			Node *newNode = new Node(data);
			newNode->_pre = _tail->_pre;
			newNode->_next = _tail;
			_tail->_pre->_next = newNode;
			_tail->_pre = newNode;

			++_size;

		}

		//在头部进行删除
		void pop_front() {
			if (size() > 0) {
				Node *pNode = _head->_next;
				pNode->_next->_pre = _head;
				_head->_next = pNode->_next;

				delete pNode;
				pNode = nullptr;
				--_size;
			} else {
				cout << "该链表为空,无法删除"  << endl;
				return;
			}
		}
		//在尾部进行删除
		void pop_back() {
			if (size() > 0) {
				Node *pNode = _tail->_pre;
				pNode->_pre->_next = _tail;
				_tail->_pre = pNode->_pre;

				delete pNode;
				pNode = nullptr;
				--_size;
			} else {
				cout << "该链表为空,无法删除"  << endl;
				return;
			}
		}

		//在中间进行插入
		void insert(int pos, int data) {
			if (pos < 0 || pos > size()) {
				cout << "该位置异常,无法插入" << endl;
				return;
			}

			Node *pNode = _head;
			while (pos-- > 0) {
				pNode = pNode->_next;
			}

			Node *newNode = new Node(data);
			newNode->_pre = pNode;
			newNode->_next = pNode->_next;
			pNode->_next->_pre = newNode;
			pNode->_next = newNode;

			++_size;
		}

		//在中间进行删除
		void erase(int data) {
			bool flag = find(data);
			if (flag) {
				Node *pNode = _head->_next;
				while (pNode && pNode != _tail) {
					if (pNode->_data == data) {
						Node *ptmp = pNode->_pre;
						pNode->_next->_pre = ptmp;
						ptmp->_next = pNode->_next;

						delete pNode;
						pNode = nullptr;
						--_size;

						pNode = ptmp;
					}
					pNode = pNode->_next;
				}
			} else {
				cout << "该元素不在链表之中,无法删除" << endl;
				return;
			}
		}

		bool find(int data) {
			if (size() > 0) {
				Node *pNode = _head->_next;
				while (pNode && pNode != _tail) {
					if (data == pNode->_data) {
						return true;
					}
					pNode = pNode->_next;
				}

				return false;
			} else {
				cout << "该链表为空,无法查找"  << endl;
				return false;
			}
		}

		void display() const {
			if (size() > 0) {
				Node *pNode = _head->_next;
				while (pNode && pNode != _tail) {
					cout << pNode->_data << "  ";
					pNode = pNode->_next;
				}
				cout << endl;
			} else {
				cout << "该链表为空,无法打印" << endl;
				return;
			}
		}
		~List() {
			cout << "~List()" << endl;
			Node *deleteNode = _head->_next;
			while (deleteNode) {
				Node *ptmp = deleteNode->_next;
				delete deleteNode;
				deleteNode = nullptr;
				deleteNode = ptmp;
			}
			delete _head;
			_head = nullptr;
		}

		int size() const {
			return _size;
		}
	private:
		Node *_head;
		Node *_tail;
		int _size;
};
int main(int argc, char **argv) {
	List lst;
	lst.display();

	cout << endl << "在链表头部进行插入 :" << endl;
	lst.push_front(1);
	lst.push_front(3);
	lst.push_front(5);
	lst.display();

	cout << endl << "在链表尾部进行插入 :" << endl;
	lst.push_back(11);
	lst.push_back(33);
	lst.push_back(55);
	lst.display();

	cout << endl << "在链表头部进行删除 :" << endl;
	lst.pop_front();
	lst.display();

	cout << endl << "在链表尾部进行删除 :" << endl;
	lst.pop_back();
	lst.display();

	cout << endl << "查找元素 :" << endl;
	/* bool flag = lst.find(100); */
	bool flag = lst.find(11);
	cout << "flag = " << flag << endl;

	cout << endl << "在中间进行插入" << endl;
	/* lst.insert(10, 100); */
	lst.insert(2, 100);
	lst.display();

	cout << endl << "在中间进行删除" << endl;
	lst.erase(11);
	lst.display();
	return 0;
}

该方式模仿STL List容器的实现,但仅支持int数据类型,引入模板后可支持泛型。

需要注意的点为:当调用类的构造函数创建对象时,默认会创建头结点为尾结点,但头结点和尾结点不存储数据,这两个节点用于头插尾插头删尾删的定位操作。

代码解析

struct Node部分

struct Node {
	Node(int data = 0, Node *pre = nullptr, Node *next = nullptr)
		: _data(data)
		, _pre(pre)
		, _next(next) {
		cout << "Node(int  = 0, Node * = nullptr, Node * = nullptr) " << endl;
	}

	~Node() {
		cout << "~Node()" << endl;
	}
	int _data;
	Node *_pre;
	Node *_next;
};

在C++中对结构体的功能做了增强,支持构造函数,该函数用于初始化对象。以上代码是双链表的数据类型定义和新结点的初始化操作。在C++中struct就是定义类的关键字,其默认访问权限为public,与之对应的定义类的关键字为class,class的默认访问权限为private.

List类部分

构造函数

List()
	: _head(new Node())
	, _tail(new Node())
	, _size(0) {
	cout << "List()" << endl;
	_head->_next = _tail;
	_tail->_pre = _head;
}

该函数的作用用于创建类对象和初始化数据成员,_head->_next = _tail;
_tail->_pre = _head;两行代码用于创建两个头结点为尾结点,其中头结点和尾结点不带数据,中间插入的节点带数据。

析构函数

~List() {
		cout << "~List()" << endl;
		Node *deleteNode = _head->_next;
		while (deleteNode) {
			Node *ptmp = deleteNode->_next;
			delete deleteNode;
			deleteNode = nullptr;
			deleteNode = ptmp;
		}
		delete _head;
		_head = nullptr;
	}

析构函数的作用用于销毁对象的数据成员,从头结点的下一个结点开始遍历采用带头尾节点的双链表,每次有一个指针保存当前节点的下一个结点,删除当前结点,再将当前结点的下一个结点置为当前结点,依次循环删除,直到只剩下头结点,最后删除头结点即完成链表的销毁操作。该双链表每次开辟结点在堆申请空间,销毁链表时需要手动释放,否则造成内存泄漏(即分配完以后未释放,造成该片已分配的空间无法再次使用)。

头插push_front()

//在头部进行插入
void push_front(int data) {
	Node *newNode = new Node(data);
	newNode->_pre = _head;
	newNode->_next = _head->_next;
	_head->_next->_pre = newNode;
	_head->_next = newNode;

	++_size;
}

尾插

//在尾部进行插入
void push_back(int data) {
	Node *newNode = new Node(data);
	newNode->_pre = _tail->_pre;
	newNode->_next = _tail;
	_tail->_pre->_next = newNode;
	_tail->_pre = newNode;

	++_size;

}

头部删除和尾部删除同理

从头遍历链表

void display() const {
	if (size() > 0) {
		Node *pNode = _head->_next;
		while (pNode && pNode != _tail) {
			cout << pNode->_data << "  ";
			pNode = pNode->_next;
		}
		cout << endl;
	} else {
		cout << "该链表为空,无法打印" << endl;
		return;
	}
}

从尾部开始遍历的链表逻辑相似,把指针定位到尾指针的前一个位置,每次往前移动,遇到头指针停止

判断指定的元素是否存在(查找)

bool find(int data) {
	if (size() > 0) {
		Node *pNode = _head->_next;
		while (pNode && pNode != _tail) {
			if (data == pNode->_data) {
				return true;
			}
			pNode = pNode->_next;
		}

		return false;
	} else {
		cout << "该链表为空,无法查找"  << endl;
		return false;
	}
}

该函数仅用于确定存在性,而不返回元素的具体位置

从任意位置插入元素

//在中间进行插入
void insert(int pos, int data) {
	if (pos < 0 || pos > size()) {
		cout << "该位置异常,无法插入" << endl;
		return;
	}

	Node *pNode = _head;
	while (pos-- > 0) {
		pNode = pNode->_next;
	}

	Node *newNode = new Node(data);
	newNode->_pre = pNode;
	newNode->_next = pNode->_next;
	pNode->_next->_pre = newNode;
	pNode->_next = newNode;

	++_size;
}

先判断插入位置是否合法,若不合法则直接return,若合法则找插入位置,找到插入位置后插入元素

从任意位置删除元素

//在中间进行删除
void erase(int data) {
	bool flag = find(data);
	if (flag) {
		Node *pNode = _head->_next;
		while (pNode && pNode != _tail) {
			if (pNode->_data == data) {
				Node *ptmp = pNode->_pre;
				pNode->_next->_pre = ptmp;
				ptmp->_next = pNode->_next;

				delete pNode;
				pNode = nullptr;
				--_size;

				pNode = ptmp;
			}
			pNode = pNode->_next;
		}
	} else {
		cout << "该元素不在链表之中,无法删除" << endl;
		return;
	}
}

先判断删除的位置是否合法,若不合法则退出,若合法则删除对应元素。

测试效果图:在这里插入图片描述
进一步优化的建议:可以将find函数返回一个具体的数值,用于记录目标元素的位置,根据双链表的特性,判断元素位置和_size/2的大小。若小于可从头开始增删元素,若大于则从尾开始增删元素。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值