数据结构4-----线性表的链式存储结构(2)

单链表的题目:

1、单链表的反转

思路:借助三个指针p1,p2,p3。让p1=head;p2=p1->nect;p3=p2->next;之后将p2->next=p1;然后将p1,p2 ,p3顺序后移,一直到p3为空就会将链表反转。(特殊情况数组中只有没有或者只有一个节点时,直接返回就可以。)

void linkListReverse(linkList* head)
{
	if (NULL == (*head) || NULL == (*head)->next)
	{
		return;
	}
	NODE* p1 = (*head);
	NODE* p2 = p1->next;
	NODE* p3 = p2->next;
	p2->next = p1;
	while (p3)
	{
		p1 = p2;
		p2 = p3;
		p3 = p3->next;
		p2->next = p1;
	}
	(*head)->next = NULL;
	(*head) = p2;
}

2、寻找单链表中倒数第i个元素

思路:快慢指针,是块指针比慢指针多走i步,然后两个指针以相同的速度前进,当快指针走到链表尾部时,慢指针的位置就是倒数第i个节点的位置(特殊处理链表中不存在倒数第i个节点)。

NODE* backFindNode(linkList head, int i)
{
	NODE* pFast = head;
	NODE* pSlot = head;
	for (int j = 0; (pFast != NULL)&&j < i-1; ++j)
	{
		pFast = pFast->next;
	}
	if (NULL == pFast||i <= 0)
	{
		return NULL;
	}
	while (pFast->next)
	{
		pFast = pFast->next;
		pSlot = pSlot->next;
	}
	return pSlot;
}

3、找出单链表的中间元素

思路:快慢指针,快指针和慢指针的其实位置都是链表的头,快指针每次比慢指针多走一步,当快指针走到链表尾部时慢指针的位置就是链表的中间位置。(当链表元素为偶数个时,返回靠前的节点)

NODE* findLinkListMid(linkList head)
{
	if (NULL == head)
	{
		return NULL;
	}
	NODE* pFast = head;
	NODE* pSlot = head;
	while (pFast->next)
	{
		pFast = pFast->next;
		if (pFast->next)
		{
			pFast = pFast->next;
			pSlot = pSlot->next;
		}
	}
	return pSlot;
}

5、两个不交叉的有序链表的排序

思路:从链表2中依此取出元素,在链表1中寻找比该元素大的节点的前一个节点,将链表2中的节点插入到链表1中寻找的位置的后面。

会有两中特殊的情况:

(1)当前链表2中元素小于链表1的头,需要头插。

(2)当前链表2中的元素大于链表1中元素的最后一个,将2中剩余元素直接插到链表1的后面,结束。

注意,每次执行完插入操作后需要将链表1中寻找的位置移动到刚才位置上。

linkList mergeLinkList(linkList* head1, linkList* head2)
{
	if (NULL == (*head1))
	{
		return *head2;
	}
	if (NULL == (*head2))
	{
		return *head1;
	}
	//(1)从2中取出第一个节点(w)
	//(2)在1中存在第一个比此值的大的元素(s)
	//(3)将此节点(w)插入到1中节点(s)的前面(循环2)
	//如果到了1的链尾部,将2中剩余元素全部插入到1的后面
	NODE* p1 = (*head1);
	NODE* pFront = NULL;//s的前一个位置
	NODE* tNode = NULL;//w
	while ((*head2))
	{
		//将节点从链表2上取下
		tNode = (*head2);
		//(2)
		while ((p1 != NULL) && (tNode->data >= p1->data))
		{
			pFront = p1;
			p1 = p1->next;
		}
		if (NULL == p1)
		{
			pFront->next = (*head2);
			break;
		}
		else if(NULL == pFront)
		{
			//头插
			(*head2) = (*head2)->next;
			tNode->next = (*head1);
			(*head1) = tNode;
			//链表二中可能存在介于新插入的数(w)和(s)之间的数
			p1 = *head1;
		}
		else
		{
			(*head2) = (*head2)->next;
			tNode->next = pFront->next;
			pFront->next = tNode;
			//链表二中可能存在介于新插入的数(w)和(s)之间的数
			p1 = pFront;
		}
	}
	return (*head1);
}

6、单链表交换任意两个位置的元素。

思路:寻找需要交换元素的位置和需要交换元素的前一个节点,将需要交换的两个从链表上拆下来,之后将这两个元素交错的插入刚才寻找到的前一个节点的位置。(特殊处理:如果两个节点的位置相邻,这时较小的元素就会称为较大(位置)元素的前驱节点,从链表上拆下时就会时较大的元素的位置丢失,这是需要重新寻找)

void swapLinkList(linkList* head, int i, int j)
{
	if (i == j||i<0||j<0)
	{
		return;
	}
	//找出两个中位置较小的
	int min = i < j ? i : j;
	//找出两个中位置较大的
	int max = i < j ? j : i;
	NODE* pMax = getPrePos(*head,max);
	//最后一个无后继
	if (NULL == pMax||NULL==pMax->next)
	{
		return;
	}
	NODE* Max = pMax->next;
	pMax->next = Max->next;
	NODE* pMin = getPrePos(*head, min);
	if (NULL == pMin&&i != 0)
	{
		return;
	}
	NODE* Min;

	//较小的数为0,需要特殊处理
	if (0!=min)
	{
		Min = pMin->next;
		pMin->next = Min->next;
	}
	else
	{
		Min = pMin;
		(*head) = (*head)->next;
	}

	if (0 == min)
	{
		Max->next = (*head);
		(*head) = Max;
	}
	else
	{
		Max->next = pMin->next;
		pMin->next = Max;
	}
	if (max - min == 1)
	{
		//两个相邻会丢失较大的数的前驱,需要重新寻找
		pMax = getPrePos(*head, max);
	}
	Min->next = pMax->next;
	pMax->next = Min;
}

7、判断单链表是否有环?如何找到环的“起始”节点,并计算环的长度?

思路:快慢指针,起始位置都是链表的头步,让快指针比慢指针每次多走一步,如果快指针走到链表的尾部,则无环,如果快指针和慢指针相遇就说明有环。当确定链表中存在环时,将相遇的两个指针的任意一个重新置回到链表的头步,然后两个指针以每次一步的速度移动,当两个节点再次相遇时,所在的节点就是入环点。计算长度时,设置一个整形变量初始化为0,让一个指针不动另一个指针每次移动一步,就让记录长度的变量加1,当两个节点再次相遇的时候,整形变量中记录的长度就是,环的长度。

创建一个有环的节点

void ringLinkList(linkList* head)
{
	NODE* p = *head;
	while (p->next)
	{
		p = p->next;
	}
	p->next = (*head)->next;
}

判断是否又环,并计算入环点和环的长度。

bool judgeLinkListRing(linkList head)
{
	if (NULL == head)
	{
		return NULL;
	}
	NODE* pFast = head;
	NODE* pSlot = head;
	while (pFast->next&&(pFast!=pSlot||(pFast==head&&pSlot==head)))
	{
		pFast = pFast->next;
		if (pFast->next)
		{
			pFast = pFast->next;
			pSlot = pSlot->next;
		}
	}
	if (NULL == pFast->next)
	{
		return false;
	}
	
	int i = 1;
	pSlot = pSlot->next;
	while (pFast != pSlot)
	{
		pSlot = pSlot->next;
		++i;
	}
	pSlot = head;
	while (pFast != pSlot)
	{
		pFast = pFast->next;
		pSlot = pSlot->next;
	}
	printf("%d,i=%d\n", pFast->data,i);
	return true;
	
}

8、单链表的排序

思路:插入排序,因为链表并不支持随机存取,所以在操作上会比较麻烦。

/*
但这个算法受限于单链表的一些特性会有些改动。
因为单链表是没有前驱的,所以要和前面一个相比就会比较麻烦。
所以我们可以每遇到一个新节点,如果他的值比前个节点的值大,就不动。
否则就将这个节点从链表上拆下,放到合适的位置。
*/
void sortLinkList(linkList* head)
{
	//只用0个或1个不需要排序
	if (NULL == (*head) || NULL == (*head)->next)
	{
		return;
	}
	NODE* pFront = (*head);
	//第一个节点前无元素
	NODE* pHead = (*head)->next;
	while (pHead)
	{
		//寻找第一个数据值小于前驱的节点
		while ((NULL != pHead) && (pHead->data >= pFront->data))
		{
			pFront = pHead;
			pHead = pHead->next;
		}
		if (NULL == pHead)
		{
			//后面的元素全部大于前面的元素,有序
			break;
		}
		else if (pFront == (*head))
		{
			//pFront指向了头节点,说明第二个元素小于第一个元素需要头插
			//将头节点指向它的下一个
			pFront->next = pHead->next;
			//头插之后将前驱指针重新置到第二个位置
			pFront = (*head);
			pHead->next = (*head);
			(*head) = pHead;
			pHead = pFront->next;
		}
		else
		{
			//后面节点的值小于前面节点的值
			//将pHead拆出来
			pFront->next = pHead->next;
			//从节点头开始寻找第一个比该节点值大的前驱节点位置
			if ((*head)->data > pHead->data)
			{
				pHead->next = (*head);
				(*head) = pHead;
			}
			else
			{
				NODE* Head = (*head);
				//寻找第一个大于当前数据值的节点
				while ((Head->next) && Head->next->data<pHead->data)
				{
					Head = Head->next;
				}
				pHead->next = Head->next;
				Head->next = pHead;
			}
			//前驱的位置不会改变
			pHead = pFront->next;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_39038983/article/details/86554711
今日推荐