連結串列面試題集合
阿新 • • 發佈:2018-11-02
連結串列面試題:
1. 比較順序表和連結串列的優缺點,說說它們分別在什麼場景下使用?
- 順序表一般用於查詢,可隨機訪問;
- 連結串列一般用於增刪改,不可隨機訪問;
- 如果資料元素不多,兩種方式沒有太大的差別
- 如果資料元素不定,建議使用連結串列
- 順序表的CPU快取記憶體效率高,而單鏈表CPU快取記憶體效率低。
連結串列的基本操作如下:
#include "List.h" void InitLinkList(pList* pplist) { (*pplist) = NULL; } pNode BuyNode(DataType d) { pNode newNode = (pNode)malloc(sizeof(Node)); if (newNode == NULL) { perror("malldc error"); exit(EXIT_FAILURE); } newNode->data = d; newNode->next = NULL; return newNode; } void DestroyLinkList(pList* pplist) { assert(pplist); pNode cur = NULL; cur = *pplist; while (cur) { pNode del = cur; cur = cur->next; free(del); del = NULL; } *pplist = NULL; } void PushBack(pList* pplist, DataType d) { assert(pplist); pNode newNode = BuyNode(d); if ((*pplist) == NULL) { *pplist = newNode; } else { pNode cur = NULL; cur = *pplist; while (cur->next) { cur = cur->next; } cur->next = newNode; } } void PopBack(pList* pplist) { assert(pplist); if ((*pplist) == NULL) { printf("連結串列為空!\n"); return; } else { pNode cur = NULL; pNode del = NULL; cur = *pplist; del = cur->next; while (cur->next->next) { cur = cur->next; del = cur->next; } cur->next = del->next; free(del); del = NULL; } } void PushFront(pList* pplist, DataType d) { pNode newNode = BuyNode(d); assert(pplist); if ((*pplist) == NULL) *pplist = newNode; else { pNode cur = *pplist; newNode->next = cur; *pplist = newNode; } } void PopFront(pList* pplist) { assert(pplist); if ((*pplist) == NULL) { printf("連結串列為空!\n"); return; } else { pNode del = NULL; del = *pplist; *pplist = (*pplist)->next; free(del); del = NULL; } } pNode Find(pList plist, DataType d) { if (plist == NULL) { printf("連結串列為空!\n"); return NULL; } else { pNode cur = plist; while (cur) { if (cur->data == d) { return cur; } cur = cur->next; } return NULL; } } void Insert(pList* pplist, pNode pos, DataType d)//在指定位置之前插入一個值 { assert(pplist); assert(pos); assert(*pplist); pNode newNode = BuyNode(d); if (pos == (*pplist)) { newNode->next = *pplist; *pplist = newNode; } else { pNode cur = *pplist; while (cur&&cur->next != pos) { cur = cur->next; } if (cur) { newNode->next = pos; cur->next = newNode; } } } void Erase(pList* pplist, pNode pos)//指定位置刪除 { assert(pplist); assert(pos); if ((*pplist) == NULL) { return; } if ((*pplist) == pos) { *pplist = pos->next; free(pos); pos = NULL; } else { pNode cur = *pplist; while (cur&&cur->next != pos) { cur = cur->next; } if (cur) { cur->next = pos->next; free(pos); pos = NULL; } } } void Remove(pList* pplist, DataType d) { assert(pplist); pNode cur = *pplist; if ((*pplist) == NULL) { return; } if ((*pplist)->data == d) { *pplist = cur->next; free(cur); cur = NULL; } else { pNode del = cur; while (cur&&del->data != d) { cur = cur->next; del = cur->next; } if (cur) { cur->next = del->next; free(del); del = NULL; } } } //void ReMoveP(pList* pplist, DataType d) //{ // pNode pCur = NULL; // pNode pre = NULL; // assert(pplist); // if (NULL == (*pplist)) // { // return; // } // pCur = (*pplist); // // while (pCur) // { // pNode pDel = NULL; // if (pCur->data == d) // { // if ((*pplist)->data == d) // { // pCur = *pplist; // *pplist = (pCur)->next; // free(pCur); // pCur = *pplist; // } // else // { // pre->next = pCur->next; // free(pCur); // pCur=pre; // // } // } // else // { // pre = pCur; // pCur = pCur->next; // } // } //} void RemoveAll(pList* pplist, DataType d) { assert(pplist); pNode cur = *pplist; if ((*pplist) == NULL) { return; } pNode pre = *pplist; while (cur) { if ((*pplist)->data == d) { cur = *pplist; *pplist = (cur)->next; free(cur); cur = *pplist; } else if (cur->data == d) { pre->next = cur->next; free(cur); cur = pre->next; } pre = cur; cur = cur->next; } } void PrintLinkList(pList plist) { pNode cur = plist; while (cur) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); } int GetListLength(pList plist) { int count = 0; pNode cur = plist; while (cur) { count++; cur = cur->next; } return count; }
2. 從尾到頭列印單鏈表
- 遞迴呼叫
void PrintTailToHead1(pList plist)//遞迴逆序列印單項鍊表
{
if (plist == NULL)
return;
PrintTailToHead1(plist->next);
printf("%d ", plist->data);
}
- 非遞迴,時間複雜度O(n^2)。
void PrintTailToHead2(pList plist)//非遞迴逆序列印單項鍊表 { pNode tail = NULL; pNode cur = plist; if (plist == NULL) return; while (plist != tail) { while (cur->next != tail) { cur = cur->next; } printf("%d ", cur->data); tail = cur; cur = plist; } }
3. 刪除一個無頭單鏈表的非尾節點
void EraseNotTailNode(pNode pos)
{
assert(pos);
assert(pos->next);
pNode del = NULL;
del = pos->next;
pos->data = pos->next->data;
pos->next = del->next;
free(del);
del = NULL;
}
4. 在無頭單鏈表的一個節點前插入一個節點
void InsertNode(pList* pplist, pNode pos, DataType d)//在無頭單鏈表的一個節點之前插入一個節點 { assert(pplist); assert(pos); assert(*pplist); pNode newNode = BuyNode(pos->data); newNode->next = pos->next; pos->next = newNode; pos->data = d; }
5. 單鏈表實現約瑟夫環
方法一:
int pLinkNodeJosephCycle(pList * pplist, int num)
{
pList tmp = NULL;
pList p = NULL;
p = (*pplist);
tmp = (*pplist);
while (tmp->next)
{
tmp = tmp->next;
}
tmp->next = (*pplist);
while ((*pplist) != (*pplist)->next)
{
for (int i = 1; i < num - 1; i++)
{
(*pplist) = (*pplist)->next;
}
p = (*pplist)->next;
(*pplist)->next = p->next;
free(p);
p = NULL;
(*pplist) = (*pplist)->next;
}
return (*pplist)->data;
}
方法二:
void JosephCycle(pList * pplist, int num)
{
pList p = NULL;
p = (*pplist);
while (p!=p->next)
{
pNode del = NULL;
int count = num;
while(--count)
{
p=p->next;
}
printf("刪除:%d\n",p->data);
del=p->next;
p->data = del->data;
p->next = del->next;
free(del);
del = NULL;
}
}
6. 逆置/反轉單鏈表
void ReverseList(pList* pplist)
{
pList q = (*pplist);
pList p = q->next;
while (p) //當p指向NULL時,表明倒置完成,退出迴圈
{
q->next = p->next;
p->next = (*pplist);
(*pplist) = p;
p = q->next;
}
}
7. 單鏈表排序(氣泡排序)
void BubbleSort(pList * pplist)
{
assert(pplist);
assert(*pplist);
pList q = NULL;
pList p = NULL;
for (int i = 0; i < GetListLength(*pplist); i++)
{
pList q = (*pplist);
pList p = q->next;
for (int j = 0; j < GetListLength(*pplist) - i - 1; j++)
{
while (p)
{
if (q->data < p->data)
{
DataType tmp = q->data;
q->data = p->data;
p->data = tmp;
}
p = p->next;
q = q->next;
}
}
}
}
8. 合併兩個有序連結串列,合併後依然有序
//遞迴
pNode Merge_R(pList plist1, pList plist2)//遞迴合併兩個有序連結串列
{
pNode cur1 = plist1;
pNode cur2 = plist2;
pNode newlist = NULL;
if (cur1 == cur2)
return NULL;
if (cur1 == NULL)
return cur2;
if (cur2 == NULL)
return cur1;
if (cur1->data < cur2->data)
{
newlist = cur1;
newlist->next = Merge_R(cur1->next,cur2);
}
else
{
newlist = cur2;
newlist->next = Merge_R(cur1,cur2->next);
}
return newlist;
}
pNode Merge(pList plist1, pList plist2)//非遞迴合併兩個有序連結串列
{
pNode cur1 = plist1;
pNode cur2 = plist2;
pNode head = NULL;
pNode tail = NULL;
if (cur1 == NULL&&cur2 == NULL)//兩條都為空不合並
return NULL;
if (cur1 == cur2)
return cur1;
if (cur1 == NULL)
return cur2;
if (cur2 == NULL)
return cur1;
if (cur1->data > cur2->data)
{
head = cur1;
cur1 = cur1->next;
}
else
{
head = cur2;
cur2 = cur2->next;
}
head->next = NULL;
tail = head;
while (cur1 != NULL&&cur2 != NULL)
{
if (cur1->data > cur2->data)
{
tail->next = cur1;
cur1 = cur1->next;
}
else
{
tail->next = cur2;
cur2 = cur2->next;
}
tail->next->next = NULL;
tail = tail->next;
}
if (cur1 == NULL)
{
tail->next = cur2;
}
else
tail->next = cur1;
return head;
}
9. 查詢單鏈表的中間節點,要求只能遍歷一次連結串列
pNode FindMidNode(pList plist)//找中間節點
{
pList fast = NULL;
pList slow = NULL;
if (NULL == plist)
{
return NULL;
}
else
{
//快慢指標
slow = plist;
fast = plist;
while ((NULL != fast) && (NULL != fast->next))
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
}
10. 查詢單鏈表的倒數第k個節點,要求只能遍歷一次連結串列
pNode FindLastKNode(pList plist, int k)
{
if ((NULL == plist) || (k <= 0))
{
return NULL;
}
else
{
pNode fast = plist;
pNode slow = plist;
//利用快慢指標,讓快指標先走K-1步,然後兩指標同時走,直到快指標指向的下一個結點為空為止
while (--k)
{
fast = fast->next;
if (NULL == fast)
{
return NULL;
}
}
while (NULL != fast->next)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
11. 判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?並計算每個演算法的時間複雜度&空間複雜度。
pNode CheckCycle(pList pList)
{
if ((NULL == pList) || (NULL == pList->next))
{
return NULL;
}
else
{
pNode pFast = pList->next->next;
pNode pSlow = pList->next;
while (pFast != pSlow)
{
if (NULL == pFast)
{
return NULL;
}
else
{
pFast = pFast->next;
pSlow = pSlow->next;
if (NULL == pFast)
{
return NULL;
}
else
{
pFast = pFast->next;
}
}
}
return pFast;
}
}
int GetCircleLength(pNode meet)
{
if (NULL == meet)
{
return 0;
}
else
{
int count = 1;
pNode pnode = meet;
while (meet != pnode->next)
{
pnode = pnode->next;
count++;
}
return count;
}
}
pNode GetCycleEntryNode(pList plist, pNode meetNode)
{
pNode pnode = plist;
if ((NULL == plist) || (NULL == meetNode))
{
return NULL;
}
while (pnode != meetNode)
{
pnode = pnode->next;
meetNode = meetNode->next;
}
return pnode;
}
12. 判斷兩個連結串列是否相交,若相交,求交點。(假設連結串列不帶環)
int CheckCross(pList p1, pList p2)
{
if((p1==NULL)||(p2==NULL))
return 0;
pNode end1=p1;
pNode end2=p2;
while(end1->next!=NULL)
end1=end1->next;
while(end2->next!=NULL)
end2=end2->next;
if(end1==end2)
return 1;
else return 0;
}
13. 判斷兩個連結串列是否相交,若相交,求交點。(假設連結串列可能帶環)【升級版】