1. 程式人生 > >【資料結構】連結串列面試題

【資料結構】連結串列面試題

1、倒序列印連結串列

(1)非遞迴方式
在這裡插入圖片描述
程式碼如下:

void ReversePrint(ListNode **pFirst)
{
 ListNode *last = NULL;
 ListNode *cur = NULL;
 assert(*pFirst != NULL);
 while(last != *pFirst)
 {
  cur = *pFirst;
  while(cur != NULL)
  {
   if(cur->next == last)
   {
    printf("%d ",cur->data);
    last = cur;
   }
   cur = cur->next;
  }
 }
 printf("\n");
}

(2)遞迴方式
可以在遞迴的最後一層(即最後一次遞迴呼叫)時列印最後一個結點的元素,在每次函式呼叫結束返回時列印當前結點的元素。程式碼如下:

void ReversePrintR(ListNode *pFirst)
{
 if(pFirst == NULL)
 {
  return ;
 }
 if(pFirst->next == NULL)
 {
  printf("%d ",pFirst->data);
 }
 else
 {
  ReversePrintR(pFirst->next);
  printf("%d ",pFirst->data);
 }
}

2、逆置連結串列

在這裡插入圖片描述
程式碼如下:

ListNode *ReverseList(ListNode **pFirst)
{
 ListNode *tmp = NULL;
 ListNode *pre = NULL;
 ListNode *cur = NULL;
 ListNode *result = NULL;
 assert(*pFirst != NULL);
 cur = *pFirst;
 while(cur != NULL)
 {
  tmp = cur->next;
  if(cur->next == NULL)
  {
   result = cur;
  }
  cur->next = pre;
  pre = cur;
  cur = tmp;
 }
 return result;
}

3、刪除一個無頭連結串列的非尾結點

在這裡插入圖片描述
程式碼如下:

void RemoveNodeNotTail(ListNode *pos)
{
 if(pos == NULL)
 {
  return ;
 }
 pos->data = pos->next->data;
 pos->next = pos->next->next;
}

4、在無頭連結串列前插入一個結點

在這裡插入圖片描述
程式碼如下:

void InsertNoHead(ListNode *pos, int data)
{
 ListNode *NewNode = NULL;
 if(pos == NULL)
 {
  return ;
 }
 NewNode = CreateNode(data);
 NewNode->data = pos->data;
 NewNode->next = pos->next;
 pos->data = data;
 pos->next = NewNode;
}

5、單鏈表實現約瑟夫環

整體思路分為兩步走:
(1)先使連結串列構成一個環(最後一個結點的next等於第一個結點);
(2)如果連結串列中的剩餘結點超過一個,每次找到第k個結點,刪除它。
程式碼如下:

ListNode *JocephCircle(ListNode *pFirst, int k)
{
 ListNode *result = NULL;
 ListNode *pre = NULL;
 ListNode *cur = NULL;
 int i = 0;
 assert(pFirst != NULL);
 cur = pFirst;
 //先使連結串列構成一個環(最後一個結點的next等於第一個結點)
 while(cur->next != NULL)
 {
  cur = cur->next;
 }
 cur->next = pFirst;
 //如果連結串列中剩餘結點超過一個,每次找到第k個結點,刪除它
 cur = pFirst;
 while(cur->next != cur)
 {
  for(i=0; i<k-1; i++)
  {
   pre = cur;
   cur = cur->next;
  }
  //刪除元素
  pre->next = cur->next;
  //free(cur);
  cur = pre->next;
 }
 result = cur;
 result->next = NULL;
 return result;
}

6、氣泡排序

在這裡插入圖片描述
程式碼如下:

void BubbleSort(ListNode **pFirst)
{
 ListNode *cur = NULL;
 ListNode *last = NULL;
 ListNode *pre = NULL;
 DataType tmp = 0;
 DataType max = 0;
 assert(*pFirst != NULL);
 while(last != (*pFirst)->next)
 {
  cur = (*pFirst)->next;
  max = (*pFirst)->data;
  while(cur != last)
  {
   if(cur->data>max) //當前值大於最大值時,交換兩數
   {
    tmp = max;
    max = cur->data;
    cur->data = tmp;
   }
   pre = cur;
   cur = cur->next;
  }
  //一次迴圈結束後,把最大值放入使迴圈結束的結點中,把該結點中的值放入連結串列開頭
  tmp = pre->data;
  pre->data = max;
  (*pFirst)->data = tmp;
  last = pre;
 }
}

7、合併兩個有序連結串列

整體思路如下:
用result記錄新連結串列,兩個連結串列的當前數進行比較,哪個小,就先放入連結串列中,並且向後移動。直至兩個連結串列的最後一個結點,結束迴圈。程式碼如下:

ListNode *MergeOrderedList(ListNode *p1First, ListNode *p2First)
{
 ListNode *cur1 = NULL;
 ListNode *cur2 = NULL;
 ListNode *result = NULL;
 ListNode *tail = NULL;
 ListNode *node = NULL;
 assert(p1First);
 assert(p2First);
 cur1 = p1First;
 cur2 = p2First;
 while((cur1 != NULL)&&(cur2 != NULL))
 {
  if(cur1->data<=cur2->data)
  {
   node = cur1;
   cur1 = cur1->next;
   if(tail == NULL)
   {
    result = node;
   }
   else
   {
    tail->next = node;
   }
   node->next = NULL;
   tail = node;
  }
  else
  {
   node = cur2;
   cur2 = cur2->next;
   if(tail == NULL)
   {
    result = node;
   }
   else
   {
    tail->next = node;
   }
   node->next = NULL;
   tail = node;
  }
 }
 if(cur1 != NULL)
 {
  tail->next = cur1;
 }
 if(cur2 != NULL)
 {
  tail->next = cur2;
 }
 return result;
}

8、查詢連結串列的中間結點(只能遍歷一次連結串列)

思路:
取兩個指標走;
一個快指標,一個慢指標;
快指標走兩步,慢指標走一步;
直至快指標走到連結串列末尾,結束迴圈;
慢指標所在位置就是連結串列的中間結點。
程式碼如下:

ListNode *FindMid(ListNode *pFirst)
{
 ListNode *cur = NULL;
 ListNode *mid = NULL;
 int count = 0;
 assert(pFirst != NULL);
 cur = pFirst;
 mid = pFirst;
 while(cur != NULL)
 {
  cur = cur->next;
  count++;
  if((count%2)==0)
  {
   mid = mid->next;
   count = 0;
  }
  //cur = cur->next;
  //count++;
 }
 return mid;
}

9、查詢連結串列的倒數第k個結點(只能遍歷一次連結串列)

思路:
取快、慢兩個指標走;
快指標先走k步;
然後在快、慢指標一起走;
直至快指標走到連結串列末尾,結束迴圈;
慢指標所在位置即為連結串列的倒數第k個結點。
程式碼如下:

ListNode *FindK(ListNode *pFirst, int k)
{
 ListNode *fast = NULL;
 ListNode *slow = NULL;
 assert(pFirst != NULL);
 fast = pFirst;
 slow = pFirst;
 while(k)
 {
  fast = fast->next;
  k--;
 }
 while(fast != NULL)
 {
  fast = fast->next;
  slow = slow->next;
 }
 return slow;
}

10、刪除連結串列的倒數第k個結點(只能遍歷一遍連結串列,不能使用替換法刪除)

思路:
用快、慢兩個指標走;
快指標先走k+1步;
再快、慢指標一起走;
直至快指標走到連結串列末尾,結束迴圈;
找到倒數第k+1個結點,讓第k+1個結點指向第k個結點之後的結點,即刪除第k個結點。
程式碼如下:

void RemoveK(ListNode **pFirst, int k)
{
 ListNode *del = NULL;
 ListNode *fast = NULL;
 ListNode *slow = NULL;
 assert(*pFirst != NULL);
 fast = *pFirst;
 slow = *pFirst;
 while(k+1)
 {
  fast = fast->next;
  k--;
 }
 while(fast != NULL)
 {
  fast = fast->next;
  slow = slow->next;
 }
 del = slow->next;
 slow->next = slow->next->next;
 //free(del);
}

11、求兩個已排序單鏈表中相同的資料,並用連結串列返回

思路:
兩個連結串列中的當前數進行比較,哪個小,哪個就往前走,兩個數相等時,就將其放入新連結串列中,直至迴圈結束。
程式碼如下:

ListNode *UnionSet(ListNode *list1, ListNode *list2)
{
 ListNode *cur1 = NULL;
 ListNode *cur2 = NULL;
 ListNode *result = NULL;
 ListNode *tail = NULL;
 ListNode *node = NULL;
 assert(list1 != NULL);
 assert(list2 != NULL);
 cur1 = list1;
 cur2 = list2;
 while((cur1 != NULL)&&(cur2 != NULL))
 {
  if(cur1->data<cur2->data)
  {
   cur1 = cur1->next;
  }
  else if(cur1->data>cur2->data)
  {
   cur2 = cur2->next;
  }
  else
  {
   node = cur1;
   cur1 = cur1->next;
   cur2 = cur2->next;
   if(tail == NULL)
   {
    result = node;
   }
   else
   {
    tail->next = node;
   }
   node->next = NULL;
   tail = node;
  }
 }
 return result;
}

== 上述內容均為學習過程總結,如有不足之處,請指正 ==