【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()时很简单)

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-06 20:37:46

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

C实现头插法和尾插法来构建单链表(不带头结点)

链表的构建事实上也就是不断插入节点的过程.而节点的插入能够分为头插法和尾插法. 头插法就是在头结点后插入该节点,始终把该节点作为第一个节点.尾插法就是在链表的最后一个节点处插入元素,作为最后一个节点.假设想要了解链表的概念和其它链表操作.请參考<数据结构与算法之链表><C语言实现链表的基本操作>两篇文章.演示样例代码上传至  https://github.com/chenyufeng1991/HeadInsertAndTailInsert . // // main.c // Hea

C语言实现单链表(不带头结点)节点的插入

对单链表进行增删改查是最主要的操作.我在上一篇博客<C语言实现链表节点的删除>实现了删除单链表中的某个节点. 这里我们要来实如今某个位置插入节点.演示样例代码上传至https://github.com/chenyufeng1991/InsertList  . 核心代码例如以下: Node *InsertToPosition(Node *pNode,int pos,int x){ if (pos < 0 || pos > sizeList(pNode) ) { printf(&quo

单链表(不带头结点)

#include <stdio.h> #include <stdlib.h> typedef int ElemType; typedef struct Node { ElemType data; struct Node *next; }LNode,*LinkList; void InitList(LinkList &l); void Create_head(LinkList &l); void Create_rear(LinkList &l); void O

*循环单链表(不带头结点)

记忆精简:不带头结点,需要创建n个结点,包括三个指针,头指针head,一个游标指针p和创建结点的s.... void creat(List &L,int n) { int e; List s,p; L=NULL; for(int i=1;i<=n;i++)/*n个结点*/ { s=(List)malloc(sizeof(Node)); cin>>e; s->data=e; if(L==NULL) L=s; else p->next=s; p=s; } p->ne

单链表(c++带头结点,)

<pre name="code" class="cpp">#ifndef _SEQLIST_ #define _SEQLIST_ #include<iostream> using namespace std; #include<assert.h> template<class Type> class Slist; //节点类 template<class Type> class Node { public:

C#数据结构-单链表

理论基础: 链表是用一组任意的存储单元来存储线性表中的数据元素. 如果结点的引用域只存储该结点直接后继结点的存储地址,则该链表叫单链表(Singly Linked List). 单链表由头引用H唯一确定.头引用指向单链表的第一个结点,也就是把单链表第一个结点的地址放在H中. C#实现: 1接口 引用线性表的接口IListDS<T> 2实现 首先,必须定义一个单链表的节点类.  1 public class Node<T> 2    { 3        private T data

java数据结构:单链表常见操作代码实现

一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashMap等集合的底层结构都是链表结构.链表以结点作为存储单元,这些存储单元可以是不连续的.每个结点由两部分组成:存储的数值+前序结点和后序结点的指针.即有前序结点的指针又有后序结点的指针的链表称为双向链表,只包含后续指针的链表为单链表,本文总结的均为单链表的操作. 单链表结构: Java中单链表采用No

数据结构 C++ 单链表 一元多项式的相加

#include <iostream> using namespace std; struct Node { double coe; //系数 int exp; //指数 Node *next; }; void CreatPoly(Node *&head, int n) // 生成带表头结点的单链表,除头结点外另生成n个结点 { head = (Node *)new Node; head->coe = 0; head->exp = 0; head->next = NU

009实现一个算法来删除单链表中的一个结点,只给出指向那个结点的指针(keep it up)

呵呵,这个题不能直接删除已知的结点,因为是单链表,不知道前驱,只知道 后继结点,直接删除会使链表断开.不过我们可以删除已知结点的后继结点, 把后继结点的值赋值给已知结点. #include <iostream> struct Node { int data; Node* next; }; bool removeNode(Node* vNode) { if (vNode == NULL || vNode->next == NULL) return false; Node* pNext =

【LeetCode-面试算法经典-Java实现】【025-Reverse Nodes in k-Group(单链表中k个结点一组进行反转)】

[025-Reverse Nodes in k-Group(单链表中k个结点一组进行反转)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 原题 Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. If the number of nodes is not a multiple of k then left-out nodes i