第8章 数据结构2

数据结构主要研究数据的组织方式以及相应的操作方法。它除了描述数据本身之外, 还描述数据之间的相互关系。它不仅是一般程序设计的基础,而且是设计编译程序、操作 系统、数据库、人工智能及其他大型应用程序的基础。如今,数据结构在计算机科学中占 有重要的地位。对于相当多的程序设计来说,认清数据的内在关系,可获得对问题的正确 认识,看清问题的结构甚至解法。在一定意义上,程序所描述的就是在数据结构上实现的 算法。算法的设计依赖于数据的逻辑结构,算法的实现依赖于数据的存储结构,所以数据 结构选择得好坏,对程序质量的影响甚大。掌握基本的数据结构知识,是提高程序设计水 平的必要条件。

 面试题13   编程实现一个双向链表的建立
 考点:双向链表的操作
 出现频率:★★★★
 【解析】
 双向链表的定义如下。

typedef struct DbNode
{
	int data;				//节点数据
	DbNode* left;			//前驱节点指针
	DbNode* right;			//后继节点指针
}DbNode;

 (1)建立双向链表:为方便,这里定义了3个函数。
 CreateNode()根据数据来创建一个节点,返回新创建的节点。
 CreateList()函数根据一个节点数据创建链表的表头,返回表头节点。
 AppendNode ()函数总在表尾插入新节点(其内部调用CreateNode()生成节点),返回表 头节点。

 //根据数据创建创建节点
DbNode* CreateNode(int data)
{
	DbNode* pnode = (DbNode*)malloc(sizeof(DbNode));
	pnode->data = data;
	pnode->left = pnode->right = pnode;		//创建新节点时
											//让其前驱和后继指针都指向自身
	return pnode;
}

//创建链表
DbNode* CreateList(int head)						//参数给出表头节点数据
{													//表头节点不作为存放有意义数据的节点
	DbNode* pnode = (DbNode*)malloc(sizeof(DbNode));
	pnode->data = head;
	pnode->left = pnode->right = pnode;

	return pnode;
}

//插入新节点,总是在表尾插入;返回表头节点
DbNode* AppendNode(DbNode* head, int data)			//参数1是链表的表头节点
{													//参数2是要插入的节点,其数据为data
	DbNode* node = CreateNode(data);				//创建数据为data的新节点
	DbNode* p = head, * q{ nullptr };

	while (p != nullptr)
	{
		q = p;
		p = p->right;
	}
	q->right = node;
	node->left = q;

	return head;
}

我们可以使用其中的CreateList()和AppendNode()来生成一个链表。下面是一个数据生 成从0到9含有10个节点的循环链表。

DbNode* head = CreateList(0);			//生成表头,表头数据为0
 
for (int i = 1; i < 10; i++)
{
	head = AppendNode(head, i);			//添加9个节点,数据为从1到9
}

 面试题14  编程实现一个双向链表的测长
 考点:双向链表的操作
 出现频率:★★★★
 【解析】
 为了得到双向链表的长度,需要使用right指针进行遍历,直到得到NULL为止。

//获取链表的长度
int GetLength(DbNode* head)					//参数为链表的表头节点
{
	int count = 1;
	DbNode* pnode = nullptr;

	if (head == nullptr)					//head为nullptr表示链表空
	{
		return 0;
	}
	pnode = head->right;
	while (pnode != nullptr)
	{
		pnode = pnode->right;				//使用right指针遍历
		count++;
	}

	return count;
}

面试题15    编程实现一个双向链表的打印
 考点:双向链表的操作
 出现频率:★★★★
 【解析】
 与测长的方法一样,使用right指针进行遍历。

//打印整个链表
void PrintList(DbNode* head)				//参数为链表的表头节点
{
	DbNode* pnode = nullptr;
	if (head == nullptr)					//head为nullptr表示链表空
	{
		return;
	}
	pnode = head;
	while (pnode != nullptr)				//使用right指针遍历
	{
		printf("%d ", pnode->data);
		pnode = pnode->right;
	}
	printf("\n");
}

面试题16  编程实现一个双向链表节点的查找
 考点:双向链表的操作
 出现频率:★★★★
 【解析】
 使用right指针遍历,直至找到数据为data的节点。如果找到节点,返回节点,否则返 回NULL。

//查找节点,成功则返回满足条件的节点指针,否则返回NULL
DbNode* FindNode(DbNode* head, int data)			//参数1是链表的表头节点
{													//参数2是要查找的节点,其数据为data
	DbNode* pnode = head;

	if (head == nullptr)							//链表为空时返回nullptr
	{
		return nullptr;
	}

	//找到数据或者到达链表末尾,退出while循环
	while (pnode->right != nullptr && pnode->data != data)
	{
		pnode = pnode->right;						//使用right指针遍历
	}

	//没有找到数据为data的节点,返回nullptr
	if (pnode->right == nullptr)
	{
		return nullptr;
	}

	return pnode;
}

面试题17   编程实现一个双向链表节点的插入
 考点:双向链表的操作
 出现频率:★★★★
 【解析】
 节点p后插入一个节点。
 这里分为两种插入情况:一种是插入位置在中间,另一种是插入位置在末尾。两种情
 况有一点不同:插入位置在中间时需要把p的原后继节点的前驱指针指向新插入的节点。

//在node节点之后插入新节点
void InsertNode(DbNode* node, int data)
{
	DbNode* newnode = CreateNode(data);
	DbNode* p = nullptr;

	if (node == nullptr)			//node为nullptr时返回
	{
		return;
	}
	if (node->right == nullptr)		//node为最后一个节点
	{
		node->right = newnode;
		newnode->left = node;
	}
	else
	{									//node为中间节点
		newnode->right = node->right;	//newnode向右连接
		node->right->left = newnode;
		node->right = newnode;			//newnode向左连接
		newnode->left = node;
	}
} 

面试题18 编程实现一个双向链表节点的删除
考点:双向链表的操作

DbNode* DeleteNode(DbNode* head, int data)		//参数1是链表的表头节点
{												//参数2是要插入的节点,其数据为data
	DbNode* ptmp = nullptr;

	DbNode* pnode = FindNode(head, data);		//查找节点
	if (nullptr == pnode)						//节点不存在,返回nullptr
	{
		return nullptr;
	}
	else if (pnode->left == nullptr)			//node为第一个节点
	{
		head = pnode->right;
		if (head != nullptr)					//链表不为空	
		{
			head->left = nullptr;
		}
	}
	else if (pnode->right == nullptr)			//node为最后一个节点
	{
		pnode->left->right = nullptr;
	}
	else										//node为中间的节点
	{
		pnode->left->right = pnode->right;
		pnode->right->left = pnode->left;
	}

	free(pnode);								//释放已被删除的节点空间
	return head;
}


 面试题19  实现有序双向循环链表的插入操作
 考点:双向循环链表的操作
 出现频率:★★★
 【解析】
 源代码如下。

#include <iostream>
using namespace std;

typedef struct DbNode
{
	int data;				//节点数据
	DbNode* left{nullptr};			//前驱节点指针
	DbNode* right{ nullptr };			//后继节点指针
}DbNode;

//根据数据创建创建节点
DbNode* CreateNode(int data)
{
	DbNode* pnode = (DbNode*)malloc(sizeof(DbNode));
	pnode->data = data;
	pnode->left = pnode;		//创建新节点时
	pnode->right = pnode;
											//让其前驱和后继指针都指向自身
	return pnode;
}

//创建链表
DbNode* CreateList(int head)						//参数给出表头节点数据
{													//表头节点不作为存放有意义数据的节点
	DbNode* pnode = (DbNode*)malloc(sizeof(DbNode));
	pnode->data = head;
	pnode->left = pnode;
	pnode->right = pnode;

	return pnode;
}

//插入新节点,总是在表尾插入;返回表头节点
DbNode* AppendNode(DbNode* head, int data)			//参数1是链表的表头节点
{													//参数2是要插入的节点,其数据为data
	DbNode* node = CreateNode(data);				//创建数据为data的新节点
	DbNode* p{nullptr}, * q{nullptr};
	if (head == nullptr)
	{
		return nullptr;
	}
	q = head->right;
	p = head->right;

	while (p != head)
	{
		q = p;
		p = p->right;
	}
	q->right = node;
	node->left = q;
	node->right = head;
	head->left = node;

	return head;
}

//获取链表的长度
int GetLength(DbNode* head)					//参数为链表的表头节点
{
	int count = 1;
	DbNode* pnode = nullptr;

	if (head == nullptr)					//head为nullptr表示链表空
	{
		return 0;
	}
	pnode = head->right;
	while (pnode != nullptr)
	{
		pnode = pnode->right;				//使用right指针遍历
		count++;
	}

	return count;
}
//15
//打印整个链表
void PrintList(DbNode* head)				//参数为链表的表头节点
{
	DbNode* pnode = nullptr;
	if (head == nullptr)					//head为nullptr表示链表空
	{
		return;
	}
	printf("%d ", head->data);
	pnode = head;
	while (pnode != head)				//使用right指针遍历
	{
		printf("%d ", pnode->data);
		pnode = pnode->right;
	}
	printf("\n");
}

//16
//查找节点,成功则返回满足条件的节点指针,否则返回NULL
DbNode* FindNode(DbNode* head, int data)			//参数1是链表的表头节点
{													//参数2是要查找的节点,其数据为data
	DbNode* pnode = head;

	if (head == nullptr)							//链表为空时返回nullptr
	{
		return nullptr;
	}

	//找到数据或者到达链表末尾,退出while循环
	while (pnode->right != nullptr && pnode->data != data)
	{
		pnode = pnode->right;						//使用right指针遍历
	}

	//没有找到数据为data的节点,返回nullptr
	if (pnode->right == nullptr)
	{
		return nullptr;
	}

	return pnode;
}

//17
插入一个有序链表(从小到大排列),返回表头
DbNode* InsertNode(DbNode* head, int data)
{
	DbNode* p{nullptr};
	DbNode* q{ nullptr };
	DbNode* node{ nullptr };
	node = CreateNode(data);		//新建数据节点
	
	if (head == nullptr)			//空链表,返回新建节点
	{
		head = node;
		return node;
	}
	if (head->data > data)			//data小于表头数据,插入到表头之前
	{								//把新建节点作为表头
		head->left->right = node;	//末节点后继指针指向node
		node->left = head->left;	//node的前驱指针指向末节点
		node->right = head;			//node的后继指针指向head
		head->left = node;			//head的前驱指针指向node
		return node;
	}
	
	p = head->right;
	while (p->data <= data && p != head)
	{
		p = p->right;
	}

	p = p->left;
	q = p->right;

	//把node插入p和q之间
	p->right = node;
	node->left = p;
	node->right = q;
	q->left = node;

	return head;
}

//18
//删除满足指定条件的节点,返回表头节点,删除失败,返回NULL(失败的原因是不存在该节点)
DbNode* DeleteNode(DbNode* head, int data)		//参数1是链表的表头节点
{												//参数2是要插入的节点,其数据为data
	DbNode* ptmp = nullptr;

	DbNode* pnode = FindNode(head, data);		//查找节点
	if (nullptr == pnode)						//节点不存在,返回nullptr
	{
		return nullptr;
	}
	else if (pnode->left == nullptr)			//node为第一个节点
	{
		head = pnode->right;
		if (head != nullptr)					//链表不为空	
		{
			head->left = nullptr;
		}
	}
	else if (pnode->right == nullptr)			//node为最后一个节点
	{
		pnode->left->right = nullptr;
	}
	else										//node为中间的节点
	{
		pnode->left->right = pnode->right;
		pnode->right->left = pnode->left;
	}

	free(pnode);								//释放已被删除的节点空间
	return head;
}

int main()
{
	DbNode* head = CreateList(1);			//生成表头,表头数据为1
	AppendNode(head, 3);
	AppendNode(head, 6);
	AppendNode(head, 8);
	PrintList(head);
	head = InsertNode(head, 0);
	PrintList(head);
	head = InsertNode(head, 4);
	PrintList(head);
	head = InsertNode(head, 10);
	PrintList(head);

	cout << "hello world" << endl;
	return 0;
}

下面是各个函数的简单说明。
 CreateNode()根据数据创建节点,包括头节点和一般节点。创建头节点时,left和right 指针都指向本身来形成环状。
 AppendNode()插入新节点,总是在末尾插入。返回表头节点。 PrintList()打印整个链表,由于属于循环链表,因此遍历回到head节点时结束打印。
 InsertNode()数据插入一个升序的链表,返回表头节点。这里插入位置分为表头和非表 头两种情况。如果是表头,返回的指针是新的节点,否则返回的是原来的表头节点。
 main()函数建立了一个升序链表,并对它的不同位置进行了插入操作。 执行结果如下。

面试题20  删除两个双向循环链表的相同结点
 考点:双向循环链表的操作
 出现频率:★★★
 有两个双向循环链表A,B,知道其头指针分别为pHeadA,pHeadB。请写一个函数, 将两链表中data值相同的节点删除。
 【解析】
 可以这样来处理:
 (1)把A中含有的与B中相同的数据节点抽出来,组成一个新的链表,例如
 链表A: 1 2 3 4 2 6 4
 链表B: 10 20 3 4 2 10
 新建链表C: 2 3 4
 (2)遍历链表C,删除A和B的所有节点。如果A含有数据相等的节点(A有两个4), 且B也含有此数据节点(B也有4),则A中数据相等的节点全部被删除。
 根据以上分析,我们实现了以下函数。
 (1)GetLink()函数,其源代码如下。

//GetLink()通过headA值表和headB链表中相同的数据节点,得到一个新的链表
DbNode* GetLink(DbNode* headA, DbNode* headB)
{
	int i = 0;
	DbNode* newHead{ nullptr };			//返回新的链表
	DbNode* pnodeA = headA;				//遍历headA
	DbNode* pnodeB = headB;				//遍历headB
	DbNode* pnode = nullptr;			//遍历newhead

	do
	{
		DbNode* node = nullptr;			//用于查看新的链表中是否已经存在节点数据
		pnodeB = headB;

		if ((node = FindNode(newHead, pnodeA->data)) != nullptr)
		{
			//若newHead中已含有此节点数据,则不考虑此节点的比较,进行下一次遍历
			pnodeA = pnodeA->right;
			continue;
		}
		do
		{
			if (pnodeA->data == pnodeB->data)				//找到A与B中相同的数据节点
			{
				i++;
				if (i == 1)									//第一次生成头节点
				{
					newHead = CreateNode(pnodeA->data);
				}
				else										//不是第一次,添加节点
				{
					AppendNode(newHead, pnodeA->data);
				}
				break;
			}
			pnodeB = pnodeB->right;							//对链表B进行遍历
		} while (pnodeB != headB);
		pnodeA = pnodeA->right;								//对链表A进行遍历
	} while (pnodeA != headA);

	return newHead;
}

GetLink()把A和B的两个链表头节点指针传入,返回一个链表头节点指针。这里有下 面几点需要说明。
 由于要遍历A和B,因此需要使用了嵌套的循环。外层是对链表A进行遍历的,内层 是对链表B进行遍历的。
 当找到A、B的相同数据后,由于新建表头节点情况(使用CreateNode函数)与插入 节点情况(AppendNode函数)不同,因此代码第25~33行进行了区别对待。
 出于效率的考虑,代码第15~20行首先判断A当前节点的数据是否已经在新建的链表 中,如果已经存在,则不进行B的遍历,继续进行A的下一个节点的判断。
 (2)DeleteNode函数,其源代码如下。

//酬除链表中所有数据等于Value的节点
DbNode* DeleteNode(DbNode* pHeader, int Value)
{
	DbNode* pNode = nullptr;
	DbNode* pNodeRight = nullptr;
	int bRet = 0;

	if (pHeader == nullptr)
		return nullptr;

	while(pHeader->data == Value)			//头节点的数据为Value,删除
	{
		pNode = pHeader->right;
		if (pHeader == pHeader->right)		//链表只剩下一个元素
		{
			free(pHeader);					//除节点,此时链表为空,返回nullptr
			return nullptr;
		}
		//链表还有其他节点,把原来头节点的左、右两个节点相连
		pHeader->left->right = pHeader->right;
		pHeader->right->left = pHeader->left;
		free(pHeader);						//释放头节点内存
		pHeader = pNode;					//把原来头节点的下一个节点作为新的头节点
	}

	pNode = pHeader->right;					//要删除的不是头节点
	while (pNode != pHeader)				//遍历链表,直到回到头节点
	{
		pNodeRight = pNode->right;			//保存下一个节点	
		if (pNode->data == Value)			//如果搜索到Value,删除此节点
		{
			pNode->left->right = pNodeRight;
			pNodeRight->left = pNode->left;
			free(pNode);
		}
		pNode = pNodeRight;					//指向下一个节点
	}

	return pHeader;
}

DeleteNode()传入的参数为链表头节点的指针以及需要删除的数据值,返回删除后的头 节点指针。有下面几点需要说明。
 口 当删除的节点为头节点时,此时需要考虑两种情况,即链表中只有一个头节点, 以及删除后的头节点数据也等于Value值。代码第13~17行对第一种情况进行了
 判断,代码第10行的while循环是出于第二种情况的考虑。另外,头节点为下一 个节点(pHeader发生了变化)。
 □当删除的节点不是头节点时,简单地进行循环,搜索数据为Value的节点并删除之, 直到当前节点回到头节点结束。
 (3)DeleteEqual ()函数,其源代码如下。

//DeleteEqual删除ppHeadA与ppHeadB两个链表所有含相同数据的节点
//参数ppHeadA和ppHeadB分别为链表A和链表B头节点指针的地址
void DeleteEqual(DbNode** ppHeadA, DbNode** ppHeadB)
{
	DbNode* pHeadA = *ppHeadA, * pHeadB = *ppHeadB;
	DbNode* head = nullptr;

	if (ppHeadA == nullptr || ppHeadB == nullptr)		//ppHeadA或ppHeadB不合法
	{
		return;
	}
	if (pHeadA == nullptr || pHeadB == nullptr)			//链表A或B有一个为空
	{
		return;
	}

	head = GetLink(pHeadA, pHeadB);						//获得A和B的相同数据节点所构成的链表
	while (head != nullptr)
	{
		pHeadA = DeleteNode(pHeadA, head->data);		//删除A中所有head的数据节点
		pHeadB = DeleteNode(pHeadB, head->data);		//删除A中所有head的数据节点
		head = DeleteNode(head, head->data);			//删除head当前节点
	}

	*ppHeadA = pHeadA;				//返回ppheadA
	*ppHeadB = pHeadB;				//返回ppHeadB
}

DeleteEqualO是最终的调用函数,它首先判断传入参数的有效性,然后调用前面的 GetLinkO)得到链表A和链表B的相同数据所构成的新链表(代码第17行),最后调用前面 的DeleteNode()循环删除各个链表中的节点(代码第18~23行)。
 注意:由于DeleteEqual()函数有可能删除链表A和B的头节点,为了保存函数返回后 A和B的头节点,参数必须使用指针的地址或者指针的引用(C++中可行)。这里使用的是 指针的地址,所以在最后(代码第25、26行)保存链表头节点指针。

#include <iostream>
using namespace std;

typedef struct DbNode
{
	int data;				//节点数据
	DbNode* left{nullptr};			//前驱节点指针
	DbNode* right{ nullptr };			//后继节点指针
}DbNode;

//根据数据创建创建节点
DbNode* CreateNode(int data)
{
	DbNode* pnode = (DbNode*)malloc(sizeof(DbNode));
	pnode->data = data;
	//pnode->left = pnode;		//创建新节点时
	//pnode->right = pnode;
											//让其前驱和后继指针都指向自身
	return pnode;
}

//创建链表
DbNode* CreateList(int head)						//参数给出表头节点数据
{													//表头节点不作为存放有意义数据的节点
	DbNode* pnode = (DbNode*)malloc(sizeof(DbNode));
	pnode->data = head;
	pnode->left = nullptr;
	pnode->right = nullptr;

	return pnode;
}

//插入新节点,总是在表尾插入;返回表头节点
DbNode* AppendNode(DbNode* head, int data)			//参数1是链表的表头节点
{													//参数2是要插入的节点,其数据为data
	DbNode* node = CreateNode(data);				//创建数据为data的新节点
	DbNode* p{nullptr}, * q{nullptr};
	if (head == nullptr)
	{
		return nullptr;
	}
	q = head->right;
	p = head->right;

	while (p != head)
	{
		q = p;
		p = p->right;
	}
	q->right = node;
	node->left = q;
	node->right = head;
	head->left = node;

	return head;
}

//尾插
void LTPushBack(DbNode* phead, int x)
{
	if (phead == nullptr)
		return;

	DbNode* newnode = CreateNode(x);

	DbNode* p{nullptr};
	DbNode* q{ nullptr };

	if (phead->right == nullptr)		//如果头结点指向的数据为空,直接加到头结点后面。
	{
		phead->right = newnode;
		newnode->left = phead;
		newnode->right = nullptr;
	}
	else
	{
		p = phead->right; //已存在数据链表,让p指向最后一个结点。
		while (p->right != nullptr)
		{
			p = p->right;				//使用right指针遍历
		}
		p->right = newnode;
		newnode->left = p;
		newnode->right = nullptr;
	}
}


//获取链表的长度
int GetLength(DbNode* head)					//参数为链表的表头节点
{
	int count = 1;
	DbNode* pnode = nullptr;

	if (head == nullptr)					//head为nullptr表示链表空
	{
		return 0;
	}
	pnode = head->right;
	while (pnode != nullptr)
	{
		pnode = pnode->right;				//使用right指针遍历
		count++;
	}

	return count;
}
//15
//打印整个链表
void PrintList(DbNode* head)				//参数为链表的表头节点
{
	DbNode* pnode = nullptr;
	if (head == nullptr)					//head为nullptr表示链表空
	{
		return;
	}
	printf("%d ", head->data);
	pnode = head->right;
	while (pnode != nullptr)				//使用right指针遍历
	{
		printf("%d ", pnode->data);
		pnode = pnode->right;
	}
	printf("\n");
}

//16
//查找节点,成功则返回满足条件的节点指针,否则返回NULL
DbNode* FindNode(DbNode* head, int data)			//参数1是链表的表头节点
{													//参数2是要查找的节点,其数据为data
	DbNode* pnode = head;

	if (head == nullptr)							//链表为空时返回nullptr
	{
		return nullptr;
	}

	//找到数据或者到达链表末尾,退出while循环
	while (pnode->right != nullptr && pnode->data != data)
	{
		pnode = pnode->right;						//使用right指针遍历
	}

	//没有找到数据为data的节点,返回nullptr
	if (pnode->right == nullptr)
	{
		return nullptr;
	}

	return pnode;
}

//17
DbNode* InsertNode(DbNode* node, int data)
{
	DbNode* p{ nullptr };
	DbNode* newnode = CreateNode(data);		//新建数据节点

	if (node == nullptr)			//空链表,返回新建节点
	{
		return nullptr;
	}

	if (node->right == nullptr)
	{
		node->right = newnode;
		newnode->left = node;
	}
	else
	{
		newnode->right = node->right;
		node->right->left = newnode;
		node->right = newnode;
		newnode->left = node;
		
	}


	return node;
}

//插入一个有序链表(从小到大排列),返回表头
DbNode* InsertNode1(DbNode* head, int data)
{
	DbNode* p{nullptr};
	DbNode* q{ nullptr };
	DbNode* node{ nullptr };
	node = CreateNode(data);		//新建数据节点
	
	if (head == nullptr)			//空链表,返回新建节点
	{
		head = node;
		return node;
	}
	if (head->data > data)			//data小于表头数据,插入到表头之前
	{								//把新建节点作为表头
		node->left = head->left;	//node的前驱指针指向末节点
		node->right = head;			//node的后继指针指向head
		head->left = node;			//head的前驱指针指向node
		return node;
	}
	
	p = head->right;
	while (p->data <= data && p != head && p->right != nullptr)
	{
		p = p->right;
	}
	if (p->right == nullptr && p->data <= data) //说明已到达最后一个元素,这时直接把它加入到最后即可
	{
		p->right = node;
		node->left = p;
		node->right = nullptr;
	}
	else
	{
		p = p->left;
		q = p->right;
		//把node插入p和q之间
		p->right = node;
		node->left = p;
		node->right = q;
		q->left = node;
	}
	

	return head;
}

//18
//删除满足指定条件的节点,返回表头节点,删除失败,返回NULL(失败的原因是不存在该节点)
//DbNode* DeleteNode(DbNode* head, int data)		//参数1是链表的表头节点
//{												//参数2是要插入的节点,其数据为data
//	DbNode* ptmp = nullptr;
//
//	DbNode* pnode = FindNode(head, data);		//查找节点
//	if (nullptr == pnode)						//节点不存在,返回nullptr
//	{
//		return nullptr;
//	}
//	else if (pnode->left == nullptr)			//node为第一个节点
//	{
//		head = pnode->right;
//		if (head != nullptr)					//链表不为空	
//		{
//			head->left = nullptr;
//		}
//	}
//	else if (pnode->right == nullptr)			//node为最后一个节点
//	{
//		pnode->left->right = nullptr;
//	}
//	else										//node为中间的节点
//	{
//		pnode->left->right = pnode->right;
//		pnode->right->left = pnode->left;
//	}
//
//	free(pnode);								//释放已被删除的节点空间
//	return head;
//}

//20
//GetLink()通过headA值表和headB链表中相同的数据节点,得到一个新的链表
DbNode* GetLink(DbNode* headA, DbNode* headB)
{
	int i = 0;
	DbNode* newHead{ nullptr };			//返回新的链表
	DbNode* pnodeA = headA;				//遍历headA
	DbNode* pnodeB = headB;				//遍历headB
	DbNode* pnode = nullptr;			//遍历newhead

	do
	{
		DbNode* node = nullptr;			//用于查看新的链表中是否已经存在节点数据
		pnodeB = headB;

		if ((node = FindNode(newHead, pnodeA->data)) != nullptr)
		{
			//若newHead中已含有此节点数据,则不考虑此节点的比较,进行下一次遍历
			pnodeA = pnodeA->right;
			continue;
		}
		do
		{
			if (pnodeA->data == pnodeB->data)				//找到A与B中相同的数据节点
			{
				i++;
				if (i == 1)									//第一次生成头节点
				{
					newHead = CreateNode(pnodeA->data);
				}
				else										//不是第一次,添加节点
				{
					AppendNode(newHead, pnodeA->data);
				}
				break;
			}
			pnodeB = pnodeB->right;							//对链表B进行遍历
		} while (pnodeB != headB);
		pnodeA = pnodeA->right;								//对链表A进行遍历
	} while (pnodeA != headA);

	return newHead;
}

//酬除链表中所有数据等于Value的节点
DbNode* DeleteNode(DbNode* pHeader, int Value)
{
	DbNode* pNode = nullptr;
	DbNode* pNodeRight = nullptr;
	int bRet = 0;

	if (pHeader == nullptr)
		return nullptr;

	while(pHeader->data == Value)			//头节点的数据为Value,删除
	{
		pNode = pHeader->right;
		if (pHeader == pHeader->right)		//链表只剩下一个元素
		{
			free(pHeader);					//除节点,此时链表为空,返回nullptr
			return nullptr;
		}
		//链表还有其他节点,把原来头节点的左、右两个节点相连
		pHeader->left->right = pHeader->right;
		pHeader->right->left = pHeader->left;
		free(pHeader);						//释放头节点内存
		pHeader = pNode;					//把原来头节点的下一个节点作为新的头节点
	}

	pNode = pHeader->right;					//要删除的不是头节点
	while (pNode != pHeader)				//遍历链表,直到回到头节点
	{
		pNodeRight = pNode->right;			//保存下一个节点	
		if (pNode->data == Value)			//如果搜索到Value,删除此节点
		{
			pNode->left->right = pNodeRight;
			pNodeRight->left = pNode->left;
			free(pNode);
		}
		pNode = pNodeRight;					//指向下一个节点
	}

	return pHeader;
}

//DeleteEqual删除ppHeadA与ppHeadB两个链表所有含相同数据的节点
//参数ppHeadA和ppHeadB分别为链表A和链表B头节点指针的地址
void DeleteEqual(DbNode** ppHeadA, DbNode** ppHeadB)
{
	DbNode* pHeadA = *ppHeadA, * pHeadB = *ppHeadB;
	DbNode* head = nullptr;

	if (ppHeadA == nullptr || ppHeadB == nullptr)		//ppHeadA或ppHeadB不合法
	{
		return;
	}
	if (pHeadA == nullptr || pHeadB == nullptr)			//链表A或B有一个为空
	{
		return;
	}

	head = GetLink(pHeadA, pHeadB);						//获得A和B的相同数据节点所构成的链表
	while (head != nullptr)
	{
		pHeadA = DeleteNode(pHeadA, head->data);		//删除A中所有head的数据节点
		pHeadB = DeleteNode(pHeadB, head->data);		//删除A中所有head的数据节点
		head = DeleteNode(head, head->data);			//删除head当前节点
	}

	*ppHeadA = pHeadA;				//返回ppheadA
	*ppHeadB = pHeadB;				//返回ppHeadB
}

int main()
{
	DbNode* head = CreateList(1);			//生成表头,表头数据为1
	//AppendNode(head, 3);
	//AppendNode(head, 6);
	//AppendNode(head, 8);
	LTPushBack(head, 3);
	LTPushBack(head, 6);
	LTPushBack(head, 8);
	printf("len = %d\n", GetLength(head));
	PrintList(head);//1 3 6 8
	head = InsertNode1(head, 0);
	PrintList(head);
	head = InsertNode1(head, 10);
	PrintList(head);
	head = InsertNode1(head, 2);
	PrintList(head);
	head = InsertNode1(head, 9);
	PrintList(head);

	cout << "hello world" << endl;
	return 0;
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值