大话数据结构 —— 双向链表 C#、C++代码实现

 


双向列表


 双向链表是每个结点除后继指针外还有一个前驱指针。和单链表类同,双向链表也有带头结点结构和不带头结点结构两种,带头结点的双向链表更为常用;另外,双向链表也可以有循环和非循环两种结构,循环结构的双向链表更为常用。

 双向链表相当于两个单向循环链表,双链表节点结构如下图示:

大话数据结构 —— 双向链表 C#、C++代码实现_第1张图片

 

大话数据结构 —— 双向链表 C#、C++代码实现_第2张图片

双链表节点比单链表多一个前驱指针域。

如下图是带头结点的双向循环链表的图示结构。双向循环链表的next和prior各自构成自己的单向循环链表:

大话数据结构 —— 双向链表 C#、C++代码实现_第3张图片

 

在双向链表中,有如下关系:设对象引用p表示双向链表中的第i个结点,则p->next表示第i+1个结点,p->next->prior仍表示第i个结点,即p->next->prior == p;同样地,p->prior表示第i-1个结点,p->prior->next仍表示第i个结点,即p->prior->next == p。下图是双向链表上述关系的图示:

大话数据结构 —— 双向链表 C#、C++代码实现_第4张图片

双向循环链表的插入过程:

  下图中的指针p表示要插入结点的位置,s表示要插入的结点,①、②、③、④表示实现插入过程的步骤:

大话数据结构 —— 双向链表 C#、C++代码实现_第5张图片

s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;

循环双向链表的删除过程:

  下图中的指针p表示要插入结点的位置,①、②表示实现删除过程的步骤:

大话数据结构 —— 双向链表 C#、C++代码实现_第6张图片

p->prior->next = p->next;
p->next->prior = p->prior;

下面用C++ 代码实现双向链表:

头文件:

#include
using namespace std;
#ifndef TT_DU_LINK_LIST
#define TT_DU_LINK_LIST
#define TT_OK 1
#define TT_ERROR 0
namespace tt

{
	class  DuLinkList
	{
	public:
		typedef  int ElemType;
		typedef void Status;
	public:
		struct DulNode
		{
			ElemType m_data;  //数据域
			DulNode *m_prior;   //指向前驱结点的指针
			DulNode *m_next;    //指向后继结点的指针
		};
	public:
		DuLinkList();
		~DuLinkList();
		ElemType insertAt(ElemType i, ElemType elem);
		ElemType removeAt(ElemType i, ElemType &elemOut);
		ElemType getAt(ElemType i, ElemType &elemOut);
		ElemType getIndexElemAt(ElemType &i, ElemType elemOut);//查找与elem相等的元素,返回第一个与elem相等元素在链表中的序号
		ElemType getLength()const;
		ElemType isEmpty()const;
		ElemType destroy();
		ElemType clear();
		Status show();
		Status traverseBack();
		ElemType priorElemAt(ElemType cur_e, ElemType &pri_e);//若cur_e是链表的数据元素,且不是第一个,则用pre_e返回它的前驱
		ElemType nextElemAt(ElemType cur_e, ElemType &Nex_e); //若cur_e是链表的数据元素,且不是最后一个,则用next_e返回它的后继,
		Status createTail(ElemType *datas, ElemType length);//创建长度为length的链表,数据通过数组指定,这里采用尾插法
	private:
		DulNode *m_heap;
	};
	inline DuLinkList::ElemType DuLinkList::isEmpty()const
	{
		return ((m_heap->m_next == m_heap) && (m_heap->m_prior == m_heap));
	}
}
#endif  //TT_DU_LINK_LIST

源文件:

#include"MyDuLinkList.h"

namespace tt
{
	DuLinkList::DuLinkList()
	{
		m_heap = new DulNode;
		if (!m_heap)
		{
			cout << "错误,内存分配失败!" << endl;
			system("pause");
			exit(0);
		}
		/*m_heap->m_next = m_heap;
		m_heap->m_prior = m_heap;*/
		m_heap->m_next = m_heap->m_prior = m_heap;
		cout << "\n********************双向的循环链表初始化成功!************************" << endl;
	}
	DuLinkList::~DuLinkList()
	{
		this->destroy();

	}

	DuLinkList::Status DuLinkList::createTail(ElemType *datas, ElemType length)//创建长度为length的链表,数据通过数组指定,这里采用尾插法
	{
		DulNode *m = m_heap;
		for (int i = 0; i < length; ++i)
		{
			DulNode *p = new DulNode;
			if (!p)
			{
				cout << "错误,无法分配内存,创建链表失败!" << endl;
				system("pause");
				exit(0);
			}
			p->m_data = datas[i];
			m->m_next = p;   //首先建立第一个节点的时候,头结点的后继节点是p
			p->m_prior = m;   //p的前驱节点是头结点
			p->m_next = m_heap;  //然后第一个节点的后继变成头结点
			m_heap->m_prior = p;  //那么头结点的前驱变成p,   这样就会形成一个首尾相接的双向循环链表
			m = p;  //最后把第一个节点变成m,  就这样按以上步骤循环
		}
	
	}
	DuLinkList::ElemType DuLinkList::insertAt(ElemType i, ElemType elem)
	{
	
		if ((i < 1) || (i > getLength() + 1))
		{
			return TT_ERROR;
		}
		DulNode* p = m_heap;  //p指向头结点
		int j = 1;
		while (j < i)
		{
			p = p->m_next;
			++j;
		}
		DulNode *s = new DulNode;
		s->m_data = elem;     //把要存储的数据放入数据域中
		s->m_prior = p;         //首先把p变成s的前驱结点
		s->m_next = p->m_next;   //在p的后继节点变成s的后继节点
		p->m_next->m_prior = s;   //把s变成p的后继节点的前驱
		p->m_next = s;   //p的后继节点是s

		return TT_OK;
	}
	DuLinkList::ElemType DuLinkList::removeAt(ElemType i, ElemType &elemOut)
	{
	
		if ((i < 1) || (i >getLength()))
		{
			return TT_ERROR;
		}
		DulNode* p = m_heap;  //p指向头结点
		int j = 1;
		while (j <= i)
		{
			p = p->m_next;
			j++;
		}
		elemOut = p->m_data;
		p->m_prior->m_next = p->m_next;  //把p的后继节点变成p的前驱结点的后继节点
		p->m_next->m_prior = p->m_prior;   //把p的前驱结点变成p的后继节点的前驱结点
		delete p;  //释放节点的内存
		return TT_OK;
	}
	DuLinkList::ElemType DuLinkList::getAt(ElemType i, ElemType &elemOut)
	{
	
		if (i > getLength())  // 判断i的位置是否超过链表的长度
		{
			return TT_ERROR;
		}
		DulNode* q = m_heap->m_next;    //让q指向链表的第一个节点
		int j = 1;
		while ((q != m_heap) && (j < i))  //如果q的后继不是m_heap,  当前位置j=1小于要查找的位置,循环继续
		{                               //删除第一个元素时,退出循环

			q = q->m_next;
			++j;
		}
		if (!(q != m_heap) || (j > i))   //删除第一个元素时,  j并没有比i大, q的后继不是m_heap取逆,  直接退出循环
		{
			return TT_ERROR;
		}
		elemOut = q->m_data;
		return TT_OK;
	}
	DuLinkList::ElemType DuLinkList::getIndexElemAt(ElemType &i, ElemType elemOut)  //查找与elem相等的元素,返回第一个与elem相等元素在链表中的序号
	{
		DulNode *p = m_heap->m_next;    //从第一个结点开始遍历
		int searchLocate = 1;
		while ((p != m_heap) && (p->m_data != elemOut)) //p未到表头时,  p的指针域不等于要查找的元素
		{
			++searchLocate;
			p = p->m_next;
		}
		if (!(p != m_heap))
		{
			return TT_ERROR;
		}
		i = searchLocate;   //返回要查找的元素的位置的序号
		return TT_OK;
	}

	DuLinkList::ElemType DuLinkList::getLength()const
	{
		DulNode *p = m_heap->m_next;   //p指向第一个结点
		int i = 0;
		while (p != m_heap) //p未到表头时
		{
			++i;
			p = p->m_next;

		}
		return i;
	}

	DuLinkList::ElemType DuLinkList::destroy()
	{
		DulNode *p = m_heap->m_next; //p指向第一个结点
		while (p != m_heap)   //p没到表头
		{
			DulNode *q = p->m_next;  //第一次循环,把第二个元素变成q
			delete p;  //第一次循环,  释放第一个元素
			p = q;   //第一次循环,  把第二个元素变成p,  那么现在p就是第一个元素
		}
		delete m_heap;  //所有元素删除完后,  因为上面是从第一个节点开始删除的,  头结点没有删除,  所有这个要删除头结点
		m_heap = nullptr;
		return TT_OK;
	}
	DuLinkList::ElemType DuLinkList::clear()
	{
		DulNode *p = m_heap->m_next; //p指向第一个结点
		while (p != m_heap) //p没到表头
		{
			DulNode *q = p->m_next;
			delete p;
			p = q;
		}
		m_heap->m_next = m_heap->m_prior = m_heap; //头结点的两个指针域均指向自身
		return TT_OK;
	}

	DuLinkList::Status DuLinkList::show()
	{
		if (isEmpty())
		{
			cout << "当前的链表没有元素,无法显示!" << endl;
		}
		else
		{
			DulNode *p = m_heap->m_next;   //p指向第一个结点
			int temp(0);  //用来显示链表中的个数
			cout << "输出双向循环链表中的所有元素:";
			while (p != m_heap)  //p未到表头
			{
				++temp;
				cout << (p->m_data) << " ";
				p = p->m_next;
			}
			cout << "\n当前双向循环链表中的元数个数为:" << temp << endl;
		}
	}
	DuLinkList::Status DuLinkList::traverseBack()  //逆序输出链表的所有元素
	{
		if (isEmpty())
		{
			cout << "当前的链表没有元素,无法显示!" << endl;
		}
		else
		{
			DulNode *m = m_heap->m_prior;  //把m指向最后一个元素
			cout << "逆序输出双向链表中的所有元素:";
			while (m != m_heap)
			{
				cout << (m->m_data) << " ";
				m = m->m_prior;   //输出元素后,m向前前进
			}
			cout << endl;
		}

	}
	DuLinkList::ElemType DuLinkList::priorElemAt(ElemType cur_e, ElemType &pri_e)//若cur_e是链表的数据元素,且不是第一个,则用pre_e返回它的前驱
	{
		DulNode *p = m_heap->m_next->m_next;  //p指向第二个元素
		while (p != m_heap)
		{
			if (p->m_data == cur_e)  //p指向结点的数据与cur_e值进行比较判断
			{
				pri_e = p->m_prior->m_data;  //将p的前驱结点的值赋给pre_e,pre_e保存返回
				return TT_OK;;
			}
			p = p->m_next;
		}
		return TT_ERROR;
	}
	DuLinkList::ElemType DuLinkList::nextElemAt(ElemType cur_e, ElemType &Nex_e) //若cur_e是链表的数据元素,且不是最后一个,则用next_e返回它的后继,
	{
		DulNode *p = m_heap->m_next->m_next;   //指向第二个元素, 如果指向第一个元素的话,获得最后一个元素的后继,就会输出垃圾值
		while (p != m_heap)
		{
			if (p->m_prior->m_data == cur_e)
			{
				Nex_e = p->m_data;         //将p所指结点的值赋给Nex_e,Nex_e保存返回
				return TT_OK;
			}
			p = p->m_next;
		}
		return TT_ERROR;
	}
}
// MyDuLinkList 的测试函数
void testMyDuLinkList()
{
	tt::DuLinkList  MyDuLinkList;

	int myLength(0);    //链表的整表创建,尾插法
	cout << "想创建多少数据的链表?";
	cin >> myLength;
	int *myDatas = new int[myLength];
	cout << "请依次输入这" << myLength << "个数据,中间以回车符隔开:" << endl;

	for (int i = 0; i < myLength; ++i)
	{

		cin >> myDatas[i];  //输入要存储的数据的值
	}
	MyDuLinkList.createTail(myDatas, myLength);   //调用createTail函数 建立单链表
	MyDuLinkList.show();

	while (true)
	{
		{
			cout << "\n*************************************************************" << endl
				<< "*******************   双向循环链表基本功能展示   *******************" << endl
				<< "*****************************************************************" << endl
				<< "********************   选择1——数据插入.   **********************" << endl
				<< "********************   选择2——数据删除.   **********************" << endl
				<< "********************   选择3——获取元素.   **********************" << endl
				<< "********************   选择4——查找元素.   **********************" << endl
				<< "********************   选择5——是否为空.   **********************" << endl
				<< "********************   选择6——获取链表长度.   **********************" << endl
				<< "********************   选择7——清空元素.   **********************" << endl
				<< "********************   选择8——输出所有元素. ************************" << endl
				<< "********************   选择9——销毁链表. ************************" << endl
				<< "*********************  选择10——获得元素的前驱.  *****************" << endl
				<< "*********************  选择11——获得元素的后继.  *****************" << endl
				<< "*********************  选择12——逆序输出所有元素.  *****************" << endl
				<< "********************   选择13——清屏!      ************************" << endl
				<< "********************   选择0——退出程序!   ************************" << endl
				<< "***********************************************************************" << endl
				<< "***********************************************************************" << endl;
		}
		cout << "\n*******************   请输入你想要使用的双向循环链表功能的序号  ***************" << endl;
		cout << "请输入你的选择:";
		int userChoice(0);
		cin >> userChoice;
		if (userChoice == 0)
		{
			cout << "程序已退出,感谢您的使用!" << "\n" << endl;
			break;
		}

		switch (userChoice)
		{
		case 1:
		{                                 //插入元素
			int pushLocate(0);
			int pushElement;
			cout << "请输入想添加元素的位置:";
			cin >> pushLocate;
			cout << "请输入想添加的元素:";
			cin >> pushElement;
			if (MyDuLinkList.insertAt(pushLocate, pushElement))
			{
				cout << "数据" << pushElement << "插入成功!" << endl;
				MyDuLinkList.show();
			}
			else
				cout << "插入的位置不合理,数据" << pushElement << "插入失败!" << endl;
			break;
		}
		case 2:
		{                                                //删除元素
			int cancelElem(0);  //要删除的元素
			int cancelLocate(0);  //要删除的位置
			cout << "请输入想要删除的位置:";
			cin >> cancelLocate;
			if (MyDuLinkList.removeAt(cancelLocate, cancelElem))
			{
				cout << "数据" << cancelElem << "删除成功!" << endl;
				MyDuLinkList.show();
			}
			else
				cout << "删除的位置不合理或者链表中没有元素,数据删除失败!" << endl;
			break;
		}
		case 3:                                   //获取元素
		{
			int getLocate(0);
			int getElem(0);
			cout << "请输入想要获取的位置:";
			cin >> getLocate;
			if (MyDuLinkList.getAt(getLocate, getElem))
			{
				cout << "获取的元素为:" << getElem << endl;
				MyDuLinkList.show();
			}
			else
			{
				cout << "获取的位置不合理或者链表中没有元素,获取数据失败!" << endl;
				MyDuLinkList.getLength();
			}
			break;
		}
		case 4:
		{                                             //查找元素
			int getLocate(0);
			int findElem(0);
			cout << "请输入你想要查找的元素的值:";
			cin >> findElem;
			if (MyDuLinkList.getIndexElemAt(getLocate, findElem))
			{
				cout << "找到了元素" << findElem << ",其在表中的索引值为:" << getLocate << "\n" << endl;
				MyDuLinkList.show();
			}
			else
			{
				cout << "链表中不存在所需找的值!" << "\n" << endl;
				MyDuLinkList.show();
			}
			break;
		}
		case 5:
			if (MyDuLinkList.isEmpty())
			{
				cout << "当前双向循环链表为空!" << endl;
				MyDuLinkList.getLength();
			}
			else
			{
				cout << "当前的双向循环链表非空!" << endl;
				MyDuLinkList.show();
			}
			break;
		case 6:
		{                                         //获取长度
			int temp = MyDuLinkList.getLength();
			cout << "双向循环链表当前的长度为:" << temp << endl;
			break;
		}
		case 7:
			if (MyDuLinkList.clear())
			{
				cout << "双向循环链表已被清空!" << endl;
				MyDuLinkList.show();
			}
			else
			{
				cout << "双向循环链表清空失败!" << endl;
				MyDuLinkList.show();
			}
			break;
		case 8:
			MyDuLinkList.show();
			break;
		case 9:
		{
			cout << "你确定要销毁该双向循环链表吗?(若销毁请输入输入(Y/y))";
			char yesOrNo;
			cin >> yesOrNo;
			if ((yesOrNo == 'Y') || (yesOrNo == 'y'))
			{
				if (MyDuLinkList.destroy())
				{
					cout << "双向循环链表已被销毁." << "\n" << endl;
				}
				else
					cout << "双向循环链表销毁失败." << "\n" << endl;
			}
			break;
		}
		case 10:
		{
			int frontElem(0);
			int getElem(0); //返回的是要获得哪一个元素的前驱的元素
			cout << "输入你想要获得哪一个元素的前驱?(注意:不能获取第一个元素的):";
			cin >> frontElem;
			if (MyDuLinkList.priorElemAt(frontElem, getElem))
			{
				cout << "数据元素" << frontElem << "的,前驱元素是:" << getElem << endl;
				MyDuLinkList.show();
			}
			else
			{
				cout << "获取前驱元素失败,不能获取第一个元素的前驱或者链表中没有你输入的元素!" << endl;
				MyDuLinkList.show();
			}
			break;
		}
		case 11:
		{
			int rearElem(0);
			int getElem(0);
			cout << "输入你想要获得哪一个元素的后继?(注意:不能获取最后一个元素的):";
			cin >> rearElem;
			if (MyDuLinkList.nextElemAt(rearElem, getElem))
			{
				cout << "数据元素" << rearElem << "的,后继元素是:" << getElem << endl;
				MyDuLinkList.show();
			}
			else
			{
				cout << "获取后继元素失败!不能获取最后一个元素的后继或者链表中没有你输入的元素!" << endl;
				MyDuLinkList.show();
			}
			break;
		}
		case 12:
			MyDuLinkList.traverseBack();
			break;
		case 13:
			system("cls");
			cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl;
			break;
		default:
			cout << "输入的序号不正确,请重新输入!" << "\n" << endl;
		}
	}
	delete[]myDatas;
	myDatas = nullptr;
}
int main()
{
	testMyDuLinkList();
	system("pause");
	return 0;
}

 

下面用C#代码实现双向列表:

namespace DateStructure
{
    interface ILinkedList
    {
        void insertAt(int pos, int elem);
        int removeAt(int pos);
        int getAt(int pos);
        int locationElemAt(int value);
        int getLength();
        bool isEmpty();
        void clear();
        void show();
        void createTail(params int[] arrElem); //尾插法


    }
    class Node
    {
        public int m_Data { set; get; } = 0;
        public Node m_Next { set; get; } = null;
        public Node m_Prev { set; get; } = null;
    }
    class DoubleLink : ILinkedList
    {
        private Node m_Head;
        public DoubleLink()
        {
            m_Head = new Node();
            Debug.Assert(m_Head != null, "头结点创建失败!");
            m_Head.m_Prev = m_Head.m_Next = m_Head;
        }

        public void createTail(params int[] arrElem) // 双向列表的头插法
        {
            m_Head.m_Data = arrElem.Length;
            Node temp = m_Head;
            for (int i = 0; i != arrElem.Length; ++i)
            {
                Node newNode = new Node();
                Debug.Assert(newNode != null, "整表创建双向链表失败!");
                newNode.m_Data = arrElem[i];
                temp.m_Next = newNode;
                newNode.m_Prev = temp;
                newNode.m_Next = m_Head;
                m_Head.m_Prev = newNode;
                temp = newNode;
            }
        }
        public void insertAt(int pos, int elem)
        {
            if ((pos < 1) || (pos > ((m_Head.m_Data) + 1)))   //i位置不合理时,返回false
            {
                WriteLine("插入元素失败,插入的位置不正确!");
                return;
            }
            Node temp = m_Head;
            int locitionPos = 1;
            while (locitionPos < pos)
            {
                temp = temp.m_Next;
                ++locitionPos;
            }
            Node newNode = new Node();
            Debug.Assert(newNode != null, "插入节点分配内存失败!");
            newNode.m_Data = elem;
            newNode.m_Next = temp.m_Next;
            temp.m_Next.m_Prev = newNode;
            temp.m_Next = newNode;
            newNode.m_Prev = temp;
            ++m_Head.m_Data;
        }

        public void clear()
        {
            m_Head.m_Data = 0;
            m_Head.m_Prev = m_Head.m_Next = m_Head;
        }

        public int getLength()
        {
            return m_Head.m_Data;
        }
        public bool isEmpty()
        {
            return m_Head.m_Data == 0;
        }

        public int getAt(int pos)
        {
            if (pos < 1 || m_Head.m_Data == 0 || pos > m_Head.m_Data)
            {
                WriteLine("获取元素的位置不正确,或者该该链表为空!");
                return -1;
            }
            Node temp = m_Head;
            int locitionPos = 1;
            while (locitionPos < pos)
            {
                temp = temp.m_Next;
                ++locitionPos;
            }
            return temp.m_Next.m_Data;
        }

        public int removeAt(int pos)
        {
            if (pos < 1 || m_Head.m_Data == 0 || pos > m_Head.m_Data)
            {
                WriteLine("删除的位置不正确,或者该链表是个空表,没有元素可以删除!");
                return -1;
            }
            Node temp = m_Head;
            int locitionPos = 1;
            while (locitionPos < pos)
            {
                temp = temp.m_Next;
                ++locitionPos;
            }
            var removeElem = temp.m_Next.m_Data;
            temp.m_Next = temp.m_Next.m_Next;
            temp.m_Next.m_Next.m_Prev = temp;
            --m_Head.m_Data;
            return removeElem;
        }

        public int locationElemAt(int value)
        {
            if (m_Head.m_Data == 0)
            {
                WriteLine("该链表为空,无法进行查找!");
                return -1;
            }
            Node temp = m_Head;
            for (int i = 0; i != m_Head.m_Data; ++i)
            {
                temp = temp.m_Next;
                if (temp.m_Data == value)
                {
                    return i + 1;
                }
            }
            return -1;
        }

        public void show()
        {
            if (m_Head.m_Data == 0)
            {
                WriteLine("该双向列表中没有数据,无法显示!");
                return;
            }
            else
            {
                Node temp = m_Head;
                Write("输出此时双向列表中所有的元素:");
                for (int i = 0; i != m_Head.m_Data; ++i)
                {
                    temp = temp.m_Next;
                    Write($"{temp.m_Data},");
                }
                WriteLine();
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            DoubleLink myList = new DoubleLink();

            const int arraySize = 5;
            int[] myIntArray = new int[arraySize] { 0, 22, 33, 44, 55 };
            myList.createTail(myIntArray);
            myList.show();

            WriteLine("请输入你想要查找的元素的值:");
            int locationPosElem = Convert.ToInt32(Console.ReadLine());
            int tempElem = myList.locationElemAt(locationPosElem);
            if (0 <= tempElem)
            {
                WriteLine($"该元素的位置序号是:{tempElem}");
                myList.show();
            }
            else
            {
                WriteLine("该元素不再链表中!");
            }

            WriteLine("请输入你想获取链表中哪一个位置的元素:");
            int getArrElem = Convert.ToInt32(Console.ReadLine());
            WriteLine($"该位置的元素是:{myList.getAt(getArrElem)}");
            myList.show();


            WriteLine("请输入你想删除哪一个位置的元素:");
            int removePos = Convert.ToInt32(Console.ReadLine());
            int temp = myList.removeAt(removePos);
            if (temp >= 0)
            {
                WriteLine($"被删除的元素为:{temp}");
            }
            WriteLine($"\n获取当前链表的总个数:{myList.getLength()}");
            myList.show();


            WriteLine("请输入你想插入的元素值:");
            int pushElem = Convert.ToInt32(Console.ReadLine());
            WriteLine("请输入你想插入的位置:");
            int pushLocition = Convert.ToInt32(Console.ReadLine());
            myList.insertAt(pushLocition, pushElem);
            WriteLine($"\n获取当前链表的总个数:{myList.getLength()}");
            myList.show();

            if (myList.isEmpty())
            {
                WriteLine("当前的链表为空!");
            }
            else
                WriteLine("当前的链表非空!");
            WriteLine($"\n获取当前链表的总个数:{myList.getLength()}");
            myList.clear();
            WriteLine($"获取当前链表的总个数:{myList.getLength()}");
        }
    }
}

你可能感兴趣的:(数据结构)