C語言實現單鏈表面試題——基礎篇(上)

1.比較順序表和鏈表的優缺點,說說它們分別在什麼場景下使用?

  1. 順序表的特點是邏輯上相鄰的數據元素,物理存儲位置也相鄰,並且,順序表的存儲空間需要預分配。
    它的優點 :
    (1)方法簡單,各種高級語言中都有數組,容易實現
    (2)不用爲表示節點間的邏輯關係而增加額外的存儲開銷。
    (3) 順序表具有按元素序號隨機訪問的特點
    它的缺點:
    (1)插入時需要將後面的元素逐個挪動比較麻煩
    (2)動態存儲比較麻煩,開大了浪費,開小了不夠用
  2. 在鏈表中邏輯上相鄰的數據元素,物理存儲位置不一定相鄰,它使用指針實現元素之間的邏輯關係。並且,鏈表的存儲空間是動態分配的。
    它的優點:插入刪除方便
    它的缺點:
    (1) 需要佔用額外的空間,來存儲下一個節點的地址
    (2)不能隨機存取元素
    應用場景:
    在一次可開闢足夠空間且節省空間則用順序表
    在需要多次增刪查改則選用鏈表。

2.從尾到頭打印鏈表:

思考方法:首先我們需要找到最後一個節點,且還能回到上一個節點,我們可以想到遞歸,我們傳頭結點”pList”給函數,函數判斷節點是否爲NULL,若爲NULL,則打印NULL,反之我們繼續調用函數,傳pList->next,則實現了:先找到最後一個節點,並在函數返回後找到上個節點的值pList->data;

void PrintHeadtoTail(ListNode *pList)//從尾到頭打印鏈表
 {
     if(pList == NULL)
     {
         printf("NULL");
         return;
     }
     PrintHeadtoTail(pList->next);//傳pList->next
    //函數返回時不會改變plist的值
    printf("<-%d",pList->data);  //打印pList->data
 }

3.刪除一個無頭鏈表的非尾節點

函數名: void ErasenonTail(ListNode *pos);

1.當看到這個問題的時候,我想這麼簡單的問題,我想到的代碼是:

void ErasenonTail(ListNode *pos)
{
    ListNode *pavi = pos;
    pos = pos->next;
    free(pavi);
}

2.但運行之後,我發現程序崩潰了,我就很納悶。我就一步一步的調試發了許多很可笑的問題:

void ErasenonTail(ListNode *pos)
 //這裏的pos雖然值和鏈表節點的值相同,但修改pos鏈表不會改變
{
    ListNode *pavi = pos;
    pos = pos->next;//pos是值傳遞
    free(pavi);
    pavi = NULL;//這裏就更可笑了,函數內部定義的值置空
}

3.經過這一次的教訓,我發現不能小看任何一道題,所以我利用畫圖得到了一個辦法
這裏寫圖片描述
代碼:

 void ErasenonTail(ListNode *pos)//
 {
     assert(pos);
     ListNode *tail = pos->next;
     pos->data = tail->data; //對指針的引用改變指針所指向空間的值
     pos->next = tail->next;
     free(tail);
 }

4. 在無頭單鏈表的一個節點前插入一個節點

這個題和上一個題的方法一樣,在鏈表節點後加一個節點簡單,再交換這個節點和新添加的節點的數據;

 void InsertnonTail(ListNode* pos, DataType x)//無頭鏈表節點前添加
 {
     assert(pos);
     ListNode *pavi = NULL;
     Compatibo(&pavi);
     pavi->data = pos->data;
     pos->data = x;
     pavi->next = pos->next;
     pos->next = pavi;
 }

5. 單鏈表實現約瑟夫環

這裏寫圖片描述

//頭結點 ppList ,開始的人數 n, 每次數的數m
void Josephus(ListNode **ppList,int n, int m)
{
    assert(ppList);
    ListNode *ret = NULL;
    ListNode *tail = NULL;
    int i = 0;
    for(i=1; i<=n; i++)
    {
        PushBack(ppList, i);
    }
    ret = Find(*ppList,n);
    ret->next = *ppList;//創建了一個循環鏈表
    while(*ppList != (*ppList)->next)
    {
        ret = *ppList;
        for(i=0;i<m-1; i++)//找出要刪除的節點
        {
            ret = ret->next;
        }
        EraseCrc(ppList, ret);//刪除節點(注意頭結點不能變)
        //在刪除頭一個節點的時候,應利用無頭節點刪除的方法
    }
    printf("%d\n",(*ppList)->data);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章