单链表的题目:
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;
}
}
}