一、链表(小象)

目录

206.反转链表(链表逆序)(easy)

92.反转链表II(medium)

160.相交链表(easy)

142.环形链表II(medium)

86.分割链表(medium)

138.复制带随机指针的链表(复杂链表的复制)(hard)

21.合并两个有序链表(easy)

23.合并K个排序链表(hard)


206.反转链表(链表逆序)(easy)

要求:不可以额外申请空间

链表翻转操作的顺序对于迭代来说是从链头往链尾,而对于递归是从链尾往链头。

思考:如果要反转链表,比如[1->2->3]这么一个链表,操作就是把1->2 给变成 2->1就可以了,但是这样就出现个问题,2->1 ,那么怎么再通过这个链表去3呢?因为2->3这个链已经断开了,所以这个问题有个关键操作就是“把当前结点的next用一个指针给保存起来”,这样改变了链接方向也不怕了,再把下一个指针赋值给head就可以了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head) return NULL;
        ListNode* new_head = NULL;
        while(head){
            ListNode *p = head->next; //首先要保存当前结点的下一个结点的地址
            head->next = new_head;//把当前的结点反逆转
            new_head = head ;//新链表的首部已经变化了
            head = p;//当前结点再前往下一个结点
        }
        return new_head;
    }
};

递归:首先指针H迭代到底如下图所示,并且设置一个新的指针作为翻转后的链表的头。由于整个链表翻转之后的头就是最后一个数,所以整个过程NewH指针一直指向存放5的地址空间。

然后H指针逐层返回的时候依次做下图的处理,将H指向的地址赋值给H->next->next指针,并且一定要记得让H->next =NULL,也就是断开现在指针的链接,否则新的链表形成了环,下一层H->next->next赋值的时候会覆盖后续的值。

递归的解法转载了(https://blog.csdn.net/fx677588/article/details/72357389)文章中的部分。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution { 
        public: 
    ListNode* reverseList(ListNode* head)
    {
       if(head == NULL||head->next ==NULL)
           return head;
        ListNode* newhead = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return newhead;
    }
};

 

92.反转链表II(medium)

要求:不可以额外生成空间

思路:和第一个反转链表一样,但是这里有四个关键的结点要注意,1、反转链表(m,n)的前一个结点 2、位置m的节点 3 位置n的节点 4 位置n+1 的结点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if(!head) return NULL;
        ListNode* ori_head = head;
        ListNode* preHead =NULL;
        int change_len = n-m+1;
        while(head&&--m){
            preHead = head;
            head = head->next;
        }
        ListNode* reverseTail = head;
        ListNode* newHead = NULL;
        while(head&& change_len){
            ListNode* p =head->next;
            head->next = newHead;
            newHead = head;
            head = p;
            change_len--;
        }
        ListNode* reverseHead = newHead ;
        ListNode* lastTail = head;
         reverseTail->next = lastTail;
        if(preHead) //如果不是从头开始逆转,prehead = NULL
        {
            preHead->next = reverseHead;
        }
        else{
            ori_head = reverseHead; 
        }
        return ori_head;
    }
};

160.相交链表(easy)

思考:

方法一:采用 set,存储下来linklistA的各个结点的地址,然后对LinkList的每个结点地址进行查询,如果查的到,说明两个节点有相交;否则没有相交。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        set<ListNode *> address_set;
        while(headA){
           address_set.insert(headA); 
            headA = headA->next;
        }
        while(headB){
            if(address_set.find(headB)!=address_set.end()){
                return headB;
            }
               headB = headB->next;
        }
        return NULL;
    }
};

 

方法二:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int getListLength(ListNode *headA){   
        ListNode *p = headA;int lengthA = 0;
          while(p!=NULL){lengthA++;
          p=p->next;
          }
        return lengthA;
     }
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA == NULL||headB == NULL)
            return NULL;
    int lengthA =  getListLength(headA);
    int lengthB =  getListLength(headB);

       ListNode * p = headA;
       ListNode * q = headB;
        int step = lengthA - lengthB;
        if(step==0){
            while(p!=q)
            {
                if(p==NULL)
                    return NULL;
                else{
                p=p->next;
                q=q->next;
                }
            }
            return p;
        }    
        else{
            if(step >0){
                for(int i =1 ;i<=step;i++)
                {
                  p = p->next;  
                }
            while(p!=q)
            {
                if(p==NULL)
                    return NULL;
                else{
                p=p->next;
                q=q->next;
                }
            }
            return p;
            }
            else{
                for(int i =step ;i<0;i++)
                {
                 q = q->next;  
                }
            while(p!=q)
            {
                if(p==NULL)
                    return NULL;
                else{
                p=p->next;
                q=q->next;
                }
            }
            return p;}
            }
    }
};

 

142.环形链表II(medium)

思考:这个题可以设置快慢指针,fast每次走两步,slow每次走一步,如果有环两者一定会相遇。(就像我们在操场跑步,跑得快的同学肯定还会在追上那个跑的慢的同学)。

141.环形链表I有个问题我每次容易犯错误,就是如果没有环,那fast越跑越远和slow不是永远不会相交了?

答:首先判断while(head&&head->next ),只要两者为空就停止循环,这就说明了链表没有环;其次再判断slow 和fast相等吗?相等就说明存在环。

附141.环形链表I代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
      if(head == NULL||head->next == NULL){ 
          return false;
      }
        ListNode *slow = head;
        ListNode *fast = head;
        while(fast&&fast->next){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast ){
                return true;
            }
        }
        return false;
    }
};

思路一:利用set来判断,把链表插入进set,当第一个重复出现的结点就是需要查找的。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        set<ListNode*> address_set;
        while(head){
        if(address_set.find(head)!= address_set.end()){
            return head;
        }
            address_set.insert(head);
            head = head->next;
        }
        return NULL;
    }
};

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        ListNode *meet = NULL;
         if(head==NULL||head->next==NULL){
                return NULL;
            }
        while(fast&&fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(slow == fast){
                meet = fast;
                break;
            }
        }
            if(meet == NULL){
                return NULL;
            }
            while(head != meet){
                head = head->next;
                meet = meet->next;
            }
            return head;
        }
    
};

86.分割链表(medium)

思路:创建两个临时结点,要注意的一点,临时结点是结构体,如果要查找下一个结点的地址是用'.'而不是'->'

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        //创建头结点而不是头结点的指针
        //ListNode* less_head = new ListNode(0);
        //ListNode* more_head = new ListNode(0);
        ListNode less_head = ListNode(0);
        ListNode more_head = ListNode(0);
        ListNode* p = &less_head;
        ListNode* q = &more_head;
        while(head){
            if(head->val < x){
                p->next = head;
                //head = head->next;
                //两个操作都需要head指针向后移动 所以可以写在if else外面
                p = head;
            }
            else{
                q->next = head;
                q = head;
            }
             head = head->next;
        }
        //p->next = &more_head; 是和more_head 的下一个结点的地址相链接
        p->next = more_head.next;
        q->next = NULL;
        //return less_head->next; 结点要以结构体的形式去访问
        return less_head.next;
        
    }
};

 

138.复制带随机指针的链表(复杂链表的复制)(hard)

思考: 

深度拷贝:生成一个全新的链表和原来的链表毫无关系(改变旧的不影响新的,改变新的不影响旧的)。

难点:比如第一个节点随机指向第三个节点的地址,但是重新创造一个全新链表的时候,你的地址已经变了,靠地址信息无法再找到对应的结点了。所以应该想到MAP映射,把地址和第几个节点映射起来,只要存在结点之间的对应关系,就不怕找不到了。

把链表A的所有地址标号1,2,...。建立 (地址->编号)的映射。

所以a->random也是地址,直接可以找到它对应哪个结点。

map<type1,type2>  --->建立type1和type2的映射。

/**
 * Definition for singly-linked list with a random pointer.
 * struct RandomListNode {
 *     int label;
 *     RandomListNode *next, *random;
 *     RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        map<RandomListNode *,int> node_map;
        vector<RandomListNode*> node_vec;
        int i = 0;
        RandomListNode* ptr = head;
        while(ptr){
            node_vec.push_back(new RandomListNode(ptr->label));
            node_map[ptr] = i;
            ptr = ptr->next;
            i++;
        }
          node_vec.push_back(NULL);
        ptr = head; i = 0;
        while(ptr){
            node_vec[i]->next = node_vec[i+1];
            if(ptr->random){
                int corresponse_node = node_map[ptr->random];
               node_vec[i]->random = node_vec[corresponse_node];
            }
            ptr = ptr->next;
            i++;
        }
        return node_vec[0];
    }
};

21.合并两个有序链表(easy)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode l(0);
        ListNode* ptr = &l;
        while(l1&&l2){
            if(l1->val <= l2->val){
                ptr->next= l1;
              //  ptr = l1;
                l1 = l1->next;
            }
            else{
                ptr->next = l2;
                //ptr = l2;
                l2 = l2->next;
            }
            ptr =ptr->next; //合成新链表,无论是list1 还是list2 的结点 新链表的当前结点指针都要向后指一位
        }
            if(l1){
                ptr->next = l1;
            }
            if(l2){
                ptr->next = l2;
            }
        
            return l.next;
    }
};

23.合并K个排序链表(hard)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode l(0);
        ListNode* ptr = &l;
        while(l1&&l2){
            if(l1->val <= l2->val){
                ptr->next= l1;
              //  ptr = l1;
                l1 = l1->next;
            }
            else{
                ptr->next = l2;
                //ptr = l2;
                l2 = l2->next;
            }
            ptr =ptr->next;
        }
            if(l1){
                ptr->next = l1;
            }
            if(l2){
                ptr->next = l2;
            }
        
            return l.next;
    }
    
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.empty()){
            return NULL;
        }
        if(lists.size()==1){
            return lists[0];
        }
        if(lists.size()==2){
            return mergeTwoLists(lists[0],lists[1]);
        }
        int mid = lists.size()/2;
        vector<ListNode*> sub_list1;
        vector<ListNode*> sub_list2;
        for(int i = 0;i<mid;i++){
            sub_list1.push_back(lists[i]);
        }
        for(int i = mid;i<lists.size();i++){
            sub_list2.push_back(lists[i]);
        }
         ListNode* l1 = mergeKLists(sub_list1);
         ListNode* l2 = mergeKLists(sub_list2);
        return  mergeTwoLists(l1,l2);
    }
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值