3专题总结-链表类题目

1、总结

链表类问题都是in-place,空间复杂度都是O(1)的,在所有的排序算法中,时间复杂度为O(nlogn)的有三个:

1)快速排序(不开辟空间);

2)归并排序(需要开辟O(n)的空间,但是在list问题上不开辟空间);

3)堆排序(首先需要有一个堆)。

 while里面写判断条件的时候规律:

如果接下来有head -> next,那么必须判断head是否为空,同理类推。

写链表的题目,一定要注意是否需要返回node,有些题目是void就是没有返回值的,自己经常没注意这个void就直接返回值了。

1.1、Remove Duplicates from Sorted List

https://leetcode.com/problems/remove-duplicates-from-sorted-list/#/description

移除重复元素,不全部删除!

思路:使用一个node,相等的时候就node -> next = node -> next -> next,不相等的时候node往后移位,这是个技巧,原来我的做法是使用两个指针,找到第一个不相等的节点就是node ->next.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == NULL){
            return head;
        }
        ListNode* node = head;
        while(node -> next != NULL){
            if(node -> val == node -> next -> val){
                node -> next = node -> next -> next;
            }
            else{
                node = node -> next;
            }
        }
        return head;
    }
};
delete Duplicates

1.2、Remove Duplicates from Sorted List II

https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/#/description

删除所有的的重复元素,包括自身。

思路:1)一直纠结while里面写head?? != NULL,记住:只要循环里面有哪个是->next->或者->val->的。那么就写这个不为空,比如这题需要在循环中判断head->next->val和head->next->next-->val,所以在while循环的判断中就需要写head->next !=NULL 而且head->next->next != NULL。

如果是head->next,和head->next->next就写while(head!=NULL&&head->next!=NULL)

2)dummy node标准写法:

哨兵节点使用于头结点不确定的情况,题目中可能需要单独对头结点进行操作,这时候就可以引入哨兵节点。

ListNode* dummy = new ListNode(0);

dummy = head;

head = dummy;

return dummy -> next;
dummy node template

上面的写法会造成内存泄露,推荐下面的写法:

 

ListNode dummy(0);//定义一个对象,使用默认构造函数
dummy.next = head;
head = &dummy;

return dummy.next;

 

3)因为本题需要知道前驱节点,这样才能将所有重复的节点去掉,所以需要使用head->next和head->next->next。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == NULL || head -> next == NULL){
            return head;
        }
        ListNode* dummy = new ListNode(0);
        dummy -> next = head;
        head = dummy;
        
       // ListNode* node;
      
        while(head -> next != NULL && head -> next -> next != NULL){
            if(head -> next -> val ==  head -> next ->next -> val){
                int flag = head -> next -> val;//这一步是技巧,有重复,就保留这个值,和值相等的都删除
                while(head -> next != NULL && head -> next -> val == flag ){
                    head -> next = head -> next -> next;
                }
            }
            else{
                head = head -> next;
            }
        }
        return dummy -> next;
        
    }
};
delete Duplicates 2

1.3  Reverse Linked List

思路:记得保存断开后的下一个节点。

https://leetcode.com/problems/reverse-linked-list/#/description

/**
 * 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* pre = NULL;
        ListNode* cur = head ;
        ListNode* post = head -> next;
        
        while(cur != NULL){
            post =cur -> next;
            cur -> next = pre;
            pre = cur;
            cur = post;
        }
       
        return pre;
    }
};
reverseList

1.4 Reverse Linked List II

https://leetcode.com/problems/reverse-linked-list-ii/#/description

思路:这题需要考虑m和n的关系,m为1的时候,是头结点,这时候很难处理,就需要引入dummy node,技巧点:因为多了一个节点,所以i=1,循环m-1次,找到m的前一个位置,循环里面判断是否为空的判断是为了判断m是否大于链表的长度。
接下来令nNode = mNode,循环n-m次,j =m.j < n。交换相邻节点,记得判断postnNode是否为空,为空则返回,而且也记得上面的总结,后面有postnNode -> next,所以要判断postnNode是否为空,为空则n大于链表长度,返回NULL。

/**
 * 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 == NULL || m >= n){
            return head;
        }
        
        ListNode* dummy = new ListNode(0);
        dummy -> next = head;
        head = dummy;
       
        for(int i = 1; i < m;++i){
            // if(head == NULL){
            //     return NULL;
            // }
            head = head -> next;
        }
        ListNode* premNode = head;
        ListNode* mNode = head -> next;
        ListNode* nNode = mNode;
        ListNode* postnNode = nNode -> next;
        ListNode* tmp;
        
        for(int j = m;j < n;++j){
            if(postnNode == NULL){
                return NULL;
            }
            tmp = postnNode -> next;
            postnNode -> next = nNode;
            nNode = postnNode;
            postnNode = tmp;
        }
        
        premNode -> next = nNode;
        mNode -> next = postnNode;
        return dummy -> next;
        
    }
};
reverse Between

反转链表递归和迭代版本

原题:leetcode 206. Reverse Linked List 

           Reverse a singly linked list.

迭代版本:

  思路:通过举例分析可以知道,在反转其中一个链表后,会发生断裂情况,没法知道下一个链节点,需要建立三个节点,所以需要首先保存后一个节点,然后将后一个结点的next指向前一个节点,接下来依次后退,pPre=pCur;pCur=pNext。

reverse List

 递归版本:思路和迭代差不多,只不过在最后两个节点交换的时候,使用递归版本实现。递归要考虑到原函数初始定义会在后面递归的时候不断的重新定义,这里思考出现卡顿,所以引入一个辅助函数。

 ListNode* helper(ListNode* pPre, ListNode* pCur) {
     ListNode* pNext(0), * pRevHead(0);    
     if (pCur == nullptr)
         return pPre;
     pNext = pCur->next;//首先保存下一结点值
     if (pNext == nullptr)
         pRevHead = pCur;
     pCur->next = pPre;//调整指针
     return  helper(pCur, pNext);
 }

 ListNode* reverseListRecur(ListNode* head) {     
     ListNode *pCur = head, *pPre(0), *pRevHead(0);
    
     if (head == nullptr) {
         return head;
     }
    
     pRevHead= helper(pPre, pCur);
     return pRevHead;
 }
reverse List Recur

1.5 Partition List

https://leetcode.com/problems/partition-list/#/description

将比x小的元素放在前面,小的元素放在后面。

思路:使用两个dummy node,分别记录比x大的数和比x小的数,最后将两者合并起来。链表的结尾必须要加上nullptr,这题dummyRight最后要dummyNode -> next = nullptr.

/**
 * 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) {
        if(head == NULL){
            return head;
        }
        ListNode* left = new ListNode(0);
        ListNode* right = new ListNode(0);
        ListNode* leftDummy = left;
        ListNode* rightDummy = right;
        
        while(head != NULL){
            if(head -> val < x){
                left -> next = head;
                left = left -> next;
            }
            else{
                right -> next = head;
                right = right -> next; 
            }
            head = head -> next;
        }
        left -> next = rightDummy -> next;
        right -> next = NULL;
        return leftDummy -> next;
    }
};
partition list

1.6 Sort List

https://leetcode.com/problems/sort-list/#/description

思路:记住归并算法在list结构中 ,所需要的空间复杂度是O(1)的,注意该题边界条件有两个变量需要考虑,不能漏写。

if(head == NULL || head -> next == NULL){//边界情况
      return head;
}

这题就使用归并算法。三步走:

1)找中间函数使用快慢指针找到链表的中间,slow = head ,fast = head -> next。快指针走两步,慢指针走一步,当fast为空的时候,就找到了slow = mid;

2)使用递归函数将问题分解,

ListNode* mergeRight = sortList(mid -> next);
mid -> next = NULL;
ListNode*  mergeLeft = sortList(head);

这里有个细节,就是要将mid-> next设为空,不然就会陷入死循环。先传给右边,也可以省掉一个节点。

3)新建dummy node和tail node,哪个大就将该节点赋到tail后面,最后返回dummy -> next。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* findMid(ListNode* head){
        ListNode* slow = head;
        ListNode* fast = head -> next;
        while(fast != NULL && fast -> next != NULL){
            fast = fast -> next -> next;
            slow = slow -> next;
        }
        return slow;
    } 
    ListNode* merge(ListNode* mergeLeft,ListNode* mergeRight){
        ListNode* dummy = new ListNode(0);
        ListNode* tail = dummy;
        
        while(mergeLeft != NULL && mergeRight != NULL){
            if(mergeLeft -> val < mergeRight -> val){
                tail -> next = mergeLeft;
                mergeLeft = mergeLeft -> next;
            }
            else{
                tail -> next = mergeRight;
                mergeRight = mergeRight -> next;
            }
            tail = tail -> next;
        }
        if(mergeLeft != NULL){
            tail -> next = mergeLeft;
        }
        if(mergeRight != NULL){
            tail -> next = mergeRight;
        }
        return dummy -> next;
    }
    
    ListNode* sortList(ListNode* head) {
        if(head == NULL || head -> next == NULL){//边界情况
            return head;
        }
        ListNode* mid = findMid(head);
        ListNode* mergeRight = sortList(mid -> next);
        mid -> next = NULL;
        ListNode*  mergeLeft = sortList(head);
        
        ListNode* result = merge(mergeLeft,mergeRight);
        return result;
    }
};
sort list

 1.7 Reorder List

https://leetcode.com/problems/reorder-list/#/description

思路:首先找中点,使用快慢指针;然后将右边的元素全部反转,参考reverse函数;最后将两个链表合并。注意点mid这个node属于left链表。  

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* findMid(ListNode* head){
        ListNode* slow = head;
        ListNode* fast = head -> next;
        while(fast != NULL && fast -> next != NULL){
            slow = slow -> next;
            fast =fast -> next -> next;
        }
        return slow;
    }
    ListNode* reverse(ListNode* head){
        ListNode* pre = NULL;
        ListNode* cur = head;
        ListNode* post = head -> next;
        while(cur != NULL){
            post = cur -> next;
            cur -> next = pre;
            pre = cur;
            cur = post;
        }
        return pre;
    }
    void reorderList(ListNode* head) {
        if(head == NULL || head -> next == NULL){
            return;
        }
        ListNode* mid = findMid(head);
        ListNode* right = reverse(mid -> next);
        
        mid -> next = NULL;
        ListNode* left = head;
        ListNode* dummy = new ListNode(0);
        ListNode* tail = dummy;
        
        while(left != NULL || right != NULL){
            if(left != NULL){
                tail -> next = left;
                left = left -> next;
                tail = tail -> next;
            }
           if(right != NULL){
                tail -> next = right;
                right = right -> next;
                tail = tail -> next;
           }
        }
        head = dummy -> next;
        
    }
};
reorder list

 2 Fast Slow Pointers

   2.1 有环链表问题总结

1)Linked List Cycle I

https://leetcode.com/problems/linked-list-cycle/#/description

思路:快慢指针,相交的话则有环

/**
 * 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 -> next;
        while(fast != NULL && fast -> next != NULL){
            slow = slow -> next;
            fast = fast -> next -> next;
            if(fast == slow){
                return true;
            }
        }
        return false;
    }
};
判断是否有环

2)Linked List Cycle II

https://leetcode.com/problems/linked-list-cycle-ii/#/description

思路:这题需要知道a = c,然后head和slow每次走一步,相遇的时候就是第一个入口交点,需要注意应该写head != slow -> next,如果写成head == slow,在两个元素组成的环中就会死循环

还有就是最后返回head, return head;//这里不能写成slow,因为两个元素的环就出错。

 

/**
 * 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) {
        if(head == NULL || head -> next == NULL){
            return NULL;
        }
        ListNode * fast = head -> next;
        ListNode * slow = head;
        while(fast != NULL && fast -> next != NULL){
            slow = slow -> next;
            fast = fast -> next -> next;
            if(fast == slow){
                break;
            }
           
        }
       
        if(fast == NULL ||fast -> next == NULL){
            return NULL;
        }
       
        while(head != slow -> next){//如果写成head == slow,在两个元素组成的环中就会死循环
           
            head = head -> next;
            slow = slow -> next;
            
        }
        
        return head;//这里不能写成slow,因为两个元素的环就出错
    }
};
找到环的入口

 

在网上搜集了一下这个问题相关的一些问题,总结如下:

1. 环的长度是多少?

2. 如何找到环中第一个节点(即Linked List Cycle II)?

3. 如何将有环的链表变成单链表(解除环)?

4. 如何判断两个单链表是否有交点?如何找到第一个相交的节点?

首先我们看下面这张图:

设:链表头是X,环的第一个节点是Y,slow和fast第一次的交点是Z。各段的长度分别是a,b,c,如图所示。环的长度是L。slow和fast的速度分别是qs,qf。

下面我们来挨个问题分析。

1. 方法一(网上都是这个答案):

第一次相遇后,让slow,fast继续走,记录到下次相遇时循环了几次。因为当fast第二次到达Z点时,fast走了一圈,slow走了半圈,而当fast第三次到达Z点时,fast走了两圈,slow走了一圈,正好还在Z点相遇。

方法二:

第一次相遇后,让fast停着不走了,slow继续走,记录到下次相遇时循环了几次。

方法三(最简单):

第一次相遇时slow走过的距离:a+b,fast走过的距离:a+b+c+b。

因为fast的速度是slow的两倍,所以fast走的距离是slow的两倍,有 2(a+b) = a+b+c+b,可以得到a=c(这个结论很重要!)。

我们发现L=b+c=a+b,也就是说,从一开始到二者第一次相遇,循环的次数就等于环的长度。

2. 我们已经得到了结论a=c,那么让两个指针分别从X和Z开始走,每次走一步,那么正好会在Y相遇!也就是环的第一个节点。

3. 在上一个问题的最后,将c段中Y点之前的那个节点与Y的链接切断即可。

4. 如何判断两个单链表是否有交点?先判断两个链表是否有环,如果一个有环一个没环,肯定不相交;如果两个都没有环,判断两个列表的尾部是否相等;如果两个都有环,判断一个链表上的Z点是否在另一个链表上。

如何找到第一个相交的节点?求出两个链表的长度L1,L2(如果有环,则将Y点当做尾节点来算),假设L1<L2,用两个指针分别从两个链表的头部开始走,长度为L2的链表先走(L2-L1)步,然后两个一起走,直到二者相遇。

 2.2 Merge k Sorted Lists(important)参考嘻唰唰的链接

https://leetcode.com/problems/merge-k-sorted-lists/#/description

思路1:使用优先级队列priority_queue的方法,先压入k个链表的头结点,定义比较函数,小顶堆(a>b,联想队列先进先出),然后选出哪个最小节点,就看看它的下一个节点是否为空,不为空的话就压入队列它的下一个节点。记得要时刻保证队列的大小为k。比较函数写入优先级队列的方法参考另外一个博客优先级队列。

时间复杂度为O(NlogK),

 

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    struct cmp{
        bool operator()(ListNode *a,ListNode *b){
            if(a != NULL && b != NULL){
                return a -> val > b-> val;
            }
        }
    };
    
    ListNode* mergeKLists(vector<ListNode*>& lists) {
       int n = lists.size();
       if(n == 0){
           return NULL;
       }
       priority_queue<ListNode *,vector<ListNode *>,cmp> que;
       
       for(int i = 0;i < n;++i){
           if(lists[i] != NULL){//保证压入优先级队列的是非空的链表节点,不然后面tmp-> next会出错
                que.push(lists[i]);
           }
       }
       ListNode *dummy = new ListNode(0);
       ListNode *tail = dummy;
       
       while(!que.empty()){
           ListNode *tmp = que.top();
           que.pop();
           tail -> next = tmp;
           tail = tail -> next;
           if(tmp -> next != NULL){//压入当前链表的下一个节点,保持链表的长度为k
               que.push(tmp -> next);
           }
       }
       
       return dummy -> next;
    }
};
priority_queue

思路2:采用递归中的分治算法,也是二分方法,先处理相等的时候,递归基,然后再归并。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergesort(ListNode* left,ListNode* right){
        ListNode* dummy = new ListNode(0);
        ListNode* tail = dummy;
        while(left != NULL && right != NULL){
            if(left -> val < right -> val){
                tail -> next = left;
                left = left -> next;
            }
            else{
                tail -> next = right;
                right = right -> next;
            }
            tail = tail -> next;
        }
        if(left != NULL){
            tail -> next = left;
        }
        if(right != NULL){
            tail -> next = right;
        }
        return dummy -> next;
    }
    ListNode* merge(vector<ListNode*>& lists,int start,int end){
        int mid;
        if(start == end){//mergesort先处理边界情况
            return lists[start];
        }
        
        mid = start + (end - start) / 2;
        ListNode* left = merge(lists,start,mid);
        ListNode* right = merge(lists,mid + 1,end);
        ListNode* tail = mergesort(left,right);
        
        return tail;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //mergeSort  binarySearch
        if(lists.size() == 0){
           return NULL; 
        }
        ListNode* result = merge(lists,0,lists.size() - 1);
        return result;
    }
};
mergesort

迭代版本:

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        if(lists.empty()) return NULL;
        int end = lists.size()-1;
        while(end>0) {
            int begin = 0;
            while(begin<end) {
                lists[begin] = merge2Lists(lists[begin], lists[end]);
                begin++;
                end--;
            }
        }
        return lists[0];
    }
    
    ListNode* merge2Lists(ListNode *h1, ListNode *h2) {
        ListNode *dummy = new ListNode(0), *tail = dummy;
        while(h1 && h2) {
            if(h1->val<=h2->val) {
                tail->next = h1;
                h1 = h1->next;
            }
            else {
                tail->next = h2;
                h2 = h2->next;
            }
            tail = tail->next;
        }
        tail->next = h1 ? h1 : h2;
        return dummy->next;
    }
};
iterator mergesort

 2.3 138. Copy List with Random Pointer

https://leetcode.com/problems/copy-list-with-random-pointer/#/description

思路:建立两个节点,哨兵节点最后返回结果,一个随着计算进行next,首先只要当前节点不为空,就不断将node压入一个哈希表unordered_map,这个map里面可以是原来表的node,value是新的node,接下来的循环是拷贝随机指针,Map[tmp] -> random = Map[tmp -> random];

/**
 * 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:
    /**
     * @param head: The head of linked list with a random pointer.
     * @return: A new head of a deep copy of the list.
     */
    RandomListNode *copyRandomList(RandomListNode *head) {
        // write your code here
        unordered_map<RandomListNode*, RandomListNode*> old2new;
        RandomListNode* dummy = new RandomListNode(-1);
        RandomListNode* tmp = head;
        RandomListNode* curr = dummy;
        
        while (tmp) {
            RandomListNode* newNode = new RandomListNode(tmp->label);
            old2new[tmp] = newNode;
            curr->next = newNode;
           
            curr = curr -> next;//如果这里写成newNode = newNode -> next,那么最后dummy->next 指向的是最后一个节点。而且下一个next对象还没创建,不知道指向谁,就会出错
            tmp = tmp->next;
        }
        tmp = head;
        while (tmp) {
            if (tmp->random) {
                old2new[tmp]->random = old2new[tmp->random];
            }
            tmp = tmp-> next;
        }
        return dummy->next;
    }
};
copy list with random point

 调整只需要O(1)空间复杂度的解法:

思路:三部曲:

1. 在原链表的每个节点后面拷贝出一个新的节点

2. 依次给新的节点的随机指针赋值,因为在第一步中,新节点和旧节点都指向相同的随机指针元素,这个赋值非常容易 cur->next->random = cur->random->next,要注意这里cur->random不能为空,初始化都为空,只有不为空的时候才需要调整。

3. 断开链表可得到深度拷贝后的新链表,这里需要注意的地方是

if(tmp->next != NULL){
//只有一个节点的时候1 -> 1' -> NULL;tmp -> next == NULL 
  tmp -> next = tmp -> next -> next;
}
/**
 * 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 *copyNext(RandomListNode *head){
         RandomListNode *h = head;
         while(h != NULL){
             RandomListNode *newNode = new RandomListNode(h -> label);
             newNode -> next = h -> next;
             newNode -> random = h -> random;
             h -> next = newNode;
             h = h -> next -> next;
         }
         return head;
     }
    
    RandomListNode *adjustRandomPoint(RandomListNode *head){
         RandomListNode *h = head;       
         while(h!= NULL){
             if( h -> random  != NULL){//开始都初始为空,只有随机指针不为空的时候才需要进行操作
                 h -> next -> random = h -> random -> next;
             }
             h = h -> next -> next;            
         }
         return head;
     }
    
    RandomListNode *splitLink(RandomListNode *head){
        RandomListNode *h = head -> next;       
        while(head != NULL){
           RandomListNode *tmp = head -> next;
           head -> next = tmp -> next;
           if(tmp->next != NULL){//只有一个节点的时候1 -> 1' -> NULL;tmp -> next == NULL 
                 tmp -> next = tmp -> next -> next;
           }
           head = head -> next;
        }
        return h;
    }
    

    RandomListNode *copyRandomList(RandomListNode *head) {
        if(head == NULL){
            return head;
        }
        RandomListNode *copyNextNode =  copyNext(head);
        RandomListNode *adjustRandonPointNode = adjustRandomPoint(copyNextNode);
        RandomListNode *result = splitLink(adjustRandonPointNode);
        return result;
    }
};
constant space copy list with random points

正常思路的好方法:记住要先保存head,因为后面要copy随机节点。

/**
 * 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) {
        if(head == nullptr){
            return head;
        }
        unordered_map<RandomListNode *,RandomListNode *> oldToNew;
        //copy next
        RandomListNode *dummy = new RandomListNode(0);        
        RandomListNode *p = dummy;
        RandomListNode *cur = head;
        
        while(cur != nullptr){
            RandomListNode *tmp = new RandomListNode(cur -> label);
            oldToNew[cur] = tmp;
            cur = cur -> next;
            dummy -> next = tmp;
            dummy = dummy -> next;
        }
        
        //copy Random
        cur = head;
        dummy = p -> next;
        
        while(cur != nullptr){
            if(cur -> random != nullptr){
                oldToNew[cur] -> random = oldToNew[cur -> random]; 
            }
            cur = cur -> next;
            dummy = dummy -> next;
        }
        return p -> next;
    }
    
    
};
正常思路简单版本

 

分析复杂度的方法:

看均到每个节点上的复杂度是多少,然后乘以N个节点,就可以得到总的时间复杂度。

 2.4 109. Convert Sorted List to Binary Search Tree

https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/#/description

排序链表转化为二叉搜索树

思路:二叉树中序递归遍历的思路,首先求出链表的长度,然后定义递归函数helper(*&head,start,end),参考了嘻唰唰的思路----->这里这个函数所做的是将*head为头的linked list构建成一个BST,然后返回BST的root,而同时,也将head移动到linked list中第end+1个节点。因为*head既是输入参数,也是返回参数,所以这里用到了指针的引用*&head。注意不能错写成了&*head。理解*&的方法是从右向左读:首先是一个引用,然后是一个对指针的引用,最后是一个对ListNode指针的引用。

那么当left subtree构建完成后,head指向了mid,构建mid树节点。然后后移head到right subtree在linked list中的头节点。继续递归构建right subtree.
 
跑一个例子:
linked list: 0->1->2->NULL
 
                                                             call (head(0), 0, 2)
                                                                    mid = 1
                                                             node(1), head(2)
                                                /                                               \
                       call (head(0), 0, 0)                                        call (head(2), 2, 2)
                               mid = 0                                                         mid = 2
                       node(0), head(1)                                           node(2), head(NULL)
                        /                    \                                              /                        \
call (head(0), 0, -1)            call (head(0), 1, 0)     call (head(2), 2, 1)          call (head(0), 2, 1)
return NULL                       return NULL               return NULL                    return NULL
 
最终结果:
    1
  /    \
0      2
需要注意的坑是: int len = 0;//len必须初始化为0,后面才能进行自增操作,head = head -> next;//记住链表分割的时候一定要修改传递的head节点,这里的递归有点难理解,先看递归基的情况,然后回溯的构造结果,每次返回给上一层的是该层的root。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode *helper(ListNode *&head,int start,int end){
       if(start > end){
           return NULL;
       }
       int mid = start + (end - start) / 2;
       TreeNode *leftNode = helper(head,start,mid - 1);
       TreeNode *root = new TreeNode(head -> val);
       head = head -> next;//记住链表分割的时候一定要修改传递的head节点
       TreeNode *rightNode = helper(head,mid + 1,end);
       root -> left = leftNode;
       root -> right = rightNode;
       return root;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        if(head == NULL){
            return NULL;
        }
        int len = 0;//len必须初始化为0,后面才能进行自增操作
        ListNode *cur = head;
        while(cur != NULL){
            cur = cur -> next;
            ++len;
        }
        TreeNode *result;
        result = helper(head,0,len - 1);
        return result;
    }
};
sortedListToBST

 思路2:可以直接使用全局变量,避免复杂的写法。

思路3:先找到中间点,然后找left,在找right,递归执行,每次需要使用快慢指针找到中间点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* toBST(ListNode *head,ListNode *tail){
        if(head == tail){//这里容易写错
            return nullptr;
        }
        ListNode* fast = head -> next;
        ListNode* slow = head;
        while(fast != tail && fast -> next != tail){
            fast = fast -> next -> next;
            slow = slow -> next;
        }
        
        TreeNode* newNode = new TreeNode(slow -> val);
        newNode -> left = toBST(head,slow);
        newNode -> right = toBST(slow -> next,tail);
        return newNode;
    }
    TreeNode* sortedListToBST(ListNode* head) {
        if(head == nullptr){
            return nullptr;
        }
        TreeNode* result = toBST(head,nullptr);
        return result;
    }
};
快慢指针版本

 

另一道简单的数组题和这个思路差不多,108. Convert Sorted Array to Binary Search Tree

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* helper(vector<int>& nums,int start,int end){
        if(start > end){
            return NULL;
        }
        int mid = start + (end - start) / 2;
        TreeNode* leftNode = helper(nums,start,mid - 1);
        TreeNode* root = new TreeNode(nums[mid]);
        TreeNode* rightNode = helper(nums,mid + 1,end);
        root -> left = leftNode;
        root -> right = rightNode;
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int len = nums.size();
        if(len == 0){
            return NULL;
        }       
        return helper(nums,0,len - 1);
    }
};
sortedArrayToBST

 

 

 

2.5 二叉搜索树与双向链表

https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

思路:题目中说要求不能创建任何新的结点,指的是不能new一个节点出来,但是可以创建局部变量。二叉树有左右两个指针,而链表也有前后两个指针,helper函数里面有两个节点,第二个pre是传引用的形式,最后的结果pre指向链表的最后一个节点,所以要得到头结点,需要不断的pre = pre -> left;

1)先将左子树构建成一个链表

2)root -> left = pre;pre -> right = root

pre = root ;

3)接下来转入右子树,将右子树转化为链表,递归执行

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    void helper(TreeNode* root,TreeNode*& pre){
        if(root == nullptr ){
            return;
        }
        helper(root -> left,pre);
        root -> left = pre;
        if(pre != nullptr){
            pre -> right = root;
        }
        pre = root;//记住pre始终指向当前节点的前一个节点
        helper(root -> right,pre);
    }
    TreeNode* Convert(TreeNode* root){
        if(root == nullptr ){
            return root;
        }
        if(root -> left == nullptr && root -> right == nullptr){
            return root;
        }
        TreeNode* pre = nullptr;
        helper(root,pre);
        TreeNode* head = pre;
        while(head != nullptr && head -> left != nullptr){
            head = head -> left;
        }
        return head;
    }
};
二叉搜索树与双向链表

2.6 25. Reverse Nodes in k-Group

https://leetcode.com/problems/reverse-nodes-in-k-group/#/description

思路:这题我是联想给定一个链表,反转第m和n之间链表的解法,在画图分析的基础上,增加了一个外层循环,循环次数是k/listLen;因为要修改第一个节点,所以需要一个dummy node,

每次调整k之后,需要将prem和m节点进行调整:

prem = m;
m = postn;

还有记得循环的次数可以通过举两个节点的简单例子进行确定。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int getLen(ListNode *head){
         int len = 0;
         while(head != nullptr){
             head = head -> next;
             ++len;
         }
         return len;
     }
    ListNode* reverseKGroup(ListNode* head, int k) {
         int listLen = getLen(head);
        if(head == nullptr || k > listLen){
            return head;
        }
        
        ListNode *dummy = new ListNode(0);
        dummy -> next = head;
        head = dummy;
        
        int times = listLen / k;
        ListNode *prem = head;
        ListNode *m = head -> next;
       
        
        for(times;times > 0;--times){
            ListNode *n = m;
            ListNode *postn = n -> next;
            for(int i = 1;i < k && postn != nullptr;++i){
                 ListNode *tmp = postn -> next;
                 postn -> next = n;
                 n  = postn;
                 postn = tmp;
            }
            prem -> next = n;
            m -> next = postn;
            
           prem = m;
           m = postn;
        }
        return dummy -> next;
    }
};
Reverse Nodes in k-Group

 

posted @ 2017-06-05 11:29  zqlucky  阅读(423)  评论(0编辑  收藏  举报