單鏈表的相關面試題
一、比較順序表與鏈表的優缺點:
單鏈表:它是一種鏈式存儲的線性表,用一組地址任意的存儲單元存放線性的的數據元素,稱存儲單元爲一個節點。
順序表:採用順序存儲結構的線性表通常稱爲順序表。
線性表的順序存儲結構:指用一組地址連續的存儲單元依次存儲線性表中的各個元素,使得線性表中在邏輯結構上相鄰的數據元素存儲在相鄰的物理存儲單元中,即通過數據元素物理存儲的相鄰關係來反映數據元素之間的邏輯上的相鄰關係。
線性表優點:
因爲邏輯上相鄰的元素其存儲的物理位置也是相鄰的,所以無需爲表示結點間的邏輯關係而增加額外的存儲空間,並且具有可以方便的隨機存取表中任一元素。
線性表缺點:
1、插入或刪除運算不方便,除表尾的位置外,在表的其他位置上進行插入或者刪除操作都必須移動大量的結點,其效率非常低。
2、由於順序表要求佔用連續的存儲空間,存儲分配只能預先進行靜態分配。因此,當表的長度變化較大時,難以確定合適的存儲規模。若按照可能達到的最大長度預先分配表空間,則可能造成一部分空間長期閒置而得不到充分利用;若事先對錶的長度估計不足,則插入操作可能使表長超過預先分配的空間而造成溢出。
1、順序表支持隨機訪問,單鏈表不支持隨機訪問。
2、順序表插入/刪除數據效率很低,時間複雜度爲O(N)(除尾插尾刪),單鏈表插入/刪除效率更高,時間複雜度爲O(1)。
3、順序表的CPU高速緩存效率更高,單鏈表CPU高速緩存效率低。
//單鏈表頭插
void
PushFront(pSListNode *pHead
, DataType
data)
{
assert(
pHead);
if
(pHead
== *pHead)
{
*phead = ByeNode(
data);
}
else
{
pSListNode pNode = ByeNode(
data);
pNode->pNext = *
pHead;
*
pHead
= pNode;
}
}
//單鏈表頭刪
void
PopFront(pSListNode *pHead
)
{
assert(
pHead
!= NULL);
if
(*pHead
== NULL)
{
return;
}
else
{
pSListNode pNode = *
pHead;
*
pHead
= pNode->pNext;
free(pNode);
}
}
//單鏈表查找一個結點
pSListNode Find(pSListNode
pHead, pSListNode
Pos, DataType
data
)
{
pSListNode pNode =
pHead;
while
(pNode)
{
if
(pNode->data ==
data
)
{
return
pNode;
}
pNode = pNode->pNext;
}
return
NULL;
}
//刪除單鏈表的某個結點
void
Erase(pSListNode *pHead
, pSListNode
Pos)
{
pSListNode pPrePos = *
pHead;
assert(NULL !=
pHead);
assert(NULL !=
Pos);
if
(Pos
== *pHead)
{
*
pHead
=
Pos
->pNext;
free(
Pos);
return;
}
while
((pPrePos != NULL) && (pPrePos->pNext !=
Pos))
{
pPrePos = pPrePos->pNext;
}
if
(pPrePos->pNext ==
Pos
)
{
pPrePos->pNext =
Pos->pNext;
free(
Pos);
}
}
//單鏈表插入
void
Insert(pSListNode *pHead
, pSListNode
Pos, DataType
data)
{
pSListNode pNewNode = NULL;
assert(NULL !=
pHead);
assert(
Pos
!= NULL);
if
(*pHead
== NULL)
return;
pNewNode = ByeNode(
data);
if
(NULL != pNewNode)
{
pNewNode->pNext =
Pos->pNext;
Pos->pNext = pNewNode;
}
}
//正向打印單鏈表
void
PrintList(pSListNode
pHead
)
{
pSListNode pCurNode =
pHead;
while
(pCurNode)
{
printf(
"%d->", pCurNode->data);
pCurNode = pCurNode->pNext;
}
printf(
"NULL\n");
}
//逆向打印單鏈表
void
PrintListTailToHead(pSListNode
pHead
)
{
if
(NULL !=
pHead
)
{
PrintListTailToHead(
pHead->pNext);
printf(
"%d->",
pHead
->data);
}
}
//銷燬單鏈表
void
DestroyList(pSListNode *pHead
)
{
pSListNode pNode = NULL;
pSListNode pPreNode = NULL;
assert(
pHead
!= NULL);
pNode = *
pHead;
while
(pNode)
{
pPreNode = pNode;
pNode = pNode->pNext;
}
free(pPreNode);
*
pHead
= NULL;
}
//刪除一個無頭單鏈表的非尾結點
void
DelNotTailNode(pSListNode
Pos
)
{
pSListNode pDelNode = NULL;
assert(
Pos
!= NULL);
assert(
Pos->pNext != NULL);
pDelNode =
Pos->pNext;
Pos->data = pDelNode->data;
Pos->pNext = pDelNode->pNext;
free(pDelNode);
}
//在無頭單鏈表的一個非頭結點前插入一個結點
void
InsertNotHead(pSListNode
Pos
, DataType
data)
{
pSListNode pNewNode = NULL;
assert(
Pos
!= NULL);
pNewNode = ByeNode(
Pos->data);//新結點保存的是舊結點的值
if
(pNewNode != NULL)
{
pNewNode->pNext =
Pos->pNext;
Pos->pNext = pNewNode;
Pos->data =
data
;
}
}
//查找單鏈表的中間結點,要求只能遍歷一次鏈表
pSListNode FindMidNode(pSListNode
pHead)
{
pSListNode pSlow =
pHead;
pSListNode pFast =
pHead;
while
((pFast != NULL) && (pFast->pNext != NULL))
{
pSlow = pSlow->pNext;
pFast = pFast->pNext->pNext;
}
return
pSlow;
}
//單鏈表實現約瑟夫環
pSListNode JosephCircle(PsListNode
pHead,
int
M)
{
pSListNode pNode =
pHead;
pSListNode pDelNode =
pHead;
int
k =
M
;
if
((pHead
== NULL) || (M
<= 0))
{
return
NULL;
}
while
(pNode->pNext != pNode)
{
k =
M;
while
(--k)
{
pNode = pNode->pNext;
}
pDelNode = pNode->pNext;
pNode->data = pDelNode->data;
pNode->pNext = pDelNode->data;
free(pDelNode);
}
return
NULL;
}
//逆置與反轉單鏈表
//頭插法
void
ReverseList(pSListNode *pHead
)
{
pSListNode pNode = NULL;
pSListNode pPreNode = NULL;
pSListNode pNewNode = NULL;
assert(
pHead
!= NULL);
if
((*pHead
== NULL)||(*pHead)->pNext==NULL)
{
return;
}
pNode = *
pHead;
while
(pNode)
{
pPreNode = pNode;
pNode = pNode->pNext;
pPreNode->pNext = pNewNode;
pNewHead = pPreNode;
}
*
pHead
= pNewNode;
}
//單鏈表冒泡排序法
void
BubbleSortList(pSListNode
pHead
)
{
pSListNode pTailNode = NULL;
pSListNode pNode = NULL;
pSListNode pPreNode = NULL;
if
(pHead
== NULL)
{
return;
}
//外循環:控制循環次數
//內循環:實現冒泡
while
(pHead
!= pTailNode)
{
pNode =
pHead;
while
(pNode->pNext != pTailNode)
{
pPreNode = pNode;
pNode = pNode->pNext;
if
(pPreNode->data > pNode->data)
{
DataType tmp = preNode->data;
pPreNode->data = pNode->data;
pNode->data = tmp;
}
}
pTailNode = pNode;
}
}
//合併兩個有序單鏈表,且新鏈表也爲有序單鏈表-------尾插法
pSListNode MergeList(pSListNode
pList1, pSListNode
pList2
)
{
pSListNode pNewHead = NULL;
pSListNode pL1Node =
pList1;
pSListNode pL2Node =
pList2;
pSListNode pNode = NULL;
pSListNode pTailNode = NULL;
//判斷兩個單鏈表是否爲空
if
(pL1Node == NULL)
{
return
pL2Node;
}
if
(pL2Node == NULL)
{
return
pL1Node;
}
//兩個單鏈表都不爲空時比較兩個單鏈表中的結點數據的大小,找出較小值賦給新頭結點,然後在新頭結點後面進行尾插
if
(pL1Node->data > pL2Node->data)
{
pNode = pL2Node;
pL2Node = pL2Node->pNext;
}
else
{
pNode = pL1Node;
pL1Node = pL1Node->pNext;
}
pNewHead = pNode;
pTailNode = pNode;
//判斷兩個鏈表中是否有一個鏈表結束
while
(pL1Node && pL2Node)
{
if
(pL1Node->data > pL2Node->data)
{
pNode = pL2Node;
pL2Node = pL2Node->pNext;
}
else
{
pNode = pL1Node;
pL1Node = pL1Node->pNext;
}
pTailNode->pNext = pNode;
pTailNode = pTailNode->pNext;
}
//鏈表L1結束,則將L2直接鏈在尾結點的下一個位置上。否則反之。
if
(pL1Node == NULL)
{
pTailNode->pNext = pL2Node;
}
else
{
pTailNode->pNext = pL1Node;
}
return
pNewHead;
}
//查找單鏈表中的倒數第k個結點,要求只能遍歷一次鏈表
pSListNode FindLastkNode(pSListNode
pHead,
int
k)
{
pSListNode pFast =
pHead;
//使用快慢指針,快指針先走k-1步之後,慢指針(走一步)再與快指針同時走,最後返回慢指針即是倒數第k個結點。
pSListNode pSlow =
pHead;
if
((pHead
== NULL) || (k
<= 0))
{
return
NULL;
}
//先讓快指針走k-1步
while
((--k
) && pFast)
//可改爲while(k--),則下面的判斷k則不需要了,且線面的while(pFast->pNext)也要對應改爲while(pFast)。
{
pFast = pFast->pNext;
}
//判斷k是否大於結點數目
if
(k
)
{
return
NULL;
}
while
(pFast->pNext)
{
pSlow = pSlow->pNext;
pFast = pFast->pNext->pNext;
}
return
pSlow;
}
//判斷一個鏈表是否帶環?若帶環,求環的長度。
//可以使用快慢指針,快指針走兩步,慢指針走一步,若相遇,則帶環。
int
HasCycle(pSListNode
pHead
)
{
pSListNode pFast =
pHead;
pSListNode pSlow =
pHead;
if
(pHead
== NULL)
{
return
-1;
}
while
(pFast && pFast->pNext)
{
pFast = pFast->pNext->pNext;
pSlow = pSlow->pNext;
if
(pSlow == pFast)
{
return
1;//有環
}
else
{
return
-1;
}
}
}
//求環的長度
int
GetCycleLen(pSListNode
pMeetNode
)
{
int
len = 1;//千萬注意
pSListNode pNode =
pMeetNode;
if
(pMeetNode
== NULL)
{
return
0;
}
while
(pNode->pNext !=
pMeetNode
)
{
len++;
}
return
len;
}
//求環的入口點
//定義兩個指針,一個指針從pMeetNode結點開始走,一個指針從頭結點開始走,兩個指針相遇的地方就是環的入口點。
pSListNode FindEnterNode(pSListNode
pHead, pSListNode
pMeetNode
)
{
pSListNode pNode1 =
pHead;
pSListNode pNodeM =
pMeetNode;
if
((pHead
== NULL) || (pMeetNode
== NULL))
//pMeetNode爲空,即此單鏈表無環。
{
return
NULL;
}
while
(pNode1 != pNodeM)
//兩指針不相等時指針繼續往下走,直到兩指針相等跳出循環。
{
pNode1 = pNode1->pNext;
pNodeM = pNodeM->pNext;
}
return
pNode1;
}
//判斷兩個指針是否相交?
//方法一:相交的情況有Y型和V型等情況,若兩個鏈表相交,則必定有相等的結點,或是從某個結點之後的所有結點都相等,或是最後一個結點相等,所以我們只需判斷兩個鏈表的最後一個結點是否相等即可。
//方法二:我們可以先求出兩個鏈表的長度,然後定義兩個指針,長度較長的鏈表指針爲p1,長度爲L1,另一個鏈表指針爲p2,長度爲L2,先讓p1走上L1-L2步,再讓p1與p2同時往下走,若p1=p2,則兩個鏈表相交。
//方法一實現:
int
IsListCrose(pSListNode
pL1
, pSListNode
pL2)
{
pSListNode pL1Node =
pL1;
pSListNode pL2Node =
pL2;
if
((pL1
== NULL) || (pL2
== NULL))
{
return
0;
}
while
(pL1Node->pNext != NULL)
{
pL1Node = pL1Node->pNext;
}
while
(pL2Node->pNext != NULL)
{
pL2Node = pL2Node->pNext;
}
if
(pL1Node == pL2Node)
{
return
1;
}
return
0;
}
//方法二實現:
int
LengthList(pSListNode
pHead
)
{
pSListNode pNode = NULL;
int
Len = 0;
pNode =
pHead->pNext;
while
(pNode != NULL)
{
Len++;
pNode = pNode->pNext;
}
return
Len;
}
int
IsListCrose(pSListNode
P1
, pSListNode
P2)
{
int
L1 = 0;
int
L2 = 0;
int
K = 0;
pSListNode pL1Node =
P1;
pSListNode pL2Node =
P2;
if
((P1
== NULL) || (P2
== NULL))
{
return
0;
}
L1=LengthList(
P1);
L2=LengthList(
P2);
K = L1 - L2;
if
(K>0)
{
while
(K--)
{
pL1Node = pL1Node->pNext;
}
while
(pL1Node)
{
pL1Node = pL1Node->pNext;
pL2Node = pL2Node->pNext;
}
if
(pL1Node == pL2Node)
{
return
1;
}
}
else
{
while
(K++)
{
pL2Node = pL2Node->pNext;
}
while
(pL2Node)
{
pL1Node = pL1Node->pNext;
pL2Node = pL2Node->pNext;
}
if
(pL1Node == pL2Node)
{
return
1;
}
}
return
0;
}
//若兩個鏈表可能相交,求他們的交點。
//相交有三種情況:
//(1)兩個鏈表都不帶環。
//(2)兩個鏈表都帶環。分別是環外相交與環內相交。
//(3)只有一個帶環。此種情況不需要管。
//相交時兩個鏈表公共一個環。
//設M1與M2分別是兩個鏈表與環的交點,若兩個鏈表相交,則M1與M2都在環內。
int
IsListCroseWithCycle(pSListNode
pL1, pSListNode
pL2)
{
pSListNode pL1MeetNode = NULL;
pSListNode pL2MeetNode = NULL;
pSListNode pL1Node =
pL1;
pSListNode pL2Node =
pL2;
if
((pL1
== NULL) || (pL2
== NULL))
{
return
0;
}
//判斷兩個鏈表是否帶環
pL1MeetNode = HasCycle(
pL1);
pL2MeetNode = HasCycle(
pL2);
//兩個鏈表都不帶環。
if
((pL1MeetNode == NULL) && (pL2MeetNode == NULL))
{
while
(pL1Node->pNext != NULL)
{
pL1Node = pL1Node->pNext;
}
while
(pL2Node ->pNext != NULL)
{
pL2Node = pL2Node->pNext;
}
if
(pL1Node == pL2Node)
{
return
1;
}
return
0;
}
//兩個鏈表都帶環。若兩個鏈表的pL1MeetNode與pL2MeetNode都在環內,則相交。
if
((pL1MeetNode != NULL) && (pL2MeetNode != NULL))
{
//先讓pL1MeetNode走上一圈,如果在某個地方pL1MeetNode等於pL2MeetNode,則兩個鏈表相交。
pSListNode pNode = pL1MeetNode;
while
(pNode->pNext != pL1MeetNode)
//確保pL1MeetNode走完一圈。
{
if
(pL2MeetNode == pNode)
//判斷pL1MeetNode是否等於pL2MeetNode
{
return
1;
}
pNode = pNode->pNext;
}
return
0;
}
return
0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.