链表相关题型(1)

链表

1.旋转链表

1.1题目描述

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

1.2题目分析

1、首先遍历整个链表,求出链表的长度n,并找出链表的尾节点tail。

2、由于k可能很大,所以我们令 k = k % n,然后再次从头节点head开始遍历,找到第n - k个节点p,那么1 ~ p是链表的前 n - k个节点,p+1 ~ n是链表的后k个节点。

3、接下来就是依次执行 tail->next = head,head = p->next,p->next = nullptr,将链表的后k个节点和前 n - k个节点拼接到一块,并让head指向新的头节点(p->next),新的尾节点即p节点的next指针指向null。

4、最后返回链表的新的头节点head。

时间复杂度分析: 链表一共被遍历两次,因此总的时间复杂度为O(n),n是链表的长度。

/**
 * 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* rotateRight(ListNode* head, int k) {
        if(head == nullptr || k == 0) return head;
        ListNode* p = head; //遍历指针
        ListNode* tail; //记录末尾结点
       
        int n = 0;
        while(p) {
            ++n;
            tail = p;
            p = p->next;
        }

        k = k % n;
        p = head;
        // while(k >= 0) {
        //     --k;
        //     mid = p;
        //     p = p->next;
        // }
        for(int i = 0; i < n - k - 1; ++i) {
            p = p->next;
        }
        
        //head....p; p->next...tail //head应该变为p->next
        tail->next = head;
        head = p->next;
        p->next = nullptr;
        return head;
    }
};

2.反转链表

2.1题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

2.2题目分析

/**
 * 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* reverseList(ListNode* head) {
        ListNode* prev = nullptr, *next;
        while(head){
            /*让prev保留反向的链表,然后将原本链表上的结点一个一个摘下后放入反向链表里*/
            next = head->next;
            head->next = prev;
            prev = head;
            head = next;
        }

        return prev;
    }
};

3.反转链表 II

3.1题目描述

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:

输入:head = [1,2,3,4,5], left = 2, right = 4

输出:[1,4,3,2,5]

3.2题目分析

  • 参考上一道题方法的方法

/**
 * 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* reverseBetween(ListNode* head, int left, int right) {
        // if(right == 1) {
        //     return head;
        // }
        int cnt = 1;
        ListNode* p = head;
        ListNode* leftprev = nullptr;

        //找到要翻转的最左端结点
        while(cnt < left) {
            leftprev = p;
            p = p->next;
            ++cnt;
        }

        ListNode* prev = nullptr;
        ListNode* next;
        ListNode* tail = p;

        //开始翻转并且进行边界判断
        while(cnt < right) {
            next = p->next;
            p->next = prev;
            prev = p;
            p = next;
            cnt++;
        }

        //组装 -- 分类讨论,判断是否从第一个元素开始翻转,left = 1
         next = p->next;
            p->next = prev;
        if(left == 1) {
            head = p;
            tail->next = next;
        }else{
            leftprev->next = p;
            tail->next = next;
        }
        return head;
    }
};
  • 虚拟指针dummyNode(优化方法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* reverseBetween(ListNode* head, int left, int right) {
        ListNode* dummy = new ListNode(-1); //虚拟结点
        dummy->next = head;
        ListNode* pre = dummy;
        for(int i = 0; i < left - 1; ++i) {
            pre = pre->next;
        }

        ListNode* cur = pre->next, *next;
        //反转链表 -- 交换的思想 
        for(int i = 0; i < right - left; ++i) {
            next = cur->next;
            cur->next = next->next;
            next->next = pre->next;
            pre->next = next;
        }
        return dummy->next;
    }
};

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

4.1题目描述

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

4.2题目分析

  • 递归算法

/**
 * 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* swapPairs(ListNode* head) {
        if(head == nullptr || head->next == nullptr) {
            return head;
        }

        ListNode* ans = head->next;
        head->next = swapPairs(ans->next);
        ans->next = head;
        return ans;
    }
};

/*
时间复杂度O(N),空间复杂度为O(N)
*/
  • 迭代算法

/**
 * 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* swapPairs(ListNode* head) {
       ListNode* dummy = new ListNode(0);
       dummy->next = head;
       ListNode* q = dummy;
       while(q->next && q->next->next) {
           ListNode* p1 = q->next;
           ListNode* p2 = q->next->next;
           q->next = p2;
           p1->next = p2->next;
           p2->next = p1;
           q = p1;
       }
       return dummy->next;
    }
};

/*
时间复杂度O(N),空间复杂度为O(1)
*/

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

5.1题目描述

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表

示例 1:

输入:head = [1,1,2]
输出:[1,2]

示例 2:

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

5.2题目分析

/**
 * 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* deleteDuplicates(ListNode* head) {
        if(head == nullptr) return nullptr;
        ListNode* nextNode = head->next, *prev = head;
        //ListNode* node;
        while(nextNode){
            while(nextNode && prev->val == nextNode->val){
                //node = nextNode;
                nextNode = nextNode->next;
                //delete[] node;
            }
            prev->next = nextNode;
            prev = nextNode;
        }
        return head;
    }
};

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

6.1题目描述

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例 1:

输入:head = [1,2,3,3,4,4,5]

输出:[1,2,5]

示例 2:

输入:head = [1,1,1,2,3]

输出:[2,3]

6.2题目分析

/**
 * 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* deleteDuplicates(ListNode* head) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode* prev = dummy, *cur = dummy->next;
        ListNode* p;
        while(cur && cur->next) {
            if(cur->val == cur->next->val) {
                /*删除重复结点*/
                int val = cur->val;
                while(cur && cur->val == val) {
                    p = cur;
                    cur = cur->next;
                    delete p;
                }
            }else{
                /*连接不重复的结点*/
                prev->next = cur;
                cur = cur->next;
                prev = prev->next;
            }
        }
        if(cur) {
            /*连接最后一个unique结点*/
            prev->next = cur;
        }else{
            /*防止出现类似[1,1,1]这样,使得prev->next指向一块被删除的内存(野指针)*/
            prev->next = nullptr;
        }
        return dummy->next;
    }
};

7. 链表的中间结点

7.1题目描述

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]

输出:此列表中的结点 3 (序列化形式:[3,4,5])

返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。

注意,我们返回了一个 ListNode 类型的对象 ans,这样:

ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

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

输出:此列表中的结点 4 (序列化形式:[4,5,6])

由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

7.2题目分析

  • 快慢指针

/**
 * 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* middleNode(ListNode* head) {
         /*寻找链表的中间结点 -- 快慢指针*/
        ListNode* fast = head, *slow = head;
        while(fast->next && fast->next->next) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return fast->next == nullptr ? slow : slow->next;
    }
};

8.重排链表

8.1题目描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

输入:head = [1,2,3,4]

输出:[1,4,2,3]

示例 2:

输入:head = [1,2,3,4,5]

输出:[1,5,2,4,3]

8.2题目分析

  • 线性表.先将所有的结点放入vector中(可以随机访问),然后再重排。

/**
 * 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:
    void reorderList(ListNode* head) {
        if(head == nullptr) {
            return;
        }
        vector<ListNode*> coll;
        ListNode* p = head;

        while(p) {
            coll.push_back(p);
            p = p->next;
        }

        int i = 0, j = coll.size() - 1;
        while(i < j) {
            coll[i]->next = coll[j];
            ++i;
            if(i == j) {
                break;
            }
            coll[j]->next = coll[i];
            --j;
        }
        coll[i]->next = nullptr;

        // int n = coll.size();
        // int mid = (n - 1) / 2;
        // p = head;
        // for(int i = 0; i < mid; ++i) {
        //     p->next = coll[i];
        //     p = p->next;
        //     p->next = coll[n - i - 1];
        //     p = p->next;
        // }
        // p = nullptr;
    }
};
  • 寻找链表中点+反转链表+合并链表

/**
 * 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:
    void reorderList(ListNode* head) {
        if(head == nullptr) {
            return;
        }
        ListNode* mid = middleNode(head);
        ListNode* l1 = head, *l2 = mid->next;
        mid->next = nullptr; //让l1与l2分开,分成两条链
        l2 = reverseList(l2);
        mergeList(l1, l2);
    }

    ListNode* middleNode(ListNode* head) {
        /*寻找链表的中间结点 -- 快慢指针*/
        ListNode* fast = head, *slow = head;
        while(fast->next && fast->next->next) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }

    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr, *cur = head;
        while(cur) {
            ListNode* nextPtr = cur->next;
            cur->next = prev;
            prev = cur;
            cur = nextPtr;
        }
        return prev;
    }

    void mergeList(ListNode* l1, ListNode* l2) {
        ListNode* p1 = l1, *p2 = l2;
        while(l1 && l2) {
            p1 = p1->next;
            p2 = p2->next;

            l1->next = l2;
            l1 = p1;

            l2->next = l1;
            l2 = p2;
        }
    }

};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子味的晚霞和少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值