【c++版数据结构】之单链表的实现(带头结点以及尾节点)

所实现的单链表的结构如下图所示:


头文件:SList.h

#include<iostream>
#include<cassert>
using namespace std;

typedef enum{FALSE,TRUE}Status;

template<class Type>
class List;

template<class Type>
class ListNode
{
	friend class List<Type>;   //友元类可以访问该类的成员
private:
	Type data;
	ListNode *next;
public:
	ListNode() :data(Type()), next(NULL){}
	ListNode(Type d, ListNode<Type> *n = NULL) :data(d), next(n){}
	void SetData(Type d){ data = d; }
	Type GetData()const{ return data; }
	~ListNode(){}
};

template<class Type>
class List
{
private:
	ListNode<Type> *first; //单链表的头指针
	ListNode<Type> *last;  //单链表的尾指针
	size_t         size;   //单链表的长度
protected:
	/*ListNode<Type> * _BuyNode(const Type &x,ListNode<Type> *_Narg = NULL)//--->有待解决
	{
		ListNode<Type> *s = new ListNode<Type>(x,_Narg);
	}*/
	ListNode<Type> * _BuyNode(const Type &x)
	{
		ListNode<Type> *s = new ListNode<Type>(x);
		assert(s != NULL);
		return s;
	}
public:
	List()
	{	
		first = last = _BuyNode(0);
		size = 0;
	}
	~List()
	{
		destory();
	}
	Status push_back(const Type &x)
	{
		ListNode<Type> *s = _BuyNode(x);
		last->next = s;
		last = s;
		size++;
		return TRUE;
	}
	void show_list()
	{
		ListNode<Type> *s = first->next;
		while (s != NULL)
		{
			cout << s->data << "->";
			s = s->next;
		}
		cout << "NULL" << endl;
	}
	Status push_front(const Type &x)
	{
		ListNode<Type> *s = _BuyNode(x);
		s->next = first->next;
		first->next = s;
		if (size == 0)
			last = s;
		size++;
		return TRUE;
	}
	Status pop_back()
	{
		if (first == last)//size == 0
		{
			cout << "单链表已空,无法尾删" << endl;
			return FALSE;
		}
		ListNode<Type> *s = first;
		while (s->next != last)
		{
			s = s->next;
		}
		delete last;
		s->next = NULL;
		last = s;
		size--;
		return TRUE;
	}
	Status pop_front()
	{
		if (first == last)//size == 0
		{
			cout << "单链表已空,无法头删" << endl;
			return FALSE;
		}
		ListNode<Type> *s = first->next;
		first->next = s->next;
		delete s;
		if (size == 1)
			last = first;
		size--;
		return TRUE;
	}
	ListNode<Type>* find(const Type &x) //返回所查元素的地址:麻烦于(delete_val,next,prio)
	{
		ListNode<Type> *s = first->next;
		while (s != NULL && s->data != x)
		{
			s = s->next;
		}
		return s;
	}
	/*ListNode<Type>* find(const Type &x)//返回所查元素的前一个地址
	{
		ListNode<Type> *s = first;
		while (s->next != NULL &&s->next->data != x)
		{
			s = s->next;
		}
		if (s->next == NULL)
			return NULL:
		else
			return s;
	}*/

	方法一
	//Status delete_val(const Type &x)//常规方法
	//{
	//	ListNode<Type> *s = find(x);
	//	if (s == NULL)
	//	{
	//		cout << "该元素不存在,无法删除" << endl;
	//		return FALSE;
	//	}	
	//	if (s == first->next)
	//		pop_front();
	//	else if (s == last)
	//		pop_back();
	//	else
	//	{
	//		ListNode<Type> *p = first;
	//		while (p->next != s)     //寻找所查元素的前一个节点
	//		{
	//			p = p->next;
	//		}
	//		p->next = s->next;
	//		delete s;
	//		size--;
	//	}
	//	return TRUE;
	//}
	//方法二
	Status delete_val(const Type &x)
	{
		ListNode<Type> *s = find(x);
		if (s == NULL)
		{
			cout << "该元素不存在,无法删除" << endl;
			return FALSE;
		}
		if (s == last)
			pop_back();//--------------->尾节点后面没有节点,无法用后面节点的值覆盖它,所以单独定义
		else
		{
			ListNode<Type> *p = s->next;
			s->data = p->data;//后面节点的值覆盖它的值
			s->next = p->next;//将后面的节点删除
			delete p;
			size--;
		}
		return TRUE;

	}

	ListNode<Type>* next(const Type &x)
	{
		ListNode<Type> *s = find(x);
		if (s == NULL)
		{
			cout << "该元素不存在" << endl;
			return NULL;
		}
		if (s == last)
		{
			cout << "该元素没有后继" << endl;
			return NULL;
		}
		else
		{
			return s->next;
		}
	}
	ListNode<Type>* prio(const Type &x)
	{
		ListNode<Type> *s = find(x);
		if (s == NULL)
		{
			cout << "该元素不存在" << endl;
			return NULL;
		}
		if (s == first->next)
		{
			cout << "该元素没有前驱" << endl;
			return NULL;
		}
		else
		{
			ListNode<Type> *p = first;
			while (p->next != s)
			{
				p = p->next;
			}
			return p;
		}
	}
	//单链表逆置:将链表从第一个元素处断开(第一个节点成为最后一个节点)
	//将链表之后节点的值,依次进行头插(值头插之后记得删除节点)
	void reserve()
	{
		if (size == 0 || size == 1)
			return;
		ListNode<Type> *s = first->next;
		ListNode<Type> *p = s->next;
		last = s;
		last->next = NULL;//第一个节点成为最后一个节点

		while (p != NULL)
		{
			s = p;
			p = p->next;
			//push_front(s->data);
			//delete s;
			s->next = first->next;
			first->next = s;
		}
	}
	//按值插入:先排好序,然后寻找要插入元素的前一个位置,在该位置之后插入该元素
	Status insert_val(const Type &x)
	{
		sort();
		ListNode<Type> *p = _BuyNode(x);
		ListNode<Type> *s = first;
		while (s->next != NULL && s->next->data < p->data)//找到所插元素的前一个节点
		{
			s = s->next;
		}
		if (s->next == NULL)//这时候,所插元素是最大的,尾插即可(未找到要插入的前一个位置)
			push_back(x);
		else
		{
			p->next = s->next;
			s->next = p;
			size++;
		}
		return TRUE;
	}
	//排序思想:将链表从第一个节点处断开(第一个成为最后一个节点)
	//将之后的节点依次按值进行插入(注意值插入之后,节点要释放)
	void sort()
	{
		if (size == 0 || size == 1)
			return;
		ListNode<Type> *s = first->next;
		ListNode<Type> *p = s->next;
		last = s;  //------->第一个节点成为最后一个节点
		last->next = NULL;
		while (p != NULL) //------->断开之后依次按值进行插入
		{
			s = p;              //保存要插入的节点
			p = p->next;        //为下一次插入做准备
			insert_val(s->data);//进行插入
			delete s;           //释放节点,防止内存泄露
		}
	}
	size_t lenth()
	{
		return size;
	}
/*
	void clear()
	{
		ListNode<Type> *s = first;
		ListNode<Type> *p;
		while (size--)            //p始终指向第一个节点(总是删除第一个节点,删除size次)
		{
			p = s->next;
			s->next = p->next;
			delete p;
		}
		size = 0;
		last = fisrt;
	}
*/
	void clear()
	{
		ListNode<Type> *s = first->next;//p始终指向第一个节点(总是删除第一个节点,删除size次)
		while (s != NULL)
		{
			first->next = s->next;
			delete s;
			s = first->next;
		}
		size = 0;
		last = first;
	}

	void destory()
	{
		clear();
		delete first;
		first = last = NULL;
	}
};

测试文件:main.cpp

#include"SList.h"
int main()
{
	List<int> mylist;
	int item;
	int n;
	int select = 1;
	ListNode<int> *p;
	while (select)
	{
		cout << "*************************************** *" << endl;
		cout << "*[1] push_back           [2] push_front *" << endl;
		cout << "*[3] show_list           [4] pop_back   *" << endl;
		cout << "*[5] pop_front           [6] insert_val *" << endl;
		cout << "*[7] lenth               [8] find       *" << endl;
		cout << "*[9] merge               [10] delete_val*" << endl;
		cout << "*[11] sort               [12] reserve   *" << endl;
		cout << "*[13] next               [14] clear     *" << endl;
		cout << "*[15] prio               [0] quit_system*" << endl;
		cout << "请选择:>";
		cin >> select;
		switch (select)
		{
		case 1:
			cout << "请输入要插入的元素(-1结束):>";
			while (cin >> item, item != -1)
			{
				mylist.push_back(item);
			}
			break;
		case 2:
			cout << "请输入要插入的元素(-1结束):>";
			while (cin >> item, item != -1)
			{
				mylist.push_front(item);
			}
			break;
		case 3:
			mylist.show_list();
			break;
		case 4:
			mylist.pop_back();
			break;
		case 5:
			mylist.pop_front();
			break;
		case 6:
			cout << "请输入要插入的元素:";
			cin >> item;
			mylist.insert_val(item);
			break;
		case 7:
			cout << "长度为:" << mylist.lenth() << endl;
			break;
		case 8:
			cout << "请输入要查找的元素:";
			cin >> item;
			if (mylist.find(item))
				cout << "it's found" << endl;
			else
				cout << "it's not exist" << endl;
			break;
		case 9:
			cout << "请输入要删除的位置:";
			cin >> n;
			//mylist.delete_pos(n,item);
			break;
		case 10:
			cout << "请输入要删除的元素:";
			cin >> item;
			mylist.delete_val(item);
			break;
		case 11:
			mylist.sort();
			break;
		case 12:
			mylist.reserve();
			break;
		case 13:
			cout << "请输入要查找后继的元素:";
			cin >> item;
			p = mylist.next(item);
			if (p != NULL)
				cout << p->GetData() << endl;
			break;
		case 14:
			mylist.clear();
			break;
		case 15:
			cout << "请输入要查找前驱的元素:";
			cin >> item;
			p = mylist.prio(item);
			if (p != NULL)
				cout << p->GetData() << endl;
			break;
		default:
			break;
		}
	}
	system("pause"); 
	return 0;
}

总结:

其中难点也是最有意思的地方在于:排序(sort)以及单链表逆置(reverse)的实现

二者的实现具有异曲同工之妙;

排序:将原来的链表从第一个节点处断开,一分为二,第一个节点以及头结点成为一个单独的链表,接着将剩余的节点依次按值进行插入

链表逆置:将原来的链表从第一个节点处断开,一分为二,第一个节点以及头结点成为一个单独的链表,接着将剩余的节点依次进行头插


头插和头删有注意点:

push_front():当插入的是第一个节点时  (尾指针要指向该节点)

pop_front ():当删除的是最后一个节点时(尾指针要指向头结点)


find()函数:

返回所查元素的地址:(按值删除-->谷歌面试题)

1:重新找一遍,定位到前一个元素的位置,然后删除所要删除的元素

2:将所要删除元素后面的元素p覆盖要删除的元素,然后将p删除


返回所查元素的前一个元素的地址(按值删除delete_val()时很简单)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值