精益求精解LeetCode(82與83)

  • 2019 年 10 月 6 日
  • 筆記

好久沒有刷題與更文了,今天來一場LeetCode上面簡單與中等題目多種方法刷題。

1.題目

83. 刪除排序鏈表中的重複元素

給定一個排序鏈表,刪除所有重複的元素,使得每個元素只出現一次。

示例 1:

輸入: 1->1->2輸出: 1->2示例 2:

輸入: 1->1->2->3->3輸出: 1->2->3

2.思想

(1)使用快慢指針

特殊情況判斷:鏈表為空或鏈表只有一個節點,直接返回。

設p=head,q=head->next,讓不斷去移動,直到q的val不等於p的val,那麼將p連接上q即可。

循環特殊情況判斷,當快指針指向為空,直接讓p指向NULL,break掉函數,返回即可。

分析:時間複雜度為O(n),空間複雜度為O(1)。

class Solution {  public:      ListNode* deleteDuplicates(ListNode* head) {          if(head==NULL||head->next==NULL) return head;          ListNode *p=head,*q=p->next;          while(p) {              if(p->val!=q->val) {                  p->next=q;                  p=q;              }              q=q->next;              if(q==NULL){                  p->next=NULL;                  break;              }          }          return head;      }  };

(2)遞歸

遞歸終止條件:無節點或只有一個節點。

遞歸到最後,例如尾部節點為2 2,也就是當head->next指向末尾的2時候,此時需要判斷head與head->next值是否相等,如果相等,直接讓head指向尾部,依次覆蓋所有重複節點。否則,直接讓head指向尾部節點。

分析:時間複雜度為O(n),空間複雜度為O(1)。

ListNode* deleteDuplicates(ListNode* head) {      //鏈表是否為空或者是否到末尾      if (head == NULL || head->next == NULL)          return head;      ListNode *p = deleteDuplicates(head->next);      if (head->val == head->next->val)          head = p;      else          head->next = p;      return head;  }

2.題目

82. 刪除排序鏈表中的重複元素 II

給定一個排序鏈表,刪除所有含有重複數字的節點,只保留原始鏈表中 沒有重複出現 的數字。

示例 1:

輸入: 1->2->3->3->4->4->5輸出: 1->2->5示例 2:

輸入: 1->1->1->2->3輸出: 2->3

3.思想

3.1方法一

這道題與上述題目很相似,於是採用上述題目思想完成。

上述思想中核心就是快慢指針,快指針q,滿指針p,每次q指向的是新元素,也就是滿足p->val!=q->val,就需要判斷當前鏈表是否值不同連續

值不同連續判斷就是p->next==q,(兩者距離只差1)。如果滿足,說明當前p指向的元素無重複,那麼直接讓r(此指針為新返回鏈表的遍歷指針)指針指向p指向的節點(注意這裡是創建了一個p->val相同的節點),r指針再指向下一個節點,q指針處理是不作為循環的遍歷指針,每次++。

到最後,q指針為空,分為兩種情況:

(1)值不同不連續: 例如:[1,2,2] p指向了2,q指向了NULL,此時需要將r->next指針直接指向末尾的NULL*

(2)值不同連續: 例如:[1,2,2,5] p指向了5,q指向了NULL,此時需要連接p,即r->next=p

ListNode* deleteDuplicates(ListNode* head) {      if(head==NULL || head->next==NULL) return head;        ListNode* p=head,*q=p->next;      ListNode* HEAD=new ListNode(0),*r=HEAD;        while(q) {          cout<<p->val<<" "<<q->val<<endl;            if(p->val!=q->val) {              if(p->next==q) {                  r->next=new ListNode(p->val);                  r=r->next;              }                p=q;          }          cout<<r->val<<endl;          q=q->next;      }      if(p->next!=q)          r->next=NULL;      else          r->next=new ListNode(p->val);      r=HEAD->next;      delete HEAD;      return r;  }

分析:*時間複雜度:O(n),空間複雜度:O(n)。

那能否優化空間複雜度為O(1)呢?

我們繼續優化。

3.2方法二

上述的空間複雜度耗費在每次都要去創建新的節點,那麼我們不創建不就行了,只需要拓展一個指針,讓該指針不斷動態修改鏈表。

ListNode* deleteDuplicates(ListNode* head) {      // 無節點或者只有一個節點情況      if(head==NULL || head->next==NULL) return head;        // p跳躍指向不同節點,q遍歷所有節點,r動態修改鏈表      ListNode* p=head,*q=p->next,*r=p;      // 針對 [1 1 2] 這種情況需要先確定返回鏈表頭結點,那麼這裡創建一個頭結點,讓上述定義的r指針取動態的修改返回鏈表。      ListNode* HEAD=new ListNode(0); // 返回鏈表的頭指針        while(q) {          /**           * 值不同           */          if(p->val!=q->val) {              /**               * 值不同且連續               */              if(p->next==q) {                  /**                   * 返回鏈表是否有開始節點                   */                  if (HEAD->next == NULL) {                      r = p;                      HEAD->next = r;                  } else {                      r->next = p;                      r = p;                  }              }              // p指向新值              p=q;          }          q=q->next;  //遍歷鏈表      }      if(p->next!=q)          r->next=NULL;      else {          if(HEAD->next==NULL)              HEAD->next=p;          else              r->next=p;      }      r=HEAD->next;      delete HEAD;      return r;  }

到最後,q指針為空,分為兩種情況:

(1)值不同不連續

例如:[1,2,2] p指向了2,q指向了NULL,此時需要將r->next指針直接指向末尾的NULL

(2)值不同連續

  • 值不同連續,且返回鏈表的沒有開始節點,也就是HEAD->next=NULL 例如:[1,1,2] p指向了2,q指向了NULL,此時需要連接p,但是Head->next為空,直接讓HEAD->next指向p即可
  • 值不同連續,且回鏈表的有開始節。 例如:[1,2,2,5] p指向了5,q指向了NULL,此時需要連接p,即r->next=p

分析:時間複雜度:O(n),空間複雜度O(1)。

到此為止,自己實現的思路全部寫完,後面是看題解與評論上的一些思路,並對他們的程式碼做了一些優化後呈現出來的。

3.3 方法三

第82題用到了遞歸法,這道題也可以!

思想就是如果當前節點與後一節點相同,那麼遞歸後一節點,直到找到不同的節點,並讓前一節點(對應前面提到的當前節點)指向後一節點,依次遞歸下去,如果遞歸過程中當前節點與後一節點不同,直接鏈接,最後的head就是最終結果。

ListNode* deleteDuplicates2(ListNode* head) {      if(head==NULL || head->next==NULL) return head;        ListNode *p = head->next;        if(head->val == p->val) {          while (p&&head->val==p->val){              p=p->next;          }          head=deleteDuplicates2(p);      } else {          head->next=deleteDuplicates2(p);      }      return head;  }

時間複雜度O(n),空間複雜度O(1)。

3.4 方法四

這個方法比較傳統,在題解中空間複雜度為O(n),這裡我將其進行優化,最終的時間複雜度為O(n),空間複雜度為O(1)。

思想是使用快慢指針,用慢指針跳過那些重複數,慢指針指的的元素就是返回鏈表中的元素。

ListNode* deleteDuplicates3(ListNode* head) {      if(head==NULL || head->next==NULL) return head;      // 1 1 2 2 3      ListNode *p=head,*q=p->next,*pre=head;      // 確定返回節點的頭在哪裡!      bool isonce=true;      while(p) {          if(q&&p->val==q->val) {              int tmp = p->val;              while(p&&p->val==tmp)                  p=p->next;              if(p) q=p->next;          } else {              // 1 2 3   1 1 2 3              if (isonce) {                  head = p;                  isonce = false;                  pre = head;              } else{                  pre->next = p;                  pre = p;              }              p=q;              if(q)                  q=q->next;          }      }      if(isonce) {          head=NULL;          isonce=false;      } else {          pre->next=p;      }        return head;  }

今日刷題完畢,好久沒有刷題了,累了,休息,閃了,歡迎大家轉發,收藏,點贊!