数据结构单向链表的C++实现

本文的链表实现使用的是c++的类模板,构造节点类模板Node<T>和链表类模板LinkedList<T>。原因是实用类模板可以适用于各种基本数据类型。以下是类模板及其接口(存放在"LinkedList.h"头文件中):

#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_

#include<iostream>
using namespace std;

//Node类模板的友元类模板声明
template <typename T>
class LinkedList;  //LinkedList作为Node类的友元类用于访问Node类的私有属性。在Node类前面加上LinkedList的声明,让编译器不报错
		
				   //注意:class LinkedList{};     这里的{}不能要。只是声明,不需要{}。

//Node类模板
template <typename T>
class Node
{
public:
	//node *head  = new node();
	Node *d_next; //指针段:指向下一个节点的指针
	T d_data; //数据段:本节点的数据

public:
	Node();//默认构造
	Node(const T &elem);//有参构造
	~Node();//析构函数
	
	//对于Node数据的类型,cout无法直接通过输出node对象来输出数据d_data,所以需要重载<<运算符实现。同时要访问Node节点的私有属性,要声明是友元函数
	template <typename R>
	friend std::ostream& operator<<(std::ostream &,  Node<R> &);  //第二个参数不能用const修饰引用,会报错
};




template <typename T>
class LinkedList 
{
private:
	//头指针
	Node<T>* d_head;  //指针指向的类型为Node<T>

	//节点数
	int d_size;

public:
	//默认构造(空链表)
	LinkedList();

	//析构
	~LinkedList();

	//尾端插入节点
	void push_back(const T &val);


	//打印链表
	template <typename R>
	friend std::ostream & operator<<(std::ostream &out, LinkedList<R> &list);
	
	//获取链表长度
	int size()
	{
		return d_size;
	}

	//头部插入节点
	void push_head(const T &val);

	//在指定位置插入节点
	void insert(const T &val,int pos);

	//删除所有数据
	void DeleteAll();

	//查找某元素并返回其节点地址
	Node<T> * Find(const T &val);
	//按元素删除节点
	void DeleteElem(const T &val);

	//尾部删除
	void DeleteLast();

	//第一个节点删除
	void DeleteFirst();

	//链表反转
	void Reserve();
};

#endif

下面是类模板接口的实现(存放在linkedList.cpp文件中):

#include<iostream>
#include "LinkedList.h"
//Node无参构造
template <class T>
Node<T>::Node() : d_data(), d_next(nullptr) {} //参数列表法实现默认构造,数据赋值为空,指向下一个节点的指针赋空指针

//Node有参构造
template <class T>
Node<T>::Node(const T &elem)
{
	this->d_data = elem;
	this->d_next = nullptr;
}

//Node析构函数
template <class T>
Node<T>::~Node(){}


//Node类模板中重载运算度算
template <typename R>
std::ostream& operator<<(std::ostream &out,  Node<R> &nod) //重载运算度算是一个友元的全局函数,不需要加作用域。第二个参数不能加const
{
	out << nod.elem();
	return out;
}

//LinkedList类模板的默认构造
template <typename T>
LinkedList<T>::LinkedList()  //默认构造长度为0的链表
{
	this->d_head = new Node<T>();//开辟头节点。头结点啥也不存,也不一定必须有,不计入链表长度,但我这里假设有头结点。
	this->d_head->d_data = NULL;//该节点的数据初值为NULL
	this->d_head->d_next = nullptr;//末指针设为空
	this->d_size = 0;

}   

//LinkedList类模板析构函数
template <typename T>
LinkedList<T>::~LinkedList()
{

	if (this->size()==0)
	{
		delete this->d_head;
	}
	else
	{
		Node<T> *tmp=nullptr;
		Node<T> *cur = this->d_head->d_next;//指向第一个节点
		while (cur->d_next != nullptr)//不是尾节点时
		{
			tmp = cur;
			cur = cur->d_next;//光标节点指向下一个节点
			delete tmp;//释放当前节点
		}
		delete cur;//释放尾节点
		delete this->d_head;//释放头结点
	}

}

template <typename T>
void LinkedList<T>::push_back(const T &val)//尾插
{
	Node<T> * tmp = new Node<T>(); //Node的有参构造
	tmp->d_data = val;
	tmp->d_next = nullptr;

	Node<T> *cur = this->d_head;//设置光标指针

	if (cur->d_next == nullptr)//如果只有头结点
	{
		cur->d_next = tmp;//头结点直接连接插入数据所创建的新节点
	}
	else 
	{
		while (cur->d_next != nullptr)//如果有其他节点
		{
			cur = cur->d_next;//更新光标开始遍历所有非尾节点
		}
		cur->d_next = tmp;//尾节点连接上插入数据的节点
	}
	d_size++; //节点个数加1
}

template <typename T>
void LinkedList<T>::push_head(const T &val)//头插
{
	Node<T> * tmp = new Node<T>(); //Node的有参构造
	tmp->d_data = val;
	tmp->d_next = nullptr;

	Node<T> *cur = this->d_head;	
	if (cur->d_next == nullptr)//如果只有头结点
	{
		cur->d_next = tmp;
	}
	else
	{
		tmp->d_next = this->d_head->d_next;//插入节点链接原来的第一个节点
		this->d_head->d_next = tmp;//头结点链接插入的节点
	}

	d_size++; //节点个数加1
}


template <typename T>
void LinkedList<T>::insert(const T &val, int pos)  //pos位置插入val
{
	Node<T> *tmp = new Node<T>();
	tmp->d_data = val;
	tmp->d_next = nullptr;
	if (pos == 0||this->d_size==0)//约定链表下标从0开始算
	{
		this->push_head(val);
		this->d_size++;
	}
	else if (pos == this->d_size)//尾插
	{
		this->push_back(val);
		this->d_size++;
	}
	else if (pos > 0 && pos < this->d_size)
	{
		Node<T> *cur = this->d_head;
		for (int i = 0; i < pos; i++)
		{
			cur = cur->d_next;
		}
		tmp->d_next = cur->d_next;
		cur->d_next = tmp;
		this->d_size++;
	}
	else
	{
		cout << "输入的位置有误!!!" << endl;
	}
}

template <typename T>
void LinkedList<T>::DeleteAll()//清空除了头结点外所有的节点
{
	if (this->d_size==0) //如果
	{
		cout << "链表已是空链表" << endl;			
	}
	else
	{
		Node<T>* cur = this->d_head->d_next;
		Node<T>* tmp = nullptr;
		while (cur->d_next != nullptr)//释放尾节点前的节点
		{
			tmp = cur;
			cur = cur->d_next;
			delete tmp;
		}
		delete cur;//释放尾节点
		this->d_head->d_next = nullptr;//空链表,头结点的指针为nullptr
		this->d_size = 0;//长度置0
	}
}

template <typename R>
std::ostream & operator<<(std::ostream &out, LinkedList<R> &list)//友元函数,重载<<运算符用于输出链表
{

	if (list.d_size==0)
	{
		out << "链表为空"<<endl;
	}
	else
	{
		Node<R> * cur = list.d_head->d_next;//光标处于第一个节点
		while (cur->d_next != nullptr)
		{

			out << cur->d_data << "-->";//输出光标所在节点的数据
			cur = cur->d_next;//光标更新,指向下一节点
		}
		out<<cur->d_data << "-->";//输出最后节点的数据
	}


	out << "&"<<endl;//输出结尾指向的空,用&表示
	return out;
}


template <typename T>
Node<T>* LinkedList<T>::Find(const T &val)
{
	if (this->d_size == 0)
	{
		cout << "空链表!!" << endl; 
		return nullptr;
	}
	else
	{
		Node<T>* cur = this->d_head->d_next;//光标处于第一个节点
		while (cur !=nullptr)//还没遍历所有节点(若最后一个节点也没找到,则cur更新后给nullptr,退出循环)
								//while (cur !=nullptr)可以遍历所有节点
			                    //while (cur->d_next != nullptr)会在最后一个节点处退出循环
		{
			if (cur->d_data == val)
			{
				cout << "找到了" << val << endl;
				return cur;
			}
			cur = cur->d_next;
		}
		cout << "未找到" << val << endl;
		return nullptr;//未找到

	}

}

template <typename T>
void LinkedList<T>::DeleteElem(const T &val)//按值删除节点
{
	Node<T>* tmp = this->Find(val);
	if (tmp == nullptr)
	{
		cout << "不存在" << val << ",无法删除!" << endl;
	}
	else
	{
		Node<T>* cur = this->d_head;

		while (cur->d_next != tmp)//寻找元素所在的节点
		{
			cur  = cur->d_next;
		}
		cur->d_next = tmp->d_next;
		delete tmp;
		tmp = nullptr;
		this->d_size--;
	}

}

template <typename T>
void LinkedList<T>::DeleteLast()//尾删
{
	if (this->d_head->d_next == nullptr)
	{
		cout << "空链表,无法删除最后一个元素" << endl;
	}
	Node<T>* cur = this->d_head->d_next;
	Node<T>* tmp = nullptr;

	while (cur->d_next != nullptr)//不是最后一个节点
	{
		tmp = cur;
		cur = cur->d_next;
	}
	
	//到最后一个节点,tmp指向第二个节点
	delete cur;
	tmp->d_next = nullptr; //倒数第二个节点变为尾节点,其d_next为nullptr
	this->d_size--;
}

template <typename T>
void LinkedList<T>::DeleteFirst()
{
	if (this->d_size==0)
	{
		cout << "空链表,无法删除第一个元素" << endl;
	}
	else
	{
		Node<T>* tmp = this->d_head->d_next;//光标处于第一个节点
		this->d_head->d_next = tmp->d_next;
		delete tmp;
		tmp = nullptr;
		this->d_size--;
	}
}


template <typename T>
void LinkedList<T>::Reserve()
{
	if (this->d_size == 0)
	{
		cout << "链表为空,不需要反转" << endl;
	}
	else if (this->d_size == 1)
	{
		cout << "链表长度为1,不需要反转" << endl;
	}
	else
	{
		Node<T>* pre;
		Node<T>* cur;
		Node<T>* latter;
		pre = nullptr;//pre在链表末尾的nullptr位置
		cur = this->d_head->d_next;//cur指向第一个节点
		latter = cur->d_next;//latter指向第二个节点
		while (latter->d_next != nullptr)
		{
			cur->d_next = pre;
			pre = cur;
			cur = latter;
			latter = latter->d_next;
		}
		latter->d_next = cur;
		cur->d_next = pre;
		this->d_head->d_next = latter;

	}


}


void test02()
{
	LinkedList<int> List1; //默认构造
	cout << List1.size() << endl;
	cout << List1 << endl;
	//测试push_back
	int num = 2;
	List1.push_back(num);
	List1.push_back(3);
	cout << List1 << endl;

	List1.push_head(0);
	List1.push_head(-1);
	cout << List1 << endl;

	List1.insert(-2,4);
	cout << List1 << endl;

	List1.DeleteAll();
	cout << List1 << endl;
	List1.DeleteAll();

	List1.Find(2);
	List1.push_head(0);
	List1.push_head(1);
	List1.push_head(2);
	List1.push_head(4);
	List1.push_head(10);
	cout << List1 << endl;
	List1.Find(2);
	List1.Find(100);
	List1.Find(0);

	cout << List1 << endl;
	List1.DeleteElem(3);
	List1.DeleteElem(0);
	cout << List1 << endl;

	List1.DeleteLast();
	cout << List1 << endl;
	List1.DeleteLast();
	cout << List1 << endl;

	List1.DeleteFirst();
	cout << List1 << endl;
	List1.DeleteFirst();
	cout << List1 << endl;

	List1.DeleteFirst();
	cout << List1 << endl;

	List1.push_back(7);
	List1.push_back(8);
	List1.push_back(9); 
	List1.push_back(10);
	cout << List1 << endl;
	List1.Reserve();//链表反转
	cout << List1 << endl;


}

using namespace std;
int main()
{
	test02();
	return 0;
}

对于链表反转,思路参考下图

 具体代码:

template <typename T>
void LinkedList<T>::Reserve()
{
	if (this->d_size == 0)
	{
		cout << "链表为空,不需要反转" << endl;
	}
	else if (this->d_size == 1)
	{
		cout << "链表长度为1,不需要反转" << endl;
	}
	else
	{
		Node<T>* pre;
		Node<T>* cur;
		Node<T>* latter;
		pre = nullptr;//pre在链表末尾的nullptr位置
		cur = this->d_head->d_next;//cur指向第一个节点
		latter = cur->d_next;//latter指向第二个节点
		while (latter->d_next != nullptr)
		{
			cur->d_next = pre;
			pre = cur;
			cur = latter;
			latter = latter->d_next;
		}
		latter->d_next = cur;
		cur->d_next = pre;
		this->d_head->d_next = latter;

	}

}

程序运行结果:

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
双向链表是一种常见的数据结构,与单向链表相比,它每个节点都有两个指针,分别指向前驱节点和后继节点。这样,双向链表可以实现双向遍历,插入和删除操作也更加方便。 以下是用C语言实现双向链表的代码: ```c #include <stdio.h> #include <stdlib.h> // 双向链表节点结构体 typedef struct ListNode { int val; struct ListNode *prev; struct ListNode *next; } ListNode; // 创建双向链表节点 ListNode *createNode(int val) { ListNode *node = (ListNode *)malloc(sizeof(ListNode)); node->val = val; node->prev = NULL; node->next = NULL; return node; } // 插入节点到双向链表头部 ListNode *insertAtHead(ListNode *head, int val) { ListNode *node = createNode(val); if (head == NULL) { head = node; } else { node->next = head; head->prev = node; head = node; } return head; } // 插入节点到双向链表尾部 ListNode *insertAtTail(ListNode *head, int val) { ListNode *node = createNode(val); if (head == NULL) { head = node; } else { ListNode *cur = head; while (cur->next != NULL) { cur = cur->next; } cur->next = node; node->prev = cur; } return head; } // 删除双向链表中的节点 ListNode *deleteNode(ListNode *head, int val) { if (head == NULL) { return NULL; } ListNode *cur = head; while (cur != NULL && cur->val != val) { cur = cur->next; } if (cur == NULL) { return head; } if (cur == head) { head = head->next; if (head != NULL) { head->prev = NULL; } } else { cur->prev->next = cur->next; if (cur->next != NULL) { cur->next->prev = cur->prev; } } free(cur); return head; } // 打印双向链表 void printList(ListNode *head) { while (head != NULL) { printf("%d ", head->val); head = head->next; } printf("\n"); } // 主函数 int main() { ListNode *head = NULL; head = insertAtHead(head, 1); head = insertAtHead(head, 2); head = insertAtTail(head, 3); printList(head); // 2 1 3 head = deleteNode(head, 2); printList(head); // 1 3 head = deleteNode(head, 3); printList(head); // 1 head = deleteNode(head, 1); printList(head); // return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值