数据结构—链表经典面试题

 链表面试题:

  • 从尾到头打印单链表
  • 删除一个无头单链表的非尾节点(不能遍历链表)
  • 在无头单链表的一个节点前插入一个节点(不能遍历链表)
  • 单链表实现约瑟夫环(JosephCircle)
  • 逆置/反转单链表
  • 单链表排序(冒泡排序&快速排序)
  • 合并两个有序链表,合并后依然有序
  • 查找单链表的中间节点,要求只能遍历一次链表
  • 查找单链表的倒数第k个节点,要求只能遍历一次链表
  • 删除链表的倒数第K个结点
  • 判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算 每个算法的时间复杂度&空间复杂度。
  • 判断两个链表是否相交,若相交,求交点。(假设链表不带环)
  • 判断两个链表是否相交,若相交,求交点。(假设链表可能带环) 【升级版】
  • 复杂链表的复制。一个链表的每个节点,有一个指向next指针指向 下一个节点,还有一个random指针指向这个链表中的一个随机节点 或者NULL,现在要求实现复制这个链表,返回复制后的新链表。
  • 求两个已排序单链表中相同的数据。void UnionSet(Node* l1, Node* l2);

从尾到头打印单链表

问题分析:

方法一:利用两层循环完成 ,外循环用 end 指针控制边界,从最后一个节点指向的NULL开始走,每一次循环向前走一步;内循环完成输出任务,让内层循环的指针 cur 每次从链表的头指针开始不断往后走,直到指向 end 指针指向的前一个节点,完成对该节点数据域的打印工作

void SListPrintTailToHead(SListNode* pHead)
{
	assert(pHead);

	SListNode* end = NULL;
	while (end != pHead) {  //控制边界,每次从后往前走一步
		SListNode* cur = pHead;

		while (cur->_next != end) {  //从头开始遍历,让cur指向给end的前一个节点
			cur = cur->_next;
		}

		printf("%d ", cur->_data);
		end = cur;
	}

	printf("\n");
}

方法二:递归打印,实质是从第一个节点开始在栈空间中不断向下建立函数栈帧,直到为最后一个节点建立完栈帧,然后从下往上不断销毁栈帧,这个过程中节点的遍历其实是从后向前的,所以可以利用递归完成逆序打印

void SListPrintTailToHeadR(SListNode* pHead)
{
	if (pHead == NULL)
		return;

	SListPrintTailToHeadR(pHead->_next);
	printf("%d ",pHead->_data);
}

删除一个无头单链表的非尾节点(不能遍历链表)

问题分析:题目中说的无头链表不是链表没头的意思,只是命题者没给你头指针而已。这个题目中直接删除 pos 指向的节点是无法实现的,需要做适当处理

void SListDelNonTailNode(SListNode* pos)
{
	assert(pos && pos->_next != NULL);  //pos不应该指向最后一个节点

	SListNode* next = pos->_next;  //pos指向的节点的下一个节点
	
	DataType tmp = pos->_data;  //进行数据交换
	pos->_data = next->_data;
	next->_data = tmp;

	pos->_next = next->_next;  //将pos指向的节点和next指向的节点的下一个节点连接起来

	delete next;
	next = NULL;
}

在无头单链表的一个节点前插入一个节点(不能遍历链表)

问题分析:同样题目中说的无头链表不是链表没头的意思,只是命题者没给你头指针而已。很明显在 pos 指向的节点前直接插入是无法实现的,需要做适当处理

void SListInsertFrontNode(SListNode* pos, DataType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(pos->_data);  //给的数据是pos指针指向节点的

	newnode->_next = pos->_next;  //将新节点连到Pos指向的节点之后
	pos->_next = newnode;

	pos->_data = x;  //将pos指针指向的节点的数据改为新节点的初值
}

单链表实现约瑟夫环(JosephCircle) 

问题分析:约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

SListNode* SListJosephCircle(SListNode* pHead, int k)
{
	assert(pHead && k>0);

	SListNode* end = pHead;
	while (end->_next != NULL) {  //最终让end指向给链表的最后一个节点
		end = end->_next;
	}
	end->_next = pHead;  //构成环

	SListNode* cur = pHead;
	while (cur->_next != cur){  //这层循环在环状链表只剩下一个节点时才不会进入

		int count = k;
		SListNode* prev = NULL;

		while (--count) {  //这层循环每次从刚被删除的节点的下一个节点开始找到下一个要删除的节点
			prev = cur;  //记录即将要被删除的节点的前一个节点
			cur = cur->_next;
		}

		prev->_next = cur->_next;

		delete cur;
		cur = NULL;

		cur = prev->_next;  //更新下次寻找要删除的节点时的起点
	}

	return cur;  //返回最终没被删除的那个幸运的节点
}

 逆置/反转单链表

问题描述:

首先认识链表的逆转

方法一:三指针法,这种方法的逆转过程直接看下面的图,需要注意的是,整个逆转结束的标志是n2 = NULL而不是n3 = NULL

下图实现的是具有五个节点的链表的逆转,你再可以走走偶数个节点链表的逆转,完成下图中的过程后再对链表的头指针作个简单处理就好了

SListNode* SListReverseOne(SListNode* pHead)
{
	assert(pHead);

	if (pHead->_next == NULL)  //只有一个节点
		return pHead;

	SListNode* n1, *n2,*n3;
	n1 = pHead;
	n2 = n1->_next;
	n3 = n2->_next;

	while (n2 != NULL) {  //逆转结束的标志是n2 = NULL
		n2->_next = n1;

		n1 = n2;
		n2 = n3;

		if (n3 != NULL)  //这个判断是要有的
			n3 = n3->_next;
	}

	pHead->_next = NULL;  //对头指针的处理
	pHead = n1;

	return pHead;
}

方法二:可以将给定链表的第一个节点取出来,当做新链表的最后一个节点;将给定链表的第二个节点取出来,将它与取出来的第一个节点连起来,当做新链表的倒数第二个节点,依次将每个节点按这样的方式连起来实现链表逆转。具体实现看下图

SListNode* SListReverseTwo(SListNode* pHead)
{
	if (pHead == NULL || pHead->_next == NULL)
		return pHead;

	SListNode* newhead = NULL;
	SListNode* cur = pHead;

	while (cur != NULL){
		SListNode* prev = cur;
		cur = cur->_next;

		prev->_next = newhead;
		newhead = prev;
	}

	return newhead;
}

单链表排序(冒泡排序&快速排序) 

问题分析:关于冒泡排序、快速排序及其他常见的排序算法我已经在之前的博客中详细写过,这里对排序算法的思想不再说明了,这里我就直接实现链表的冒泡排序和快速排序了

数据结构—冒泡排序

数据结构—快速排序

合并两个有序链表,合并后依然有序

问题分析:

方法一:定义一个链表节点类型的指针作为新链表的头指针,比较两个链表中未处理的第一个节点中数据值的大小,调用链表的尾插函数,不断将两个链表的节点依次插入到新链表后面,如果其中一个链表先遍历完,这时候将另一个链表中未遍历的节点依次连到新链表后面即可。

SListNode* SListMergeOne(SListNode* pHead1, SListNode* pHead2)
{
	if (pHead1 == NULL)
		return pHead2;

	if (pHead2 == NULL)
		return pHead1;

	SListNode* newlist = NULL,*list1 = pHead1, *list2 = pHead2;

	while (list1 && list2) {
		if (list1->_data < list2->_data)
		{
			SListPushBack(&newlist,list1->_data);
			list1 = list1->_next;
		}
		else if (list1->_data > list2->_data)
		{
			SListPushBack(&newlist, list2->_data);
			list2 = list2->_next;
		}
		else
		{
			SListPushBack(&newlist, list1->_data);
			list1 = list1->_next;

			SListPushBack(&newlist, list2->_data);
			list2 = list2->_next;
		}
	}

	while (list1) {
		SListPushBack(&newlist, list1->_data);
		list1 = list1->_next;
	}

	while (list2) {
		SListPushBack(&newlist, list2->_data);
		list2 = list2->_next;
	}

	return newlist;
}

方法二:这里提供的方法和方法一的核心思想都是一样的,这里只是通过比较两个给定的链表的头节点的大小,先找出了新链表的头节点,后序操作只需依次在新链表的后面连接节点就行,不需调用链表的尾插操作函数,代码中只给了两句注释,但是他们是这种方法的特别之处和特别需要注意的地方嗷

SListNode* SListMergeTwo(SListNode* pHead1, SListNode* pHead2)
{
	if (pHead1 == NULL)
		return pHead2;
	if (pHead2 == NULL)
		return pHead1;

	SListNode* newlist = NULL,*list1 = pHead1,*list2 = pHead2;

	if (list1->_data <= list2->_data)
	{
		newlist = list1;
		list1 = list1->_next;
	}
	if (list1->_data > list2->_data)
	{
		newlist = list2;
		list2 = list2->_next;
	}

	SListNode* cur = newlist;  //通过上面找出了新链表的头节点
	while (list1 && list2) {
		if (list1->_data < list2->_data)
		{
			cur->_next = list1;
			list1 = list1->_next;
		}
		else if (list1->_data > list2->_data)
		{
			cur->_next = list2;
			list2 = list2->_next;
		}
		else
		{
			cur->_next = list1;
			list1 = list1->_next;

			cur = cur->_next;  //这里必须要让cur往后走一步,这点很容易忘记的!!!

			cur->_next = list2;
			list2 = list2->_next;
		}

		cur = cur->_next;
	}

	while (list1) {
		cur->_next = list1;
		list1 = list1->_next;

		cur = cur->_next;
	}

	while (list2) {
		cur->_next = list2;
		list2 = list2->_next;

		cur = cur->_next;
	}

	return newlist;
}

查找单链表的中间节点,要求只能遍历一次链表

问题分析:单链表的节点数量有可能是奇数个有可能是偶数个,节点数量为奇数个时链表的确有绝对的中间节点,节点数量为偶数个时其实是没有绝对的中间节点的,在这里我们只需找到前一个节点即可,因为在单链表中知道前一个节点找后一个节点是很容易的事,这里更应该关注的是对时间复杂度的要求,题目要求只能遍历一次链表,解决这类问题的方法是前后指针法(快慢指针法)

SListNode* SListFindMidNode(SListNode* pHead)
{
	assert(pHead);

	SListNode* slow = pHead,*fist = pHead;  //起初将快慢指针都指向给链表的头节点

	while (fist->_next != NULL){  //当快指针指向最后一个节点是慢指针已经指向中间节点了
		fist = fist->_next;

		if (fist->_next)  //这里的判断必须要有
		{
			fist = fist->_next;
			slow = slow->_next;
		}
	}

	return slow;
}

查找单链表的倒数第k个节点,要求只能遍历一次链表

问题分析:这个问题需要特别关注的还是题目对时间复杂度的要求,这里我们仍然用快慢指针法

SListNode* SListFindTailKNode(SListNode* pHead, size_t k)
{
	assert(pHead || k<0);

	SListNode* slow = pHead, *fast = pHead;

	while (--k){  //先让快指针向后走K-1步
		fast = fast->_next;

		if (fast == NULL)  //出现这种情况的原因是k值大于链表的长度,我们规定出现这种情况时返回头节点
			return pHead;
	}

	while (fast->_next != NULL) {  //让两个指针同时走,直到快指针指向最后一个节点
		fast = fast->_next;
		slow = slow->_next;
	}

	return slow;
}

删除链表的倒数第K个结点

问题分析:解决这个问题首先用上题中的做法找到倒数第K个节点(你也可以直接调用上面的函数),然后将该节点的值与它后面的节点的值进行交换,此时再删除该节点后面的节点就行了,这种做法的弊端是无法删除最后一个节点(K=1),你可以调用链表的尾删操作函数,你也可以这样做,找出链表的倒数第 k+1 个节点,然后删除该节点的下一个节点即可,这种做法的弊端是无法删除第一个节点(K = 链表的长度)

void SListpopTailKNode(SListNode** pHead, size_t k)
{
	assert(*pHead || k<0);

	if (k == 1)  //删除最后一个节点的情况
	{
		SListPopBack(pHead);
		return;
	}

	SListNode* kNode = SListFindTailKNode(*pHead,k);  //调用函数,获取倒数第k个节点
	SListNode* next = kNode->_next;  //倒数第k个节点的下一个节点

	swap(&(kNode->_data),&(next->_data));  //交换数据

	kNode->_next = next->_next;  

	delete next;
	next = NULL;
}

判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算 每个算法的时间复杂度&空间复杂度

问题分析: 

首先判断链表是否带环

SListNode* SListIsCycle(SListNode* pHead)
{
	assert(pHead);

	SListNode* slow = pHead, *fast = pHead;

	while (fast && fast->_next != NULL) {

		fast = fast->_next->_next;  //快指针每次走两步
		slow = slow->_next;  //慢指针每次走一步

		if (slow == fast)
			return slow;  //slow、fast是快慢指针在环内的相遇节点
	}

	return NULL;  //如果判断出链表不带换,返回NULL
}

求环的长度

size_t SListCycleLen(SListNode* meetnode)
{
	assert(meetnode);

	SListNode* cur = meetnode->_next;
	size_t count = 1;

	while (cur != meetnode) {
		++count;

		cur = cur->_next;
	}

	return count;
}

求环的入口点

这个问题值得好好思考,的确有点意思,你是一眼无法找出解题思路的

SListNode* SListEntryNode(SListNode* pHead, SListNode* meetnode)
{
	assert(pHead || meetnode);

	SListNode* tail = pHead, *end = meetnode;

	while (tail != end) {
		tail = tail->_next;
		end = end->_next;
	}

	return tail;
}

判断两个链表是否相交,若相交,求交点。(假设链表不带环)

问题分析:

SListNode* SListIsCrossNode(SListNode* list1, SListNode* list2)
{
	assert(list1 || list2);

	size_t len1 = 1, len2 = 1;  //遍历链表时少遍历了一个节点,所以初始长度直接给成1

	SListNode* end1 = list1;
	SListNode* end2 = list2;

	while (end1->_next != NULL) {  //求链表 list1 的长度并记录最后一个节点
		++len1;

		end1 = end1->_next;
	}

	while (end2->_next != NULL) {  //求链表 list2 的长度并记录最后一个节点
		++len2;

		end2 = end2->_next;
	}

	if (end1 != end2)  //链表的最后一个节点不相等,说明二者没有相交,返回一个空指针
		return NULL;

	SListNode* longlist = list1;
	SListNode* shortlist = list2;

	if (len1 < len2)  //确定两个链表的长短情况
	{
		longlist = list2;
		shortlist = list1;
	}

	int gap = abs((int)(len1 - len2));  //求两个链表的长度差

	while (gap--) {  //先让较长的链表走 gap 步
		longlist = longlist->_next;
	}

	while (shortlist != longlist) {  //再让两个指针同时走,两个指针相遇时,他们指向的就是链表的交点了
		shortlist = shortlist->_next;
		longlist = longlist->_next;
	}
	
	return shortlist;
}

判断两个链表是否相交,若相交,求交点。(假设链表可能带环) 【升级版】

问题分析:这时候分的情况是比较多的,先理解清楚各种相交组合是非常重要的

解决这道题整体思路应该是先分清楚是那种组合方式,再针对不同的组合方式提供方法 

下面提供的代码量相对比较大,整体的思路是这样的:

  1. 先判断出两个链表是否带环,有三种情况:两个链表都不带环、两个链表中有且只有一个带环、两个链表都带环,可以说这三种情况将问题分解成了三大块
  2. 两个链表都不带环:这个问题刚好是上一题中我们解决了的问题,在这里直接复用就行,这一大块就这么轻松地解决了
  3. 两个链表中有且只有一个链表带环:我们说过了,这种情况是不可能相交的,也就是说是没有交点的,直接返回一个NULL,这一大块也就这么轻松地解决了
  4. 两个链表都带环:这种情况是这里最复杂的一个块,这里有两种情况:两个带环链表相交、两个链表不想交(关于如何判断相交代码部分注释特别清楚,这里不再重复)
  • 两个带环链表不相交:既然不想交,那么谈不上什么交点了,直接返回NULL就行了
  • 两个带环链表相交:这种情况又分为两种情况:环的入口点一样、环的入口点不一样(关于如何求出这两种情况下的交点,代码部分注释特别清楚,这里不再重复)

这个题目的确是按上面所说的那样步步实现的,关于实现的细节直接看代码即注释

SListNode* SListCrossNode(SListNode* pHead, SListNode* pHead2)
{
	assert(pHead || pHead);

	//通过判断,首先分析出两个链表是否带环

	SListNode* list1 = pHead, *list2 = pHead2;

	//注意判断链表是否带环的函数:链表带环,返回快慢指针环内相遇节点;链表不带换,返回NULL
	SListNode* meetnode1 = SListIsCycle(list1);  //判断链表1是否带环
	SListNode* meetnode2 = SListIsCycle(list2);  //判断链表2是否带环

	if (meetnode1 == NULL && meetnode2 == NULL)  //两个链表都不带环的情况
	{
		//这里直接调用链表不带环的情况下,实现判断两个链表是否相交,如果相交,求交点的函数
		//返回值:如果相交,返回相交的节点;如果不想交,返回NULL
		SListNode* crossnode = SListIsCrossNode(list1, list2);

		if (crossnode != NULL)  //说明两个链表相交,我们返回相交的节点
		{
			return crossnode;
		}
		else  //说明两个链表不相交,我们返回一个NULL
		{
			return NULL;
		}
	}

	if(meetnode1 != NULL && meetnode2 != NULL)  //两个链表都带环的情况
	{
		//首先判断两个带环的链表是否相交

		//思路:如果两个带环的链表相交,则两个链表必定有公共的环,我们只需记录下一个链表环内的一个节点,
		//然后遍历另一个链表的环,看是否有与另一个链表环内记录下的节点属性完全相同的节点,如果有,两个带环
		//链表相交,如果没有,两个带环链表不相交

		size_t lenlist2 = SListCycleLen(list2);  //求链表2的环长
		SListNode* cur = meetnode2;  //链表2环内的一个节点(判断是否带环时快慢指针的相遇点)

		while (lenlist2--) {
			if (meetnode1 == cur)  //说明两个带环链表相交,下一步是求交点
			{
				//走到这里两个带环链表已经相交了,我们的任务是求交点
				
				//先求出两个带环链表的环入口点
				SListNode* crossnode1 = SListEntryNode(list1,meetnode1);
				SListNode* crossnode2 = SListEntryNode(list2,meetnode2);

				//这里两个链表环的入口点有可能一样,也有可能不一样,继续分类
				if (crossnode1 == crossnode2)  //两个链表环的入口点一样
				{
					//这时候只要从两个带环链表头到链表环入口点之间寻找出交点即可
					//(这种情况下环内所有节点都当做二者的交点)

					size_t len1 = 1, len2 = 1;
					SListNode* cur1 = list1, *cur2 = list2;

					while (cur1 != crossnode1) {  //求出链表1从链表头到环入口的距离  
						++len1;
						cur1 = cur1->_next;
					}
					while (cur2 != crossnode2) {  //求出链表2从链表头到环入口的距离
						len2++;
						cur2 = cur2->_next;
					}

					int gap = abs((int)(len1-len2));  //求出长度差

					cur1 = list1;  //让两个指针重新指向链表头
					cur2 = list2;

					if (len1 > len2)  //先让较长的先走gap步
					{
						while (gap--) {
							cur1 = cur1->_next;
						}
					}
					if (len1 < len2)
					{
						while (gap--) {
							cur2 = cur2->_next;
						}
					}

					while (cur1 != cur2)  //现在让两个指针同步走,二者相等时指向的就是交点
					{
						cur1 = cur1->_next;
						cur2 = cur2->_next;
					}

					return cur1;
				}
				else  //两个链表环的入口点不一样
				{
					//这种情况下任何一个带环单链表环的入口即是两个带环单链表的交点(交点有可能很多,至少两个)
					return crossnode1;  //return crossnode2;也行
				}
			}

			cur = cur->_next;
		}

		//走到这里说明两个带环链表不相交,直接返回一个NULL
		return NULL;
	}

	//走到这里的情况只有一种,就是一个链表不带环,一个链表带环,我们直接返回一个NULL
	return NULL;
}

复杂链表的复制。一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表。 

问题分析:根据题目的描述,先给出这个链表节点的结构

struct ComplexListNode
{
         int _data;  //数据域
         ComplexListNode* _next;  //指向下一个节点
         ComplexListNode* _random;  //指向链表中一个随机的节点或者NULL
};

这个问题算是链表这块比较复杂的题目了,我们将整个过程分成三步解决这个问题

先按如下方式复制节点

现在来置那些新插入的节点的 _random 指针

最后一步是拆复制的链表并回复旧链表

这里我提供一个完整的代码,从建立链表到复制链表再到对我们复制链表算法的测试

typedef struct ComplexListNode
{
	int _data;
	ComplexListNode* _next;
	ComplexListNode* _random;
}ComplexListNode;

ComplexListNode* BuyComplexListNode(DataType x)  //申请节点并初始化
{
	ComplexListNode* node = new ComplexListNode;
	assert(node);

	node->_data = x;
	node->_next = NULL;
	node->_random = NULL;

	return node;
}

void ComplexListPushBack(ComplexListNode** pHead,DataType x)
{
	ComplexListNode* newnode = BuyComplexListNode(x);

	if (*pHead == NULL)
	{
		*pHead = newnode;
		return;
	}

	ComplexListNode* cur = *pHead;
	ComplexListNode* prev = NULL;
	while (cur) {
		prev = cur;

		cur = cur->_next;
	}

	prev->_next = newnode;
}

ComplexListNode* ComplexListFind(ComplexListNode* pHead,DataType x)
{
	assert(pHead);

	ComplexListNode* cur = pHead;
	while (cur) {
		if (cur->_data == x)
			return cur;

		cur = cur->_next;
	}

	return NULL;
}

void ComplexListPrint(ComplexListNode* pHead)
{
	assert(pHead);

	ComplexListNode* cur = pHead;
	while (cur){
		printf("%d ",cur->_data);

		cur = cur->_next;
	}

	printf("\n");
}


ComplexListNode* CopyComplexList(ComplexListNode* pHead)
{
	assert(pHead);

	//复制节点
	ComplexListNode* cur = pHead;
	while (cur) {
		ComplexListNode* next = cur->_next;

		ComplexListNode* newnode = BuyComplexListNode(cur->_data);

		cur->_next = newnode;
		newnode->_next = next;

		cur = next;
	}

	//置 _random 指针
	ComplexListNode* prev = pHead;
	ComplexListNode* next = prev->_next;

	while (next != NULL) {
		if (prev->_random == NULL)
			next->_random = NULL;
		else
			next->_random = prev->_random->_next;

		prev = next;
		next = next->_next;
	}

	//拆复制的链表并回复旧链表
	ComplexListNode* newhead = pHead->_next;

	ComplexListNode* cur1 = pHead;
	ComplexListNode* cur2 = newhead;

	while (cur2->_next != NULL) {
		cur1->_next = cur2->_next;
		cur1 = cur1->_next;

		cur2->_next = cur1->_next;
		cur2 = cur2->_next;

		if (cur2->_next == NULL)  //最后需将旧链表的最后一个节点的 _next 指向给NULL
			cur1->_next = NULL;
	}

	return newhead;
}
void test()
{
	ComplexListNode* l = NULL;

	ComplexListPushBack(&l, 1);
	ComplexListPushBack(&l, 2);
	ComplexListPushBack(&l, 3);
	ComplexListPushBack(&l, 4);
	ComplexListPrint(l);

	ComplexListNode* p1 = ComplexListFind(l, 1);
	ComplexListNode* p2 = ComplexListFind(l, 2);
	ComplexListNode* p3 = ComplexListFind(l, 3);
	ComplexListNode* p4 = ComplexListFind(l, 4);

	//按图示置_random
	p1->_random = p3;
	p2->_random = NULL;
	p3->_random = p2;
	p4->_random = p2;

	ComplexListPrint(CopyComplexList(l));
}

求两个已排序单链表中相同的数据。void UnionSet(Node* l1, Node* l2)

问题分析:这个题目是比较简单的,你可以这样做,先比较链表1和链表2的第一个节点值是否相等,如果相等我们就找到了一个相同的数据,让指向两个链表的指针同时往后走;如果不相等,让指向第一个节点值小的那个链表的指针往后走一步,继续进行比较,然后做类似以上的处理,直到其中的一个链表遍历完为止

size_t UnionSet(SListNode* l1, SListNode* l2,DataType* _a) //数组 _a 的长度等于较短链表的长度
{
	assert(l1 || l2);

	SListNode* list1 = l1;
	SListNode* list2 = l2;
	size_t index = 0;

	while (list1 && list2) {
		if (list1->_data = list2->_data)
		{
			_a[index++] = list1->_data;

			list1 = list1->_next;
			list2 = list2->_next;
		}
		else if (list1->_data > list2->_data)
		{
			list2 = list2->_next;
		}
		else
		{
			list1 = list1->_next;
		}
	}

	return index-1;  //最终返回有效下表的最大值
}

这些面试题就说到这里,个人觉得说的算是比较详细了,难免解题过程中有遗漏的地方,或者你有更优的解题思路,遇到这类问题有心的朋友可以和我再次交流,我的QQ号(3166375975)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值