leetcode刷题链表-第1刷

Posted 非晚非晚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode刷题链表-第1刷相关的知识,希望对你有一定的参考价值。

为了提高自己理解算法和编程的能力,在这里会时不时的刷一下LeetCode的题目,每一期十道题。坚持就是胜利,希望自己能够一直坚持把题目刷下去,加油!

1. 两数相加

题目:给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储 一位 数字。

解题思路:加法运算,主要考察链表的熟悉程度

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    	//先算一个加法
        ListNode* head = new ListNode( (l1->val + l2->val) % 10 );
        ListNode* tail = head;
        int flag = (l1->val + l2->val) / 10;
        l1 = l1->next;
        l2 = l2->next;
        while(l1 != nullptr || l2 != nullptr)
        {
            ListNode* tmp = new ListNode;
            if(l1 !=nullptr && l2 != nullptr)//都不为零
            {
                tmp->val = (l1->val + l2->val + flag) % 10 ;
                flag = (l1->val + l2->val + flag) / 10;
                l1 = l1->next;
                l2 = l2->next;
            }
            else if(l1 != nullptr)//l2为零
            {
                tmp->val = (l1->val + flag) % 10 ;
                flag = (l1->val + flag) / 10;
                l1 = l1->next;
            }
            else //l1为零
            {
                tmp->val = (l2->val + flag) % 10 ;
                flag = (l2->val + flag) / 10;
                l2 = l2->next;
            }
            tail->next = tmp;
            tail = tail->next;
        }
        if(flag == 1) //结尾判断是否有进位
        {
            ListNode* tmp = new ListNode(1);
            tail->next = tmp;
            tail = tail->next;
        }
        tail->next = nullptr;//最后指向nullprt
        return head;
    }
};

2. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?

解题思路:定义两个指针,一个快指针,一个慢指针,快指针先走n步。因为size = n + x;其中对于同一个元素,n为倒着数(从1开始),x为顺着数(从0开始)的位置。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(!head | !head -> next) return NULL;
        ListNode * fast = head, *slow = head;
        //快指针先走n步
        for(int i = 0; i < n; i++){
            fast = fast -> next;
        }
        if(!fast){
            return head -> next;    
        }
        //快慢指针一起
        while(fast -> next){
            fast = fast -> next;
            slow = slow -> next;
        }
        slow -> next = slow -> next -> next;
        return head;
    }
};

3. 合并两个有序链表

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

解题思路:新建链表,并分别用head和tail指针指向头部和尾部,然后依次遍历就可以了。(使用了一个空的head,也就是val为0的head)

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // 使用带头结点的链表解决问题
        // 待输出链表的头部
        ListNode * head = new ListNode();//新建的head不用
        
        // 待输出链表的 tail 结点
        ListNode * tail = head;
        while(l1 != nullptr && l2 != nullptr) {
            if(l1->val > l2->val) {
                tail->next = l2;
                l2 = l2->next;
            }else{
                tail->next = l1;
                l1 = l1->next;
            }
            tail = tail->next;
        }

        // l1 或 l2 可能还有剩余结点没有合并, 
        // 由于从上面的 while 循环中退出, 那么链表 l1 和 l2 至少有一个已经遍历结束
        if(l1 != nullptr) tail->next = l1;
        if(l2 != nullptr) tail->next = l2;
        
        return head->next;
    }
};

4. 合并K个升序链表

题目:给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

解题思路:和第3题是一样的,只不过多添加了一个循环而已。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
    {
        ListNode* head = new ListNode();//头节点
        ListNode* tail = head;
        while(l1 && l2){//两个序列都不为0
            if(l1->val > l2->val){
                tail->next = l2;
                tail = tail->next;
                l2 = l2->next;
            }else{
                tail->next = l1;
                tail = tail->next;
                l1 = l1->next;
            }
        }
        if(l1) tail ->next = l1;//l1不为空
        if(l2) tail->next = l2;//l2不为空
        return head->next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* head = nullptr;//头节点
        for(int i = 0; i < lists.size(); i++)
            head = mergeTwoLists(head, lists[i]);//合并两个
        return head;
    }
};

5. 两两交换链表中的节点

题目:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换

在这里插入图片描述

解题思路:主要考察交换节点,注意节点的前面的链接和后面的链接问题。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0);//开辟一个为0的节点
        dummyHead->next = head;
        ListNode* temp = dummyHead;//temp为记录前链接
        while (temp->next != nullptr && temp->next->next != nullptr) {
            ListNode* node1 = temp->next;//待交换的第一个节点
            ListNode* node2 = temp->next->next;//带交换的第二个节点
            temp->next = node2;//前链接
            node1->next = node2->next;//后连接
            node2->next = node1;//交换
            temp = node1;//往前移
        }
        return dummyHead->next;
    }
};

6. 旋转链表

题目:给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

在这里插入图片描述

解题思路:其实就是把末尾的k个链表挪到前面,先计算链表元素的个数,防止k过大,需要取余,然后设计环形链表,最后设置nullptr指针就可以了。(当然也可以用快慢指针,不过计算比这个稍微复杂)

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (k == 0 || head == nullptr || head->next == nullptr) {
            return head;
        }
        int n = 1;
        ListNode* iter = head;
        while (iter->next != nullptr) {//计算元素个数
            iter = iter->next;
            n++;
        }
        int add = n - k % n;
        if (add == n) {
            return head;
        }
        iter->next = head;//形成环形链表
        while (add--) {//找末尾节点
            iter = iter->next;
        }
        ListNode* ret = iter->next;//头结点
        iter->next = nullptr;//末尾节点断开
        return ret;
    }
};

7. 删除排序链表中的重复元素

题目:存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素只出现一次 。返回同样按升序排列的结果链表。
在这里插入图片描述
解题思路:把重复链表的第一个指向下一个就可以了,还是比较简单的一提。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == nullptr || head->next == nullptr) return head;
        ListNode* post = nullptr;
        ListNode* tmp = head;
        while(tmp->next != nullptr)
        {
            post = tmp->next;
            if(tmp->val == post->val)//如果相等
            {
                tmp->next = post->next;
            }
            else//如果不相等
            {
                tmp = tmp->next;
            }
        }
        return head; 
    }
};

8. 删除排序链表中的重复元素 II

题目:存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中没有重复出现的数字。返回同样按升序排列的结果链表。
在这里插入图片描述

解体思路:保留前向节点,然后依次比较key和tmp节点,key为不动的待比较节点,tmp为移动带比较节点。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == nullptr || head->next == nullptr) return head;
        ListNode * dummyHead = new ListNode();
        dummyHead->next = head;
        ListNode* pre = dummyHead;//保留前向节点
        ListNode* key = dummyHead->next;//待比较节点
        ListNode* tmp = key->next;//移动的带比较节点
        while(tmp != nullptr)
        {
            if(key->val == tmp->val)//相同元素
            {
                tmp = tmp->next;
            }
            else if(key->next == tmp)//不同元素,且相邻
            {
                pre->next = key;
                pre = key;
                key = key->next;
                tmp = tmp->next;
            }
            else//不同元素,且不相邻
            {
                key = tmp;
                tmp = key->next;
            }
        }
        if(key->next == tmp)// 相邻
        {
            pre->next = key;
        }
        else pre->next = nullptr;//最后几位元素相同
        return dummyHead->next;
    }
};

9. 分隔链表

题目:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有小于 x 的节点都出现在大于或等于 x 的节点之前。你应当保留 两个分区中每个节点的初始相对位置。

解题思路:建立两个链表,一个小于x的链表和一个大于等于x的链表。然后把两个链表连接起来即可。(可以各新建一个值为0的节点,避免第一次的判断。)

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(head == nullptr) return head;
        ListNode* tmp = head;
        ListNode* biggerHead = nullptr;
        ListNode* litterHead = nullptr;
        ListNode* tailBiger = biggerHead;
        ListNode* tailLitter = litterHead;
        while(tmp !=nullptr)
        {
            if(tmp->val < x)//litter
            {
                if(tailLitter == nullptr) //如果是第一次
                {
                    litterHead = tmp;
                    tailLitter = tmp;
                }
                else
                {
                    tailLitter->next = tmp;
                    tailLitter = tailLitter->next;
                }
            }
            else//bigger
            {
                if(tailBiger == nullptr)//如果是第一次
                {
                    biggerHead = tmp;
                    tailBiger = tmp;
                }
                else
                {
                    tailBiger->next = tmp;
                    tailBiger = tailBiger->next;
                }
            }
            tmp = tmp->next;
        }
        if(biggerHead == nullptr) {tailLitter->next = nullptr;return litterHead;}
        else if(litterHead == nullptr) {tailBiger->next = nullptr;return biggerHead;}
        else{//两个都不为nullptr
            tailLitter->next = biggerHead;
            tailBiger->next = nullptr;
            return litterHead;
        }
    }
};

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

解题思路:使用哈希表,key为旧链表,value为新链表。

/*
// Definition for a Node.
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*, Node*> m;
        m[nullptr] = nullptr;
        Node *srcNode = head;
        Node *desNode = nullptr;
		//构建哈希表
        while(srcNode != nullptr){
            desNode = new Node(srcNode->val);
            m[srcNode] = desNode;
            srcNode = srcNode->next;
        }
        //next和random赋值
        srcNode = head;
        while(srcNode != nullptr){
            m[srcNode]->next = m[srcNode->next];
            m[srcNode]->random = m[srcNode->random];
            srcNode = srcNode->next;
        }
        return m[head];
    }
};

以上是关于leetcode刷题链表-第1刷的主要内容,如果未能解决你的问题,请参考以下文章

漫步刷题路 - 合并两个有序链表

202004leetcode刷题记录

LeetCode 20天算法刷题计划第三天:双指针

leetcode分类刷题(续2)

leetcode分类刷题(续1)

LeetCode刷题(持续更新ing……)