leetcode之链表总结

leetcode之链表总结

1. 反转链表(简单)

https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

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

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* curr = head;
        while(curr!=NULL){
            ListNode* temp = curr->next;
            curr->next = pre;
            pre = curr;
            curr = temp;
        }
        return pre;
    }
};

做完这个可以做反转链表2

2. 回文链表(简单)

请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false

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

第一种方法
https://leetcode-cn.com/problems/palindrome-linked-list/
比较骚的操作:
第一步:复制链表到数组中
第二步:使用双指针法判断是否为回文

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode* dummy = head;
        vector<int> res;
        while(dummy!=NULL){
            res.push_back(dummy->val);
            dummy = dummy->next;
        }
        int i=0;
        int j=res.size()-1;
        while(i<=j){
            if(res[i]==res[j]){
                i++;
                j--;
            }
            else{
                return false;
            }
        }
        return true;
    }
};

时间复杂度o(n),空间复杂度o(n)

第二种做法:快慢指针
整个流程分为五个部分:
1.找到前半部分链表的尾节点
2.反转后半部分链表
3.判断是否回文
4.恢复链表(一般情况下希望链表执行完之后不对原始链表进行改变,不要这一步,对结果也不会产生影响)
5.返回结果

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head==NULL) return true;
        ListNode *firstEnd = findMid(head);
        ListNode *secondList = reverseList(firstEnd->next);
        ListNode *first = head, *second = secondList;
        bool result = true;
        while(result && second!=NULL){
            if(first->val != second->val){
                result = false;
            }
            first = first->next;
            second = second->next;
        }
        firstEnd->next = reverseList(secondList);
        return result;
    }
    ListNode *reverseList(ListNode* head){
        ListNode *pre = nullptr,*curr=head;
        while(curr!=NULL){
            ListNode *temp = curr->next;
            curr->next = pre;
            pre = curr;
            curr = temp;
        }
        return pre;
    }
    ListNode *findMid(ListNode* head){
        ListNode *slow = head,*fast = head;
        while(fast->next!=NULL&&fast->next->next!=NULL){
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
};

时间复杂度 o(n),空间复杂度 o(1)

注:这两种做法都有牢记,在不对空间复杂度做要求时,推荐第一种做法,毕竟简单。但第二种做法涉及到链表一些常见的解题技巧,快慢指针,反转链表等,也要记住。

3.移除重复节点(简单)

https://leetcode-cn.com/problems/remove-duplicate-node-lcci/
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

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

示例2:
输入:[1, 1, 1, 1, 2]
输出:[1, 2]

1.哈希表 hash_set
与去重有关的问题首选哈希表(集合)

class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
        if(head==NULL||head->next==NULL) return head;
        unordered_set<int> occurred = {head->val};
        ListNode *pos = head;
        while(pos->next!=NULL){
            ListNode *cur = pos->next;
            if(!occurred.count(cur->val)){
                occurred.insert(cur->val);
                pos = pos->next;
            }else{
                pos->next = pos->next->next;
            }
        }
        return head;
    }
};

时间复杂度 o(n) 空间复杂度 o(n)

  1. 双重循环
    对每一个节点保证后面的节点与他没有重复的。
class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
        if(head==NULL||head->next==NULL) return head;
        ListNode *pos = head;
        while(pos!=NULL){
            ListNode *cur = pos;
            while(cur->next!=NULL){
                if(cur->next->val==pos->val){
                    cur->next = cur->next->next;
                }else{
                    cur = cur->next;
                }
            }
            pos = pos->next;
        }
        return head;
    }
};

时间复杂度 o(n^2) 空间复杂度 o(1)

4.环形链表(简单)

https://leetcode-cn.com/problems/linked-list-cycle/
快慢指针的典型应用

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。
在这里插入图片描述
在这里插入图片描述

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

  1. 如果存在环,那么快慢指针一定会在某一刻相遇
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==NULL||head->next==NULL) return false;
        ListNode *slow = head->next,*fast=head->next->next;
        while(slow!=fast){
            if(fast==NULL||fast->next==NULL) return false;
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

时间复杂度 o(n) 空间复杂度 o(1)

2.哈希表,如果存在环,节点就会被重复添加。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        unordered_set<ListNode*> seen;
        while(head!=NULL){
            if(seen.count(head)){
                return true;
            }else{
                seen.insert(head);
                head = head->next;
            }
        }
        return false;
    }
};

时间复杂度 o(n), 空间复杂度 o(n)

5.环形链表2(中等)

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

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

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

进阶:

你是否可以使用 O(1) 空间解决此题?
在这里插入图片描述
在这里插入图片描述
1.延续上一题哈希表的思想

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode*> seen;
        while(head!=NULL){
            if(seen.count(head)){
                return head;
            }else{
                seen.insert(head);
                head = head->next;
            }
        }
        return NULL;
    }
};

时间复杂度 o(n) 空间复杂度 o(n)

2.快慢指针

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

涉及到数学推理,不建议
时间复杂度 o(n),空间复杂度 o(1)

6.重排链表(中等)

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

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

示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.

示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

1.直接双重循环进行插入

class Solution {
public:
    void reorderList(ListNode* head) {
        if(head==NULL||head->next==NULL) return;
        ListNode *pre = head;
        while(pre!=NULL&&pre->next!=NULL&&pre->next->next!=NULL){
            ListNode *cur = pre;
            while(cur->next->next!=NULL){
                cur = cur->next;
            }
            cur->next->next = pre->next;
            pre->next = cur->next;
            cur->next=NULL;
            //if(pre->next==NULL||pre->next->next==NULL) return;
            pre = pre->next->next;
        }
    }
};

时间复杂度 o(n^2) 空间复杂度 o(1)

2.线性表
因为链表不支持下标访问,所以我们无法随机访问链表中任意位置的元素。

因此比较容易想到的一个方法是,我们利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。

对于链表中涉及到元素交换的都可以考虑使用线性表(数组)进行处理

class Solution {
public:
    void reorderList(ListNode* head) {
        if(head==NULL||head->next==NULL) return;
        vector<ListNode*> res;
        ListNode *cur = head;
        while(cur!=NULL){
            res.push_back(cur);
            cur = cur->next;
        }
        int i=0,j = res.size()-1;
        while(i<j){
            res[i]->next = res[j];
            i++;
            if(i==j) break;
            res[j]->next = res[i];
            j--;
        }
        res[i]->next = NULL;
    }
};

注意:循环跳出的条件,边界值
时间复杂度 o(n) ,空间复杂度 o(n)

3. 寻找链表中点 + 链表逆序 + 合并链表
链表常见解题方法的综合应用

class Solution {
public:
    void reorderList(ListNode* head) {
        if(head==NULL||head->next==NULL) return;
        ListNode *firstEnd = findMid(head);
        ListNode *secondList = reverseList(firstEnd->next);
        firstEnd->next = NULL;
        ListNode *first=head,*second = secondList;
        while(second!=NULL){
            ListNode *temp = second->next;
            second->next = first->next;
            first->next = second;
            first = first->next->next;
            second = temp;

        }
        //first->next = NULL;
    }
    ListNode *reverseList(ListNode* head){
        ListNode *pre=NULL,*cur = head;
        while(cur!=NULL){
            ListNode *temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
    ListNode *findMid(ListNode* head){
        ListNode *slow = head,*fast = head;
        while(fast->next!=NULL && fast->next->next!=NULL){
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
};

时间复杂度 o(n) 空间复杂度 o(1)

7.有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
在这里插入图片描述
分治

class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        return buildTree(head,nullptr);
    }
    ListNode *findMid(ListNode* left,ListNode *right){
        ListNode *slow = left,*fast = left;
        while(fast!=right && fast->next!=right){
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
    TreeNode *buildTree(ListNode *left,ListNode *right){
        if(left==right) return nullptr;
        ListNode *mid = findMid(left,right);
        TreeNode* root = new TreeNode(mid->val);
        root->left = buildTree(left,mid);
        root->right = buildTree(mid->next,right);
        return root;
    }
};

时间复杂度 o(nlogn) 空间复杂度 o(logn)

总结

反转链表 + 快慢指针+找中间节点
考虑使用线性表(vector)/数组
与去重(不重复)有关的用哈希表 unordered_set m; unordered_set<ListNode*> m;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值