链表中一些有趣的小题目总结
1、单链表中间结点
问:如何快速查找定位到一条单链表的中间结点?
答:(1)、可以将链表中的所有结点遍历一遍得到总个数,再查找到中间结点(这不废话嘛,这么low的答案就不拉出来献丑了=、=)
(2)、可以使用快慢指针法,一个指针一倍速率遍历,另一个指针两倍速率遍历链表,当第一个走到末尾时第二个则刚好在中间位置
代码:
NODE *Search(NODE *pHead) { NODE *fast = pHead, *slow = pHead; while(fast) { if (fast->pNext == NULL) fast = fast->pNext; else fast = fast->pNext->pNext; slow = slow->pNext; } return slow; }
2、链表中是否有环
问:如何判断一个单链表中是否有环存在,如果存在则找出环的入口(即链表环的)
答:还是用快慢指针的方式,将快指针二倍速率遍历链表,用慢指针一倍速率遍历链表,分别记录快慢指针走的步数,如果两个指针相遇,且步数不一致则表示有环,否则无环。
代码:
int IsRing(NODE *pHead, NODE **pEnter) { int a = 0, b = 0; NODE *pFast = pHead, *pSlow = pHead; while(pFast != NULL && pFast->pNext != NULL) { pFast = pFast->pNext->pNext; pSlow = pSlow->pNext; a++; b += 2; if (pFast == pSlow && a != b){ pFast = pHead; while(pFast != pSlow){ pFast = pFast->pNext; pSlow = pSlow->pNext; } *pEnter = pSlow; return 1; } } *pEnter = NULL; return 0; }
3、判断两个单链表是否相交
问:如何判断两个单链表是否相交,如果相交则给出第一个相交的点
答:(1)、将一个链表首尾相连,判断另一个链表中是否有环,如果有环,则两个链表相交,而链表的环入口就是第一个相交的点,如果没有环则两个链表不相交
代码:
int IsIntersection(NODE *pHead1, NODE *pHead2, NODE **intersec) { NODE *temp = pHead1; while(temp->pNext){ temp = temp->pNext; } temp->pNext = pHead1; return IsRing(pHead2, intersec); //上题中的函数 }
(2)、将一条链表从头遍历到尾,再判断第二条链表的尾结点是否和第一条相同,如果相同则相交。这时我们记下链表的长度。将两条链表重新遍历一次,此次遍历时较长链表的起点置为length(较长) – length(较短)的结点处,较短链表从头结点开始,当两个结点第一次相遇时该点就是相交的第一个点。
代码:
int IsIntersection(NODE *pHead1, NODE *pHead2, NODE **intersec) { int length1 = 0, length2 = 0, i, len; NODE *pTemp1 = pHead1, *pTemp2 = pHead2; //第一条链表从头遍历到尾 while(pTemp1->pNext){ pTemp1 = pTemp1->pNext; length1++; } //第二条链表从头遍历到尾 while(pTemp2->pNext){ pTemp2 = pTemp2->pNext; length2++; } //如果相交则从新遍历 if (pTemp1 == pTemp2){ len = abs(length1 - length2); pTemp1 = pHead1; pTemp2 = pHead2; //将较长链表的遍历起始位置到lengthmax - lengthmin处 if (length1 > length2){ for (i = 0; i < len; i++){ pTemp1 = pTemp1->pNext; } } else { for (i = 0; i < len; i++){ pTemp2 = pTemp2->pNext; } } while(pTemp1 != pTemp2){ pTemp1 = pTemp1->pNext; pTemp2 = pTemp2->pNext; } *intersec = pTemp1; return 1; } *intersec = NULL; return 0; }
4、在平均时间复杂度为O(1)内删除给定的结点
问:在时间复杂度为O(1)的范围内删除给定的结点。
答:采用“偷梁换柱法”,将待删除结点后边的那个结点的值赋值待删除结点,再将待删除结点后边的结点删除。当然此方法只能用于非尾结点,如果是尾结点则只能通过遍历找到尾结点的前一个结点,此时的平均时间复杂度为:[(n-1)*O(1)+O(n)]/n
= O(1)
代码:
void DelNode(NODE *pHead, NODE *pDel) { NODE *pTemp = pHead, *pTar; if (pDel == pHead){ printf("不能删除头结点\n"); return; } if (pDel->pNext != NULL){ pDel->data = pDel->pNext->data; pTar = pDel->pNext; pDel->pNext = pTar->pNext; } else { while(pTemp->pNext != pDel){ pTemp = pTemp->pNext; } pTar = pTemp->pNext; pTemp->pNext = pTar->pNext; } free(pTar); }
5、求链表倒数第k个结点
问:求单链表的倒数第k个结点(尾结点是倒数第一个结点)
答:两个指针p1和p2,p1,p2指向首结点,再将p1向后移动k步,将p1和p2同时向后移,移动到p1为最后一个结点时p2所在位置就是倒数第k个结点。
代码:
NODE *ListNode(NODE *pHead, int k) { int i = 0; NODE *p1 = pHead, *p2 = pHead->pNext; if (k < 0) return NULL; while(p1 != NULL && i < k){ p1 = p1->pNext; i++; } if (p1 != NULL){ while(p1->pNext != NULL){ p1 = p1->pNext; p2 = p2->pNext; } return p2; } else{ return NULL; } }