精益求精解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;  }

今日刷题完毕,好久没有刷题了,累了,休息,闪了,欢迎大家转发,收藏,点赞!