1.如何判斷兩個單鏈表是否相交,如果相交,找出交點(兩個鏈表都不存在環)
如果兩個單鏈表相交,那應該呈“Y”字形,則交點之後的節點是相同的。
所以判斷是否相交,只需看兩個鏈表的最後一個節點是否爲同一個即可。
假設兩個單鏈表的長度分別爲L1、L2(L1 > L2),則(L1-L2)的值就是交匯之前兩個鏈表的長度差;
因此,只有讓更長的鏈表先走L1-L2步,然後兩個鏈表開始一起走,如果某次走到一個相同的節點,該節點即爲交點。
typedef struct ListNode{
int data;
struct ListNode *next;
}ListNode; //定義節點
static int GetListLength(ListNode *head)
{
int n=0;
while(head != NULL)
{
n++;
}
return n;
}
static ListNode * FindCommonNode(ListNode *a,ListNode *b)
{
int i;
int n1 = GetListLength(a);
int n2 = GetListLength(b);
//如果a<b,則互換a,b位置重新取長度
if(a<b)
{
return FindCommonNode(b,a);
}
for(i=0;i<n1-n2;i++) //較長的先走多出長度
{
a = a->next;
}
//相同長度時,同時向下走,指向相同時,則爲交點
while(a != NULL && a != b)
{
a = a->next;
b = b->next;
}
return a;
}
2.判斷一個鏈表是否有環,並找到環的入口點
如果一個單鏈表有環,那應該呈“6”字形或“O”形。
設置兩個指針(fast, slow),初始值都指向頭節點,slow每次前進一步,fast每次前進二步,如果鏈表存在環,則fast必定先進入環,而slow後進入環,兩個指針必定 相遇:如果鏈表是呈"O"字形,則slow剛好遍歷完一次的時候,與fast相遇;如果呈“6”字形,則更早相遇。
當fast若與slow相遇時,slow還沒有遍歷完鏈表,而fast已經在環內循環了n圈(1<=n)。假設slow走了s步,則 fast走了2s步(fast步數還等於s 加上在環上多轉的n圈),設環長爲r,則:
2s = s + nr,簡化爲 s= nr
s = x + y,x爲鏈表起點到環入口點的距離,y是slow在環內走過的距離;
可以得到 x = y - s = y - nr,從鏈表頭、相遇點分別設一個指針(p1, p2),每次各走一步,當p1走過距離x時到達入口點,而p2走過的距離爲y-nr,y是相遇點與入口點的距離,因此y也走到了入口點,也就是說p1、p2在環入口點相遇了。
//鏈表是否有環
static ListNode* FindLoopPort(ListNode* head)
{
ListNode* slow = head;
ListNode* fast = head;
//找相遇節點
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
break;
}
if(fast == NULL || fast->next ==NULL) //走完鏈表,無環
return NULL;
//有環時,此時fast在相遇點,slow設置爲起點,再相遇時,爲環入口點
slow = head;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
3.求一個單鏈表(無環)的中間節點
設置兩個指針(fast, slow),初始值都指向頭節點,slow每次前進一步,fast每次前進二步,當fast走到末尾時,slow剛好指向中間節點。
4.假如鏈表長度爲N,如何返回鏈表的倒數第K個結點
假設用兩個指針,指針P1先走K-1步,然後指針P2纔開始走,當指針P1遍歷完鏈表時,P2還剩K-1個結點沒有遍歷,此時,P2爲第K個節點,返回P2即可。
//求倒數第n個節點
ListNode *FindLastKNode(ListNode *Head, int n)
{
ListNode* p1=head;
ListNode* p2=head;
while(--n == 0 && p1 != NULL)
{
p1 = p1->next;
}
if(p1 == NULL) return NULL;
while(p1->next != NULL)
{
p2 = p2->next;
p1 = p1->next;
}
return p2;
}
5.如何反轉一個單鏈表(或逆序輸出)
static Node* ReverseList(Node* Head)
{
Node* pNode = Head;
Node* pNext = NULL;
Node* pPrev = NULL;
while (pNode)
{
pNext = pNode->next;
if (NULL == pNext)
{
Head = pNode;
}
pNode->next = pPrev; //斷開原本的鏈,反轉指向
pPrev = pNode; //將逆置後的頭,付給pPrev,pPrev始終爲逆置後鏈表的第一位
pNode = pNext; //將節點後推一位,循環反轉
}
return Head;
}
//遞歸實現逆置,此時默認爲有頭結點的鏈表
Node* reverselist1(Node* head)
{
static Node *h = head; //記錄頭結點
Node *c = NULL;
//當遍歷到鏈表結尾部分是,a爲倒數第二位,b爲倒數第一位
if (head == NULL || head->next == NULL)
return head;
c = reverselist1(head->next); //開始遞歸
if (head != h) //沒有返回到頭結點時,從最後一位依次逆轉指向,並把逆轉後的節點返回,繼續進行逆轉
{
head->next->next = head;
head->next = NULL;
return c;
}
else //返回到頭結點時,將已經全部逆置的鏈表附在頭結點後面,完成逆置。返回頭結點。
{
head->next = c;
return head;
}
}