連結串列面試題
阿新 • • 發佈:2018-12-13
關於連結串列已經學了有一段時間了,今天抽空進行了整理,列出來常見的有關連結串列的面試題,以下想法如有瑕疵望批評指出,希望能給初學者帶來一點參考和價值
從尾到頭列印單鏈表
遞迴列印
// 1、從尾到頭列印單鏈表 遞迴
void ListReversePrint(ListNode *Node)
{
if (Node == NULL)
{
return;
}
ListReversePrint(Node->next);
printf("%d-->",Node->data);
}
非遞迴列印
void ListReveresePrint2(ListNode *Node) { ListNode *end = NULL; while (end != Node) { ListNode *cur = Node; while (cur->next != end) { cur = cur->next; } printf("%d-->",cur->data); end = cur; } }
刪除一個無頭單鏈表的非尾結點(不能遍歷連結串列)
核心程式碼
//2. 刪除一個無頭單鏈表的非尾結點(不能遍歷連結串列) void ListDelNotFirst(ListNode **Node ,ListNode *pos) { ListNode *del; if (Node == NULL) { return; } // pos 位置為第一個結點時,修改指標 if (pos == *Node) { *Node = pos->next; free(pos); } // pos 位置不是第一個結點時也不是最後一個結點時 pos->data = pos->next->data; //先修改data的值 del = pos->next; //將pos->next作為要刪除的結點 pos->next = del->next; free(del); }
測試
void TestListDelNotFirst() { ListNode *list = NULL; ListPushBack(&list,1); ListPushBack(&list,2); ListPushBack(&list,3); ListPushBack(&list,4); ListPushBack(&list,5); ListPushBack(&list,6); ListPrint(list); ListDelNotFirst(list,list); ListPrint(list); }
在無頭單鏈表的一個結點前插入一個結點(不能遍歷連結串列)
//3.在無頭單鏈表的一個結點前插入一個結點(不能遍歷連結串列)
void ListInsertBefore(ListNode **Node ,ListNode *pos ,DataType data)
{
ListNode *newNode;
if (*Node == NULL && pos == NULL)
{
return;
}
//插入的結點是在第一個結點上
if (pos == *Node)
{
ListNode *newNode = CreateNode(data);
newNode->next = *Node;
*Node = newNode;
return;
}
//插入的結點不是第一個結點時
newNode = CreateNode(pos->data);
newNode->next = pos->next; //後插
pos->next = newNode;
pos->data = data;
}
逆置/反轉單鏈表
思路一:
從第二個結點開始,將其刪除,人挪活插在第一個結點處,直到連結串列結束
void listReverse(ListNode **Node)
{
if (Node == NULL)
{
return ;
}
if (*Node == NULL)
{
return;
}
//只有一個結點
if ((*Node)->next == NULL)
{
return ;
}
//至少兩個結點
ListNode *cur = *Node;
while (cur->next != NULL)
{
ListNode *rm = cur->next;
cur->next = rm->next;
rm->next = *Node;
*Node = rm;
}
}
思路二:
改變每個結點的指標指向從而逆置
void listReverseRePoint(ListNode **Node)
{
if (Node == NULL)
{
return;
}
if (*Node == NULL)
{
return;
}
if ((*Node)->next == NULL)
{
return;
}
ListNode *pre = *Node;
ListNode *cur = (*Node)->next;
pre->next = NULL;
while (cur != NULL)
{
ListNode *temp = cur->next;
cur->next = pre;
pre =cur;
cur = temp;
}
*Node = pre;
}
找出兩個連結串列裡相同資料
// 5.找出兩個連結串列裡相同資料
void ListIntersection(ListNode *list1,ListNode *list2)
{
ListNode *head1 = list1;
ListNode *head2 = list2;
DataType data;
if (head1 ==NULL && head2 == NULL)
{
return ;
}
while (head1 != NULL && head2 != NULL)
{
if (head1->data < head2->data)
{
head1 = head1->next;
}
if (head1->data > head2->data)
{
head2 = head2->next;
}
if (head1->data == head2->data)
{
printf("%d ",head1->data);
data = head1->data;
while (head2 != NULL && data == head2->data )
{
head2 = head2->next;
}
while (head1 != NULL && data == head1->data )
{
head1 = head1->next;
}
}
}
}
測試
void TestListInterstion()
{
ListNode *list1 = NULL;
ListNode *list2 = NULL;
ListPushBack(&list1,1);
ListPushBack(&list1,1);
ListPushBack(&list1,3);
ListPushBack(&list1,4);
ListPushBack(&list2,2);
ListPushBack(&list2,2);
ListPushBack(&list2,4);
ListPushBack(&list2,4);
ListIntersection(list1,list2);
}
單鏈表實現約瑟夫環
核心程式碼
//6.單鏈表實現約瑟夫環
ListNode* ListJosephCircle(ListNode *list,DataType k)
{
//第一步,變成環
ListNode *tail = list;
ListNode *cur = list;
ListNode *pre = NULL;
int i =0;
#if 0
if (list == NULL)
{
return NULL;
}
#endif
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = list;
//第二步,找到第k個數
while ( cur->next != cur)
{
#if 0
//1 2 3 4 5 6 7 8
while (--k)
{
pre = cur; //記錄剔除結點的前一個結點
cur = cur->next;
}
#endif
for (i=1;i<k;i++)
{
pre = cur; //記錄剔除結點的前一個結點
cur = cur->next;
}
pre->next =cur->next;
printf("淘汰:%d\n",cur->data);
free(cur);
cur = pre->next;
}
cur->next = NULL;
return cur;
}
測試
void TestListJosephCircle()
{
ListNode *list1 = NULL;
ListNode *re;
ListPushBack(&list1,1);
ListPushBack(&list1,2);
ListPushBack(&list1,3);
ListPushBack(&list1,4);
ListPushBack(&list1,5);
ListPushBack(&list1,6);
ListPrint(list1);
printf("\n");
re = ListJosephCircle(list1,3);
printf("%d\n",re->data);
}
合併兩個有序的連結串列,合併後依然有序
具體程式碼
//7.合併兩個有序的連結串列,合併後依然有序
ListNode *ListMerge(ListNode *list1,ListNode *list2)
{
ListNode *cur1 = list1;
ListNode *cur2 = list2;
ListNode *result = NULL; //結果連結串列
ListNode *tail = NULL; //結果連結串列中的最後一個結點,方便尾插
ListNode *next = NULL; //儲存遍歷過程中的下一個結點
ListNode *node;
while (cur1 != NULL && cur2 != NULL)
{
if (cur1->data <= cur2->data)
{
node = cur1;
}
else
{
node =cur2;
}
next = node->next;
if (result != NULL)
{
tail->next = node;
}
else
{
result = node;
}
node->next = NULL;
tail = node;
if (node == cur1)
{
cur1 = next;
}
else
{
cur2 = next;
}
}
//一個連結串列空了
if (cur1 == NULL)
{
tail->next =cur2;
}
if (cur2 == NULL)
{
tail->next =cur1;
}
return result;
}
測試
void TestMerge()
{
ListNode *list1 = NULL;
ListNode *list2 = NULL;
ListNode *result = NULL;
ListPushBack(&list1,1);
ListPushBack(&list1,2);
ListPushBack(&list1,3);
ListPushBack(&list1,4);
ListPushBack(&list2,1);
ListPushBack(&list2,2);
ListPushBack(&list2,3);
ListPushBack(&list2,4);
result = ListMerge(list1,list2);
ListPrint(result);
}
查詢單鏈表的中間結點,要求只能遍歷一次連結串列
思路:定義快慢指標,當快指標走完時,慢指標剛好是在中間位置,好比是兩人賽跑,跑的快點人的速度是慢的人的二倍
具體程式碼
//8.查詢單鏈表的中間結點,要求只能遍歷一次連結串列
void ListFindMidNode(ListNode *list)
{
ListNode *fast = list;
ListNode *slow = list;
while (fast != NULL && fast->next != NULL)
{
fast = fast->next;
if (fast == NULL)
{
break;
}
fast = fast->next;
if (fast == NULL)
{
break;
}
slow = slow->next;
}
printf("%d\n",slow->data);
}
測試
void TestFindMid()
{
ListNode *list = NULL;
ListPushBack(&list,1);
ListPushBack(&list,2);
ListPushBack(&list,3);
ListPushBack(&list,4);
ListPushBack(&list,5);
ListPushBack(&list,6);
ListFindMidNode(list);
}
查詢單鏈表中的倒數第K個結點,要求只能遍歷一次連結串列
思路:定義快慢指標,快指標先走k步,滿指標再開始走,快指標走完的時候就是慢指標走到倒數第k個結點處
具體程式碼
//9.查詢單鏈表中的倒數第K個結點,要求只能遍歷一次連結串列
void ListFindTailK(ListNode *list,DataType k)
{
ListNode *fast = list;
ListNode *slow = list;
while (k--)
{
fast = fast->next;
}
while (fast != NULL)
{
fast = fast->next;
slow = slow->next;
}
printf("%d\n",slow->data);
}
測試
void TestFindTailK()
{
ListNode *list = NULL;
ListPushBack(&list,1);
ListPushBack(&list,2);
ListPushBack(&list,3);
ListPushBack(&list,4);
ListPushBack(&list,5);
ListPushBack(&list,6);
ListFindTailK(list,3);
}
刪除單鏈表中的倒數第K個結點
思路:同查詢倒數第k個結點一樣,我們定義快慢指標進行非同步,還需要定義一個pre來記錄要刪除結點的先驅,刪除後需要指向被刪除的下一個
具體程式碼
//10.刪除單鏈表中的倒數第K個結點
void ListDelTailK(ListNode *list, DataType k)
{
ListNode *fast = list;
ListNode *slow = list;
ListNode *pre = NULL;
while (k--)
{
fast = fast->next;
}
while (fast != NULL)
{
pre = slow;
fast = fast->next;
slow = slow->next;
}
pre->next = slow->next;
free(slow);
}
測試
void TestDelTailK()
{
ListNode *list = NULL;
ListPushBack(&list,1);
ListPushBack(&list,2);
ListPushBack(&list,3);
ListPushBack(&list,4);
ListPushBack(&list,5);
ListPushBack(&list,6);
ListPrint(list);
printf("\n刪除倒數第%d個結點\n",3);
ListDelTailK(list,3);
ListDelNotFirst(list,list);
ListPrint(list);
}