鏈表相關筆試題

  在筆試面試考數據結構時,因爲時間有限,所出的題不會是紅黑樹、平衡二叉樹等比較複雜的數據結構。鏈表結構簡單,題目規模小但須要仔細考慮細節,所以稱爲筆試面試中的高頻考點。所以,下面總結出鏈表相關題目,以供複習。面試

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

    2.從尾到頭打印單鏈表(劍指offer第五題)數據結構

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

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

    5.單鏈表實現約瑟夫環code

    6.逆置/反轉單鏈表排序

    7.單鏈表排序(冒泡排序&快速排序)遞歸

    8.合併兩個有序鏈表合併後依然有序內存

    9.查找鏈表的中間節點,要求只能遍歷一次鏈表io

    10.查找單鏈表倒數第K個節點,要求只能遍歷一次鏈表

    11.判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?並計算每一個算法的時間複雜度&空間複雜度。

    12.判斷兩個鏈表是否相交,若相交,求交點(假設鏈表不帶環)

    13.判斷兩個鏈表是否相交,若相交,求交點(假設鏈表帶環)

    14.複雜鏈表的複製,一個鏈表的每一個節點,有一個指向next指針指向下一個節點,還有一個random指針指向這個鏈表中的一個隨機節點或者NULL,如今要求實現複製這個鏈表

    15.求兩個已排序鏈表中相同的數據。void UnionSet(ListNode* l1,ListNode* l2);

/////////////////////////////////////////////////////////////////////////////////////////////////////////(分割線)///////////////////////////////////////////////////

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

      首先咱們從順序表和鏈表的結構上來進行分析:
        (1)對於順序表,不管是動態的仍是靜態的,他們都是連續的存儲空間,在讀取上時間效率比較高,可經過地址之間的運算來訪問,可是在插入和刪除時會出現比較麻煩的負載操做。

        (2)對於順序表,由於是鏈式存儲。所以在咱們須要的時候咱們纔在堆上爲他們開闢空間,鏈表對於插入刪除比較簡單,可是遍歷的話須要屢次跳轉。

      其次,咱們從順序表和鏈表的空間申請方式來看:

        (1)對於順序表,空間開闢是在順序表已滿的時候開闢,開闢次數較多的時候會出現較大的空間浪費

        (2)對於鏈表,空間是針對單個節點的,不存在多餘的空間浪費。而且在碎片內存池的機制下,能夠有效的利用空間。

      綜上所述:順序表通常用於查找遍歷操做比較頻繁的狀況下使用,鏈表則針對於數據刪除修改操做比較多的狀況下使用。

    2.從尾到頭打印單鏈表

      從尾到頭打印單鏈表有兩種解法,一種是利用棧把節點從頭至尾push進去,利用棧先進後出的特色,從尾到頭打印單鏈表節點,一種是利用遞歸,在輸出現有節點以前輸出下一個節點,循環直至最後一個節點,而後再將節點從尾到頭依次打印。

     code1:利用棧

      void PrintTailToHead(ListNode* head)
      {
          stack<int> st;
          ListNode* p = head;
          while (p != NULL)
          {
              st.push(p->_data);
              p = p->_next;
          }
          while (!st.empty())
          {
              printf("%d->", st.top());
              st.pop();
          }
      }

     code2:利用遞歸

      void PrintTailToHead(ListNode* head)
     {
          if (head != NULL)
          {
              while (head->_next != NULL)
              {
                  PrintTailToHead(head->_next);
              }
          }
          printf("%d->", head->data);
     }

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

      因爲鏈表無頭,因此用常規方法刪除節點是不可能的。因此咱們能夠換種思路,將要刪除的節點後面的節點的值賦給要刪的節點,而後再把要刪除節點的後面的節點刪除,等於經過轉換,爲被刪除節點創造了一個頭結點。代碼以下:

      void DeleteNotTailNode(ListNode* p)
     {
          ListNode* s = p->_next;
          assert(s);
          p->_data = s->_data;
          p->_next = s->_next;
          free(p);
     }

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

      這個題目跟上一個題目很像。在這個非頭結點後面插入一個節點,把這個非頭節點的值賦給新插入的節點,而後再把要插入的值賦給這個非頭節點便可。

      void InsertNotHeadNode(ListNode* p, int data)
     {
          ListNode* s = (ListNode)malloc(sizeof(&ListNode));
          assert(s);
          s->_next =p->_next;
          p->_next = s;
          s->_data = p->_data;
          p->_data = data;
     }

    5.單鏈表實現約瑟夫環(劍指offer第45題)

       

    6.逆置/反轉單鏈表(劍指offer第16題)

      

    7.單鏈表排序(冒泡排序&快速排序)

    8.合併兩個有序鏈表合併後依然有序(劍指offer第17題)

      這個題比較簡單,分別用指針指向兩個鏈表,比較兩個鏈表指針所指向節點的值,而後將節點取下來從新組成一個鏈表便可,代碼以下:

      ListNode Merge(ListNode* head1, ListNode* head2)
     {
          if (head1 == NULL)
              return head2;
          if (head2 == NULL)
              return head1;
          ListNode* newhead = NULL;
          if (head1->_data < head2->_data)
          {
              newhead = head1;
              newhead->_next=Merge(head1->_next, head2);
          }
          if (head1->_data>head2->data)
          {
              newhead = head2;
              newhead->_next = Merge(head1, head2->_next);
          }
     }

    9.查找鏈表的中間節點,要求只能遍歷一次鏈表

      查找鏈表的中間節點,但只能遍歷一次鏈表,因此咱們會想到用快慢指針來解決這個問題。定義一個快指針,每次走兩步,載定義一個慢指針,每次走一步。等到快指針走到鏈表尾,慢指針所指向的節點就是鏈表的中間節點。代碼以下:

      ListNode* FindMidNode(ListNode* head)
     {
          ListNode* fast;

        ListNode* slow;
          fast = head;
          slow = head;
          while (fast&&fast->_next)
          {
              slow = slow->_next;
              fast = fase->_next->_next;
          }
          retrun slow;
     }

    10.查找單鏈表倒數第K個節點,要求只能遍歷一次鏈表(劍指offer第15題)

      其實這個題跟上面的題很像,稍微轉化一下就能想出思路。咱們能夠定義兩個指針,一個指針先走K步,而後兩個指針同時移動,等到先走的指針走到鏈表尾部,後走的指針所指向的節點就是倒數第K個節點。要注意考慮鏈表的各類狀況。代碼以下:

      ListNode* FindKthNode(ListNode* head,int k)
      {
          if (head == NULL || k == 0)
              return NULL;    
          ListNode* fast;

       ListNode* slow;
          fast = head;
          slow = head;
          for (int i = 0; i < k - 1; ++i)   //要注意鏈表長度比K短的狀況
          {
              if (fast->_next != NULL)
                  fast = fast->_next;
              else retrun NULL;
          }
          while (fast->_next != NULL)
          {
              fast = fast->_next;
              slow = slow->_next;
          }
          return slow;
     }

    11.判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?並計算每一個算法的時間複雜度&空間複雜度。(劍指offer第56題)

    12.判斷兩個鏈表是否相交,若相交,求交點(假設鏈表不帶環)

    13.判斷兩個鏈表是否相交,若相交,求交點(假設鏈表帶環)

    14.複雜鏈表的複製,一個鏈表的每一個節點,有一個指向next指針指向下一個節點,還有一個random指針指向這個鏈表中的一個隨機節點或者NULL,如今要求實現複製這個鏈表(劍指offer第26題)

    15.求兩個已排序鏈表中相同的數據。void UnionSet(ListNode* l1,ListNode* l2);

    16.在已排序的鏈表中刪除鏈表中重複的結點(劍指offer第57題)

相關文章
相關標籤/搜索