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;
}
}