博客园  :: 首页  :: 新随笔  :: 管理

1.链表

Posted on 2021-03-29 21:45  wsg_blog  阅读(117)  评论(0编辑  收藏  举报

Index LeetCode

(单)链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一节点的指针,因此很多链表问题可以用递归来处理,不同于数组,链表不能直接获取任意节点的值,必须要通过指针找到该节点后才能获取其值。在未遍历到链表结尾时,我们也无法知道链表的长度,除非依赖其他数据存储长度,链表表示方法如下

struct ListNode{
  int val;
  ListNode *next;
  ListNode(int x):val(x),next(nullptr){}
};


由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一节点而非当前节点本身,二是建立一个虚拟节点(dummy node),使其指向当前链表的头节点,这样即使原链表所有节点全部删除,也会有一个dummy存在,返回dummy->next即可。

leetcode中的head节点一般就是a1的地址,a1的数值为head->val
链表的操作一般就是对指针进行修改,一般很少关注数值val,除非排序,把指针当成一个int临时变量看待就行
一般来说,算法题不需要删除内存。在刷LeetCode的时候,如果想要删除一个节点,可以直接进行指针操作而无需回收内存。实际做软件工程中,对于无用的内存,建议尽量显示回收,或利用智能指针。

常用方法:反转迭代、头插法、递归、快慢指针、哈希、归并

BM1.反转链表(206.反转链表)[easy]

反转链表:链表“指针”翻转
输入:1->2->3->4->5->null
输出:null<-1<-2<-3<-4<-5
双指针迭代法

ListNode* reverseList(ListNode* head){
  ListNode *pre = nullptr, *cur=head;  //反转前需要前节点和当前节点
  while(cur != nullptr){
    ListNode* temp=cur->next;      //用temp,不要用head
    cur->next=pre;        //双指针迭代
    pre=cur;
    cur=temp;
  }
  return pre;            //当前节点cur为null,左节点pre就是翻转后的头节点
}
BM2.链表内指定区间反转(92.反转链表II)[medium]

输入:1->2->3->4->5->null,left=2,right=4
输出:1->4->3->2->5->null
双指针头插法

ListNode* reverseBetween(ListNode* head, int left, int right){
  ListNode* dummyNode=new ListNode(-1);  //加个表头dummyNode,这类问题的一般做法
  dummyNode->next=head;
  ListNode* pre=dummyNode;  //前序节点
  ListNode* cur=head;       //当前节点
  for(int i=0; i<left-1; i++){  //找到left处的pre
    pre=cur;
    cur=cur->next;
  }
  for(int i=left; i<right; i++){  //头插法插入节点
    ListNode* temp=cur->next;
    cur->next=temp->next;
    temp->next=pre->next;
    pre->next=temp;
  }
  return dummyNode->next;
}
BM3.链表中的节点每k个一组翻转(25.K个一组翻转链表)[hard]

输入:1->2->3->4->5->null k=2
输出:2->1->4->3->5->null
k=3 :3->2->1->4->5->null
top101-p9,使用递归的方法,使每段链表链接起来很自然

ListNode* reverseKGroup(ListNode* head, int k){  //递归
  ListNode* tail=head;  //找到每次反转的尾部
  for(int i=0; i<k; i++){
    if(tail==nullptr)  //如果不足k次到了尾链表,直接返回,不反转
      return head;
      tail=tail->next;
  }
  ListNode* pre=nullptr;  //反转前需要前序节点和当前节点
  ListNode* cur=head;
  while(cur!=tail){    //在到达当前段尾节点前
    ListNode* temp=cur->next;  //反转
    cur->next=pre;
    pre=cur;
    cur=temp;
  }
  head->next=reverseKGroup(tail, k);  //当前尾指向下一段要反转的链表
  return pre;
}
BM4.合并两个排序的链表(21.合并两个有序链表)[easy]

输入:l1=[1,2,3],l2=[1,3,4]
输出:[1,1,2,3,4,4]

ListNode* mergeTwoLists(ListNode * list1, ListNode* list2){
  if(list1==nullptr) return list2;
  if(list2==nullptr) return list1;
  ListNode* head=new ListNode(-1), *dummy=head;
  while(list1 && list2){
    if(list1->val > list2->val){
      dummy->next=list2;
      list2=list2->next;
    }else{
      dummy->next=list1;
      list1=list1->next;
    }
    dummy=dummy->next;
  }
  dummy->next=list1? list1:list2;
  return head->next;
}
BM5.合并k个已排序链表(23.合并k个升序链表)[hard]

输入:lists=[[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
merge2Lists()

ListNode* mergeKList(vector<ListNode*>& lists){
  if(lists.size()==0) return nullptr;
  /* 这样也可以,但是合并次数太多,我们可以利用归并排序中的两两合并,减少合并次数
  ListNode* ans=lists[0];
  for(int i=1; i<lists.size(); i++){
    ans=merge2Lists(ans, lists[i]);
  }
  return ans;*/
  return divideMerge(lists, 0, lists.size());
}
ListNode* divideMerge(vector<ListNode*>& lists, int left, int right){
  if(left > right){
    return nullptr;
  }else if(left == right){  //中间一个的情况
    return lists[left];
  }else{
    int mid=(left+right)/2;
    return merge2List(divideMerge(lists, left, mid), divideMerge(lists, mid+1, right));
  }
}
ListNode* merge2Lists(ListNode* l1, ListNode* l2){  //21.合并两个有序链表
  if(l1==nullptr) return l2;
  if(l2==nullptr) return l1;
  ListNode* dummy=new ListNode(-1), *head=dummy;
  while(l1 && l2){
    if(l1->val > l2->val){
      head->next=l2;
      l2=l2->next;
    }else{
      head->next=l1;
      l1=l1->next;
    }
    head=head->next;
  }
  head->next=l1? l1:l2;
  return dummy->next;
}
BM6.判断链表是否有环(141.环形链表)[easy]

输入:链表头节点head
输出:有环true,无环false
快慢指针、哈希

bool hasCycle(ListNode* head){  //哈希
  unordered_set<ListNode*> hash;
  while(head){
    if(hash.count(head)) return true;
    hash.insert(head);
    head=head->next;
  }
  return false;
}
bool hasCycle(ListNode* head){  //快慢指针
  ListNode *fast=head, *slow=head;
  while(fast && slow){
    slow=slow->next;
    if(fast->next==nullptr) return false;
    fast=fast->next->next;
    if(fast==slow) return true;
  }
  return false;
}
BM7.链表中环的入口结点(142.环形链表II)[medium]

输入:链表头节点head
输出:环形链表的相交节点或null
快慢指针、哈希

ListNode* detectCycle(ListNode *head){  //快慢指针和环形链表基本一样
  if(head==nullptr) return head;
  ListNode* slow=head, *fast=head;
  while(fast != nullptr){
    slow=slow->next;
    if(fast->next==nullptr)  return nullptr;
    fast=fast->next->next;
    if(fast==slow){
      ListNode* ptr=head;
      while(ptr!=slow){    //主旨思想:2-1=1
        ptr=ptr->next;
        slow=slow->next;
      }
      return ptr;
    }
  }
  return nullptr;
}
BM8.链表中倒数最后k个结点(剑指Offer22.链表中倒数第k个节点)[easy]

输入:1->2->3->4->5,和k=2
输出:4->5
快慢指针

ListNode* getKthFromEnd(ListNode* head, int k){
  if(head==nullptr) return head;  
  ListNode *slow=head, *fast=head;
  for(int i=0; i<k; i++){
    if(fast==nullptr) return nullptr;
    fast=fast->next;
  }
  while(fast){
    slow=slow->next;
    fast=fast->next;
  }
  return slow;
}
BM9.删除链表的倒数第n个结点(19.删除链表中倒数第N个结点)[medium]

输入:head=[1,2,3,4,5],n=2
输出:[1,2,3,5]
---------------------快慢指针,注意头节点删除问题---------------------

ListNode* removeNthFromEnd(ListNode* head, int n){
  if(head==nullptr) return head;
  ListNode* slow=head, *fast=head;
  for(int i=0; i<n; i++){
    fast=fast->next;
  }
  if(fast==nullptr) return head->next;  //头节点删除问题
  while(fast->next){
    slow=slow->next;
    fast=fast->next;
  }
  slow->next=slow->next->next;
  return head;
}
BM10.两个链表的第一个公共结点(剑指Offer52.两个链表的第一个公共结点)[easy]

输入:headA,headB
输出:node or null
哈希或双指针

ListNode *getIntersectionNode(ListNode *headA, *headB){  //哈希
  if(!headA || !headB) return nullptr;
  unordered_set<ListNode*> hash;
  while(headA){
    hash.insert(headA);
    headA=headA->next;
  }
  while(headB){
    if(hash.count(headB)) return headB;
    headB=headB->next;
  }
  return nullptr;
}
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB){  //双指针
  if(!headA || !headB) return nullptr;
  ListNode *l1=headA, *l2=headB;
  while(l1 || l2){
    if(l1==l2) return l1;
    l1=l1 ? l1->next:headB;
    l2=l2 ? l2->next:headA;
  }
  return nullptr;
}
BM11.链表相加(二)(445.两数相加II)[medium]

输入:[9,3,7],[6,3]
输出:{1,0,0,0}
如果链表不是逆序首先反转链表,逆序相加,变量carry、sum

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2){
  if(l1==nullptr) return l2;
  if(l2==nullptr) return l1;
  ListNode* dummy=new ListNode(-1), *head=dummy;
  ListNode *r1=reverseList(l1);
  ListNode *r2=reverseList(l2);
  int sum=0, carry=0;
  while(r1 || r2){
    sum=carry;
    if(r1){
      sum+=r1->val;
      r1=r1->next;
    }
    if(r2){
      sum+=r2->val;
      r2=r2->next;
    }
    carry=sum/10;
    head->next=new ListNode(sum%10);
    head=head->next;
  }
  if(carry) head->next=new ListNode(1);
  return reverseList(dummy->next);
}
ListNode* reverseList(ListNode* head){
  if(head==nullptr) return head;
  ListNode *pre=nullptr, *cur=head;
  while(cur){
    ListNode* temp=cur->next;
    cur->next=pre;
    pre=cur;
    cur=temp;
  }
  return pre;
}
BM12.单链表的排序(148.排序链表)[medium]

输入:head=[-1,5,3,4,0]
输出:[-1,0,3,4,5]
归并排序,合并两个有序链表top101-p38

ListNode* merge(ListNode* l1, ListNode* l2){
  if(l1==nullptr) return l2;
  if(l2==nullptr) return l1;
  ListNode* dummy=new ListNode(-1), *head=dummy;
  while(l1 && l2){
    if(l1->val > l2->val){
      head->next=l2;
      l2=l2->next;
    }else{
      head->next=l1;
      l1=l1->next;
    }
    head=head->next;
  }
  head->next=l1? l1:l2;
  return dummy->next;
}
ListNode* sortInList(ListNode* head){
  if(head==nullptr || head->next==nullptr)  //空链表或只有一个元素的链表就是有序的
    return head;
  ListNode* left=head, *mid=head->next, *right=head->next->next;
  while(right!=nullptr && right->next!=nullptr){  //右边指针到达末尾时,中间的指针指向该段链表的中间
    left=left->next;
    mid=mid->next;
    right=right->next->next;
  }
  left->next=nullptr;   //从这里断开
  return merge(sortInList(head), sortInList(mid));  //分成两段排序,合并排好序的两段
}
BM13.判断一个链表是否为回文结构(234.回文链表)[easy]

输入:1->2->2->1,1->2
输出:true,false
链表数据存到数组里、快慢指针找中点,后半段反转,前后比较

bool isPalindrome(ListNode* head){
  if(head==nullptr || head->next==nullptr) return true;
  vector<int> nums;
  while(head){
    nums.push_back(head->val);
    head=head->next;
  }
  int l=0, r=nums.size()-1;
  while(l<r){
    if(nums[l++]!=nums[r--]) return false;
  }
  return true;
}
BM14.链表的奇偶重排(328.奇偶链表)[medium]

输入:head=[1,2,3,4,5]
输出:[1,3,5,2,4]
双指针

ListNode* oddEvenList(ListNode* head){
  if(head==nullptr) return head;
  ListNode *even=head->next;  //even开头指向第二个节点,可能为空
  ListNode *odd=head;  //odd开头指向第一个节点
  ListNode *evenhead=even;  //指向even开头
  while(even != nullptr && even->next != nullptr){
    odd->next=even->next;  //odd连接even的后一个,即奇数位
    odd=odd->next;  //odd进入后一个奇数位
    even->next=odd->next;  //even连接后一个奇数位的后一位,即偶数位
    even=even->next;  //even进入后一个偶数维
  }
  odd->next=evenhead;    //even整体接在odd后面
  return head;
}
BM15.删除有序链表中重复的元素-I(83.删除排序链表中重复的元素)[easy]

输入:1->1->2
输出:1->2
迭代

ListNode* deleteDuplicates(ListNode* head){
  if(head==nullptr) return head;
  ListNode *dummy=head;
  while(head!=nullptr && head->next!=nullptr){
    if(head->val == head->next->val){
      head->next=head->next->next;
    }else{
      head=head->next;    
    }
  }
  return dummy;
}
BM16.删除有序链表中重复的元素-II(82.删除排序链表中的重复元素II)[medium]

输入:1->2->3->3->4->4->5
输出:1->2->5 删除原始链表中所有重复的数字,只留下不同的数字。返回已排序的链表

ListNode* deleteDuplicates(ListNode* head){
  if(head==nullptr) return head;
  ListNode *dummy=new ListNode(-1);
  dummy->next=head;  //在链表前加一个表头
  ListNode* cur=dummy;
  while(cur->next!=nullptr && cur->next->next!=nullptr){
    if(cur->next->val == cur->next->next->val){  //如果遇到相同节点
      int temp=cur->next->val;
      while(cur->next!=nullptr && cur->next->val==temp){
        cur->next=cur->next->next;
      }else{
        cur=cur->next;
      }
    }
  }
  return dummy->next;  //返回时去掉表头
}