链表练习题集

 

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 *new_head = NULL;
        while(head)
        {
            ListNode *next = head->next;
            head->next = new_head;
            new_head=head;
            head=next;
        }
        return new_head;
        
    }
};

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-linked-list-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

/**
 * 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) {
    int change_len = n - m + 1;//要逆置链表数
    ListNode *pre_head = NULL;//开始逆置等等链表的前驱
    ListNode *result = head;//最终转换后链表的头结点
    while(head && --m)
    {
        pre_head = head;//记录head前驱
        head = head->next;
    }
    ListNode *modify_list_tail = head;
    ListNode *new_head = NULL;
    while(head && change_len)//逆置change_len个节点
    {
        ListNode *next = head->next;
        head->next = new_head;
        new_head = head;
        head = next;
        change_len--;//完成每一个节点逆置后change_len--;
    }
    modify_list_tail->next = head;//连接逆置后的链表尾部与逆置段的后一个节点
    if(pre_head)//如果pre不是NULL,说明不是从第一个节点开始的 M > 1
    {
        pre_head->next = new_head;//将逆置链表的开始节点与前驱相连
    }
    else
    {
        
        result = new_head;//如果是空说明从第一个节点开始逆置,结果就是头节点
    }
    return result;
    }
};

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) 内存。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 解题思路:

先求得A和B链表的长度,让指针走到两个链表一样长度的地方。

之后两个链表一起前进如果两个指针指向相同则说明有交点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int get_list_length(ListNode*head)
    {
        int len = 0;
        while(head)
        {
            len++;
            head = head ->next;
        }
        return len;
    }
    ListNode * forward_long_list(int long_len, int short_len, ListNode* head)
    {
        int delta = long_len - short_len;
        while(head && delta)
        {
            head = head->next;//将指针移动到多出节点个数后面的位置
            delta --;
        }
        return head;
    }
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int list_A_len = get_list_length(headA);
        int list_B_len = get_list_length(headB);
        if(list_A_len > list_B_len)
        {
            headA = forward_long_list(list_A_len,list_B_len,headA);
        }
        else
        {
            headB = forward_long_list(list_B_len,list_A_len,headB);
        }
        while(headA && headB)
        {
            if(headA == headB)
            {
                return headA;
            }
            headA = headA->next;
            headB = headB->next; 
        }
        return NULL;
    }
    
};

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
解释:链表中没有环。


来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 思路:利用快慢指针的方法来解决。

/**
 * 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) {
        ListNode *slow = head;
        if(slow == NULL)
        return false;
        
        ListNode *fast = head->next;
        
    
        while(fast && slow && fast->next)
        {
            if(fast == slow)
            {
                
                return true;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        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
解释:链表中没有环。


来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

(图略)

解题思路:首先找到一个环内的节点表明链表内确实有环,之后让链表内的相遇节点和链表外的头结点一起向前移动,这样相遇的节点就是链表的环开始的地方的节点

class Solution {
public:
     ListNode *detectCycle(ListNode *head) {
 ListNode *fast = head;
 ListNode *slow = head;
 ListNode *meet = nullptr;
 while  (fast)
 {
     slow = slow->next;
     fast = fast->next;
     if(!fast)
     {
         return nullptr;
     }
     fast = fast->next;
     
     if(fast == slow)
     {
         
         meet = slow;
         break;
     }
 }
 if(meet == nullptr)
 {
     return nullptr;
 }
 ListNode *start = head;
        while(start && meet)
        {
            if(start == meet)
            {
                return start;
            }
            start = start->next;
            meet = meet->next;
        }
        return NULL;
    }
};

86. 分隔链表

 

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:建立两个临时链表morehead和lesshead 使用尾插法 遍历所给的head链表 把小于的插入lesshead把大于等于的插入到morehead里。这样就可以得出符合条件的两个链表 最后把morehead插入lesshead的尾部就可以了。返回lesshead头的下一个节点。

/**
 * 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(0);
        ListNode more_head(0);
        ListNode *less_ptr = &less_head;
        ListNode *more_ptr = &more_head;
        while(head)
        {
            if(head->val < x)
            {
                less_ptr->next = head;
                less_ptr = less_ptr->next;
            }   
            else
            {
                more_ptr->next = head;
                more_ptr = more_ptr->next;
            }
            head = head->next;
        }
        less_ptr->next = more_head.next;
        more_ptr->next = nullptr;
        return less_head.next;
    }
};

 在下一个题开始之前先复习一下stl中的映射使用方法:

#include<stdio.h>
#include<queue>
#include<vector>
#include<functional>
#include<map>

using namespace std;
/*
class Node {
public:
    int val;
    Node* next;
    Node* random;

    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};


class Solution {
public:
    Node* copyRandomList(Node* head) {

    }
};
*/
struct RandomListNode
{
    int lable;
    RandomListNode *next, *random;
    RandomListNode(int x) : lable(x), next(NULL), random(NULL){}
};
int main()
{
map <RandomListNode* ,int> node_map;
RandomListNode a(2);
RandomListNode b(4);
RandomListNode c(6);

a.next = &b;
b.next = &c;

a.random = &c;
b.random = &a;
c.random = &c;

node_map[&a] = 1;
node_map[&b] = 2;
node_map[&c] = 3;

printf("a.random id is %d
b.random id is %d
c.random id is %d
",node_map[a.random],node_map[b.random],node_map[c.random]);

return 0;
}

 

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

 

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

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

我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。
 

示例 1:

 

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

 

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

 

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
 

提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

第一步:

遍历链表 每读到一个节点,就创建一个心的节点 把它放入vector中

把当前遍历的链表地址和对应遍历的链表数放入map中

第二步:

再次遍历链表,首先根据遍历的进度把新链表(也就是vector里面的链表)首尾相接

再根据遍历原链表 得到 对应当前 str所指向的 random指针域里存放(指向)的地址 所对应的map映射(也就是第几个)

再把新链表(vector中存放的)中的random指向对应(第几个)vector的地址。

这样就可以实现新链表的random链接了。

class Node {
public:
    int val;
    Node* next;
    Node* random;

    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};


class Solution {
public:
    Node* copyRandomList(Node* head) {
    map <Node*, int> node_map;//地址到节点位置map
    vector <Node*> node_vec;
    Node *ptr = head;//使用vector根据存储节点位置访问地址
    int i = 0;
    while(ptr)//将新链表节点push到node_vec中
    {
        node_vec.push_back(new Node(ptr->val));
        node_map[ptr] = i;
        ptr = ptr->next;
        i++;//遍历原始链表
    }
    node_vec.push_back(0);
    ptr = head;
    i=0;//再次遍历原始链表,连接新的链表的next指针,random指针
    while(ptr)
    {
        node_vec[i]->next = node_vec[i+1];//连接新链表的next
        if(ptr->random)//当random指针不为空的时候
        {
            int id = node_map[ptr->random];//根据node_map确认
            node_vec[i]->random = node_vec[id];//原链表random指向的位置id
        }
        ptr = ptr->next;//连接新链表random指针
        i++;
    }
    return node_vec[0];
    }
};

21. 合并两个有序链表

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

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

建立一个临时链表 两个指针指向链表的起始,之后分别遍历两个链表,把当前小的那个移动过去,再进行下一次判断。

注意的是,每次操作后对应操作的链表和新链表都要向后移动。

/**
 * 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 head(0);
    ListNode *ptr = &head;
    ListNode *p1 = l1, *p2 = l2;
    while(p1 && p2)
    {
        if(p1->val < p2->val)
        {
            ptr->next = p1;
            p1 = p1->next;

        }
        else
        {
            ptr->next = p2;
            p2 = p2->next;
        }

            ptr = ptr->next;
    }
    if(p1 == NULL && p2 != NULL)
    {
        ptr->next = p2;
    }
    else if(p2 == NULL && p1 != NULL)
    {
        ptr->next = p1;
    }
    else
    {
        ptr->next = NULL;
    }
    return head.next;
    }
};

 最后来一个升级版的练习

这里采用了的是归并思想,把N条链表进行分治处理最后归并也可以高效率的解决问题~

23. 合并K个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

/**
 * 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 head(0);
    ListNode *ptr = &head;
    ListNode *p1 = l1, *p2 = l2;
    while(p1 && p2)
    {
        if(p1->val < p2->val)
        {
            ptr->next = p1;
            p1 = p1->next;

        }
        else
        {
            ptr->next = p2;
            p2 = p2->next;
        }

            ptr = ptr->next;
    }
    if(p1 == NULL && p2 != NULL)
    {
        ptr->next = p2;
    }
    else if(p2 == NULL && p1 != NULL)
    {
        ptr->next = p1;
    }
    else
    {
        ptr->next = NULL;
    }
    return head.next;
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0)
            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*> sub1_lists;
        vector<ListNode*> sub2_lists;

        for(int i = 0; i < mid;i++)
        {
            sub1_lists.push_back(lists[i]);
        }
        for(int i = mid; i< lists.size();i++)
        {
            sub2_lists.push_back(lists[i]);
        }
        ListNode *l1 = mergeKLists(sub1_lists);
        ListNode *l2 = mergeKLists(sub2_lists);
        return mergeTwoLists(l1,l2);
    }
};
原文地址:https://www.cnblogs.com/KID-XiaoYuan/p/12217352.html