链表

一.单链表

707.设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 nextval 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

 

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3
class MyLinkedList {
private:
    struct ListNode{
        int val;
        ListNode *next;
    };
    ListNode *head;
public:
    /** Initialize your data structure here. */
    MyLinkedList() {
        head = NULL;
    }
    
    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index) {
        if(index < 0)
            return -1;
        ListNode *temp = head;
        for(int i = 0; i < index; i++)
        {
            if(temp == NULL)
                return -1;
            temp = temp->next;
        }
        if(temp == NULL)
            return -1;
        return temp->val;
    }
    
    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val) {
        ListNode *temp = new ListNode;
        temp->val = val;
        temp->next = NULL;
        if(head == NULL)
            head = temp;
        else
        {
            temp->next = head;
            head = temp;
        }
    }
    
    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val) {
        ListNode *temp = new ListNode;
        temp->val = val;
        temp->next = NULL;
        if(head == NULL)
            head = temp;
        else
        {
            ListNode *p = head;
            while(p->next != NULL)
                p = p->next;
            p->next = temp;
        }
    }
    
    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    void addAtIndex(int index, int val) {
        ListNode *temp = new ListNode;
        temp->val = val;
        temp->next = NULL;
        if(head == NULL && index == 0)
        {
            head = temp;
            return;
        }
        if(index == 0)
        {
            temp->next = head;
            head = temp;
            return;
        }
        
        ListNode *p = head;
        for(int i = 0; i < index-1 ; i++)
        {
            if(p == NULL)
                return;
            p = p->next;
        }
        if(p == NULL)
            return ;
        ListNode *q = p->next;
        temp->next = q;
        p->next = temp;
        return;
    }
    
    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index) {
        if(head == NULL)
            return;
        ListNode *temp = head;
        if(index == 0)
        {
            head = temp->next;
            temp->next = NULL;
            delete(temp);
        }
        for(int i = 0; i < index -1; i++)
        {
            if(temp == NULL)
                return;
            temp = temp->next;
        }
        if(temp == NULL || temp->next == NULL)
            return;
        ListNode *p = temp->next;
        temp->next = p->next;
        p->next = NULL;
        delete(p);
    }
};

二.双指针

141.环形链表

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

 

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

 

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

/**
 * 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)
            return false;
        ListNode *fast = head, *slow = head;
        while(fast->next != NULL && fast->next->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow)
                return true;
        }
        return false;
    }
};

142.环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

说明:不允许修改给定的链表。

 

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

进阶:
你是否可以不用额外空间解决此题?

第一种方法:快指针先走一圈求出环的长度,然后快指针先走环的长度,然后慢指针出发。快慢指针的相交点就是环的入口。

第二种方法:初始化slow指针指向head节点,fast指向head->next节点,假设slow与fast在c点相遇。由于slow走过的路程为x+y,fast走过的路程为x+y+z+y,且fast走过的距离是slow的两倍,所以:2(x+y)=x+y+z+y

化简得:x=z,此时把slow指针放到c处,fast指针放到a处,俩指针以相同速度向前走,则相遇节点为b,且b为相遇节点。


 

/**
 * 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 * ret = hasCycle(head);
        if(ret == NULL)
            return NULL;
        ListNode *p = head;
        while(p != ret)
        {
            p = p->next;
            ret = ret->next;
        }
        return p;
    }
    
    ListNode *hasCycle(ListNode *head) {
        if(head == NULL)
            return NULL;
        ListNode *fast = head, *slow = head;
        while(fast->next != NULL && fast->next->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow)
                return fast;
        }
        return NULL;
    }
};

160.相交链表

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表

在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

第一种方法,使用两个栈,出栈比较不相等的两个节点的前一个节点就是交点,时间和空间复杂度都是O(n)

/**
 * 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) {
        if(headA == NULL || headB == NULL)
            return NULL;
        stack<ListNode*>listA, listB;
        while(headA != NULL)
        {
            listA.push(headA);
            headA = headA->next;
        }
        while(headB != NULL)
        {
            listB.push(headB);
            headB = headB->next;
        }
        
        ListNode *pre = NULL;
        while(!listA.empty() && !listB.empty())
        {
            ListNode *tempA = listA.top();
            ListNode *tempB = listB.top();
            if(tempA != tempB)
                break;
            pre = tempA;
            listA.pop();
            listB.pop();
        }
    
        return pre ? pre : NULL;
    }
};

第二种方法,分别计算两条链表的长度,长的那条先往前移动,然后两条链表同时移动,直到找到相同的交点。

/**
 * 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) {
        if(headA == NULL || headB == NULL)
            return NULL;
        int countA = 0;
        int countB = 0;
        ListNode *tempA = headA, *tempB = headB;
        while(tempA != NULL)
        {
            countA++;
            tempA = tempA->next;
        }
        while(tempB != NULL)
        {
            countB++;
            tempB = tempB->next;
        }
        int n = 0;
        tempA = headA;
        tempB = headB;
        if(countA > countB)
        {
            n = countA - countB;
            for(int i = 0; i < n;i++)
                tempA = tempA->next;
        }
        else
        {
            n = countB - countA;
            for(int i = 0; i < n;i++)
                tempB = tempB->next;
        }
        while(tempA != NULL && tempB!= NULL)
        {
            if(tempA == tempB)
                return tempA;
            tempA = tempA->next;
            tempB = tempB->next;
        }
        return NULL;
    }
};

19.删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

思路:使用快慢指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(head == NULL)
            return NULL;
        ListNode *Dummy = new ListNode(0);
        Dummy->next = head;
        ListNode *first = head, *second = Dummy; 
        for(int i = 0; i < n - 1; i++)
        {
            if(first->next == NULL)
                return head;
            first = first->next;
        }
        while(first->next != NULL)
        {
            first = first->next;
            second = second->next;
        }
        ListNode *temp = second->next;
        second->next = temp->next;
        return Dummy->next;
    }
};

三.经典问题

206.反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

第一种方法:迭代法

/**
 * 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) {
        ListNode *Dummy = new ListNode(0);
        while(head != NULL)
        {
            ListNode *temp = new ListNode(head->val);
            temp->next = Dummy->next;
            Dummy->next = temp;
            head = head->next;
        }
        return Dummy->next;
    }
};
/**
 * 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)
            return head;
        ListNode *Dummy = new ListNode(0);
        Dummy->next = head;
        while(head->next != NULL)
        {
            ListNode *node = head->next;
            head->next = node->next;
            
            node->next = Dummy->next;
            Dummy->next = node;
        }
        return Dummy->next;
    }
};

第二种方法:递归

class Solution {
public:   
    ListNode* reverseList(ListNode* head) 
	{
            return reverse(head,NULL);
        }
	ListNode * reverse(ListNode *head,ListNode *newNode)
	{
		if(head==NULL)
			return newNode;
		ListNode *next=head->next;
		head->next=newNode;
		return reverse(next,head);
	}
};

203.移除链表元素

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        while(head != NULL)
        {
            if(head->val == val)
            {
                ListNode *p = head;
                head = head->next;
                p->next = NULL;
                delete(p);
            }
            else
                break;
        }
        ListNode *p = head;
        while(p != NULL && p->next != NULL)
        {
            if(p->next->val == val)
            {
                ListNode *q = p->next;
                p->next = q->next;
                q->next = NULL;
                delete(q);
            }
            else
                p = p->next;
        }
        return head;
    }
};

328.奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

示例 2:

输入: 2->1->3->5->6->4->7->NULL 
输出: 2->3->6->7->1->5->4->NULL

双指针,一个指向奇数节点,一个指向偶数节点。

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return head;
        ListNode *odd = head, *even = head->next;
        ListNode *temp = even;
        while(temp != NULL && temp->next != NULL)
        {
            ListNode *node = temp->next;
            temp->next = node->next;
            node->next = even;
            odd->next = node;
            
            temp = temp->next;
            odd = odd->next;
        }
        return head;
    }
};

234.回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

第一种方法:使用O(n)空间的数组,然后双指针来判断

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<int>list;
        while(head != NULL)
        {
            list.push_back(head->val);
            head = head->next;
        }
        int i = 0, j = list.size()-1;
        while(i < j)
        {
            if(list[i] != list[j])
                return false;
            i++;
            j--;
        }
        return true;
    }
};

第二种方法:空间复杂度O(1),先用快慢指针找到链表的中点,然后逆置后半部分并且和前半部分比较

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

四.总结

让我们简要回顾一下单链表和双链表的表现。

它们在许多操作中是相似的。

  1. 它们都无法在常量时间内随机访问数据
  2. 它们都能够在 O(1) 时间内在给定结点之后或列表开头添加一个新结点
  3. 它们都能够在 O(1) 时间内删除第一个结点

但是删除给定结点(包括最后一个结点)时略有不同。

  • 在单链表中,它无法获取给定结点的前一个结点,因此在删除给定结点之前我们必须花费 O(N) 时间来找出前一结点。
  • 在双链表中,这会更容易,因为我们可以使用“prev”引用字段获取前一个结点。因此我们可以在 O(1) 时间内删除给定结点。

21.合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

第一种方法递归法

/**
 * 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) {
        if(l1 == NULL)
            return l2;
        else if(l2 == NULL)
            return l1;
        else if(l1->val < l2->val)
        {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        else
        {
            l2->next= mergeTwoLists(l1, l2->next);
            return l2;
        }
            
    }
};

第二种方法:迭代法

/**
 * 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 *ret = new ListNode(0);
        ListNode *temp = ret;
        while(l1 && l2)
        {
            if(l1->val < l2->val)
            {
                temp->next = l1;
                l1 = l1->next;
            }
            else
            {
                temp->next = l2;
                l2 = l2->next;
            }
            temp = temp->next;
        }
        if(l1)
            temp->next = l1;
        if(l2)
            temp->next = l2;
        return ret->next;
    }
};

2.两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *dummy = new ListNode(-1), *cur = dummy;
        int carry = 0;
        while (l1 || l2) {
            int val1 = l1 ? l1->val : 0;
            int val2 = l2 ? l2->val : 0;
            int sum = val1 + val2 + carry;
            carry = sum / 10;
            cur->next = new ListNode(sum % 10);
            cur = cur->next;
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }
        if (carry) cur->next = new ListNode(1);
        return dummy->next;
    }
};

430.扁平化多级双向链表

您将获得一个双向链表,除了下一个和前一个指针之外,它还有一个子指针,可能指向单独的双向链表。这些子列表可能有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。

扁平化列表,使所有结点出现在单级双链表中。您将获得列表第一级的头部。

示例:

输入:
 1---2---3---4---5---6--NULL
         |
         7---8---9---10--NULL
             |
             11--12--NULL

输出:
1-2-3-7-8-11-12-9-10-4-5-6-NULL

思路:这道题直接用栈就阔以搞定:

1.如果当前节点有孩子节点,把当前节点的下一个节点放入栈中,并且把当前节点的下一个节点指向孩子节点

2.否则如果当前节点的下一个节点为空且栈不为空(已经处理完所有的带孩子节点的节点,现在已经到了孩子节点的最后一个节点),那么当前节点的下一个节点指向栈顶节点。

3。每次都更新当前节点为下一个节点

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* prev;
    Node* next;
    Node* child;

    Node() {}

    Node(int _val, Node* _prev, Node* _next, Node* _child) {
        val = _val;
        prev = _prev;
        next = _next;
        child = _child;
    }
};
*/
class Solution {
public:
    Node* flatten(Node* head) {
        if(head == NULL)
            return head;
        stack<Node*> s;
        Node *p = head;
        while(p)
        {
            if(p->child)
            {
                s.push(p->next);
                p->next = p->child;
                if(p->next)
                    p->next->prev = p;
                p->child = NULL;
            }
            else if(p->next == NULL && !s.empty())
            {
                p->next = s.top();
                s.pop();
                if(p->next)
                    p->next->prev = p;
            }
            p = p->next;
        }
        return head;
    }
};

138.复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深拷贝。 

 

示例:

输入:
{"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1}

解释:
节点 1 的值是 1,它的下一个指针和随机指针都指向节点 2 。
节点 2 的值是 2,它的下一个指针指向 null,随机指针指向它自己。

提示:

  1. 你必须返回给定头的拷贝作为对克隆列表的引用。

第一种方法,使用hash表存储新节点和原节点。第一遍遍历生成所有新节点时同时建立一个原节点和新节点的哈希表,第二遍给随机指针赋值时,查找时间是常数级。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;

    Node() {}

    Node(int _val, Node* _next, Node* _random) {
        val = _val;
        next = _next;
        random = _random;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == NULL)
            return NULL;
        Node* Dummy = new Node(0, NULL, NULL);
        Node* p = Dummy;
        Node* q = head;
        unordered_map<Node*, Node*>nodemap;
        while(q != NULL)//拷贝next指针
        {
            Node* temp = new Node(q->val, NULL, NULL);
            nodemap.insert(pair<Node*, Node*>(q, temp));
            p->next = temp;
            p = p->next;
            q = q->next;
        }
        
        p = Dummy->next;
        q = head;
        while(q != NULL)//拷贝random指针
        {
            p->random = nodemap[q->random];
            p = p->next;
            q = q->next;
        }
        return Dummy->next;
    }
};

方法二:将每个节点都复制一遍,新节点放在原节点的后面,然后拷贝随机指针,副本节点的随机指针是原节点的随机指针的下一个节点;拷贝完成后将副本分离出来。空间复杂度为O(1)

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;

    Node() {}

    Node(int _val, Node* _next, Node* _random) {
        val = _val;
        next = _next;
        random = _random;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == NULL)
            return NULL;
        Node *p = head;
        //先复制每一个节点,如原来是1->2->3->4,复制后变为1->1'->2->2'->3->3'->4->4'
        while(p)
        {
            Node *temp = new Node(p->val, NULL, NULL);
            temp->next = p->next;
            p->next = temp;
            p = p->next->next;//走两步,跨过新加入的节点  
        }
        //复制Random指针
        p = head;
        while(p != NULL)
        {
            if(p->random)
                p->next->random = p->random->next;
            p = p->next->next;
        }
        //分离两个链表:即分为1->2->3->4和1'->2'->3'->4'
        p = head;
        Node *res = new Node(0, NULL, NULL);
        Node *q = res;
        while(p != NULL)
        {
            Node *temp = p->next;
            if(temp)
                p->next = temp->next;
            q->next = temp;
            q = temp;
        }
        return res->next;
    }
};

61.旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 个位置,其中 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

第一种方法:使用快慢指针,找到后面的一段链表,后面链表的尾结点指向head

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head == NULL || k == 0)
            return head;
        int count = 0;
        ListNode *p = head;
        while(p)
        {
            p = p->next;
            count++;
        }
        k = k % count;
        if(k == 0)
            return head;
        
        ListNode *fast = head, *slow = head;
        for(int i = 0; i < k; i++)
            fast = fast->next;
        while(fast->next != NULL)
        {
            fast = fast->next;
            slow = slow->next;
        }
        
        ListNode *second = slow->next;
        p->next = NULL;
        ListNode *q = second;
        while(q->next != NULL)
            q = q->next;
        q->next = head;
        return second;
    }
};

第二种方法:先遍历整个链表获得链表长度n,然后此时把链表头和尾链接起来,再往后走n - k % n个节点就到达新链表的头结点前一个点,这时断开链表即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head == NULL || k == 0)
            return head;
        ListNode *cur = head;
        int count = 1;
        while(cur->next)
        {
            count++;
            cur = cur->next;
        }
        cur->next = head;//变成环
        
        k = k % count;
        int n = count-k;
        for(int i = 0; i < n;i++)
            cur = cur->next;
        ListNode *newNode = cur->next;//新的头结点
        cur->next = NULL;//断开环
        return newNode;
    }
};

237.删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

现有一个链表 -- head = [4,5,1,9],它可以表示为:

示例 1:

输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

  • 链表至少包含两个节点。
  • 链表中所有节点的值都是唯一的。
  • 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
  • 不要从你的函数中返回任何结果。

思路:将当前节点的值替换为下一个节点,删掉当前节点的下一个节点。

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值