LeetCode 链表(1)

题目

1. 常规题

2 两数相加

给两个链表分别代表两个正数的逆序表示,计算两个链表之和。

依次按位进行相加。

class Solution {
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        int acc = 0, val = 0;
        auto head = l1, tail = l1;
        while (l1 && l2) {
            val = l1->val + l2->val + acc;
            acc = val / 10;
            l1->val = val % 10;
            if (!l1->next)
                l1->next = l2->next, l2->next = nullptr;
            tail = l1;
            l1 = l1->next, l2 = l2->next;
        }
        while (l1) {
            val = l1->val + acc;
            acc = val / 10;
            l1->val = val % 10;
            tail = l1;
            l1 = l1->next;
        }
        if (acc)
            tail->next = new ListNode(1);
        return head;
    }
};

21 合并两个有序链表

合并两个有序链表。

逐个比较大小并添加到当前节点后面,并移动对应的链表节点。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode *head = new ListNode(0), *node = head;
        while (l1 && l2) {
            if (l1->val < l2->val)
                head->next = l1, l1 = l1->next;
            else
                head->next = l2, l2 = l2->next;
            head = head->next;
        }
        while (l1)
            head->next = l1, l1 = l1->next, head = head->next;
        while (l2)
            head->next = l2, l2 = l2->next, head = head->next;
        return node->next;
    }
};

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

删除链表中所有重复的节点。

将每个节点与其后面的节点的值做对比,如果相同则删除后面的节点。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        auto ret = head;
        while (head) {
            while (head->next && head->next->val == head->val) {
                auto next = head->next;
                head->next = next->next;
                delete next;
            }
            head = head->next;
        }
        return ret;
    }
};

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

删除链表中所有重复的节点,只保留原始链表中没有重复出现的数字。

为了删除所有重复的节点并只保留所有没有出现过的数字,需要提前两个节点检查接下来的两个节点的值是否相同,如果相同的话需要将这两个节点及其后的所有重复节点都删除。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode *node = new ListNode(0), *ret = node, *next = nullptr, *temp = nullptr;
        node->next = head;
        while (node && node->next) {
            next = node->next->next;
            while (next && node->next->val == next->val) {
                temp = next;
                next = next->next;
                delete temp;
            }
            if (next != node->next->next) {
                temp = node->next;
                node->next = next;
                delete temp;
            } else
                node = node->next;
        }
        return ret->next;
    }
};

203 移除链表元素

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

先判断头节点是否等于给定值,再判断其后面的节点是否等于给定值。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        while (head && head->val == val) {
            auto prev = head;
            head = head->next;
            delete prev;
        }
        auto ret = head;
        while (head) {
            while (head->next && head->next->val == val) {
                auto next = head->next;
                head->next = next->next;
                delete next;
            }
            head = head->next;
        }
        return ret;
    }
};

817 链表组件

给一个链表和一个数组,找到链表中一段子链表的值都在数组中的子链表的个数。

先将数组转换成哈希表方便查询,再依次遍历整个链表,判断一段子链表结束时或遍历结束时子链表是否是符合条件。

class Solution {
public:
    int numComponents(ListNode* head, vector<int>& G) {
        unordered_set<int> exist(G.begin(), G.end());
        bool cont = false, curr = false;
        int res = 0;
        while (head) {
            curr = exist.count(head->val);
            res += cont && !curr;
            cont = curr;
            head = head->next;
        }
        return res + cont;
    }
};

24 两两交换链表中的节点

给一个链表,返回两两交换其中相邻的节点后的结果。

用三个指针把要交换的两个节点和他们的前驱节点保存下来,交换后再更新三个指针,按顺序交换即可。

class Solution {
public:
    ListNode *swapPairs(ListNode *head) {
        if (!head || !head->next)
            return head;
        ListNode *root = new ListNode(0), *prev = root;
        root->next = head;
        auto first = head, second = head->next;
        while (first && second) {
            auto temp = second->next;
            prev->next = second;
            second->next = first;
            first->next = temp;
            prev = first;
            first = temp;
            second = temp ? temp->next : nullptr;
        }
        return root->next;
    }
};

430 扁平化多级双向链表

给一个带子节点的双向链表,将其扁平化并使所有结点出现在单级双链表中。

对于某一个节点,如果它有 child 节点,那么需要将其 child 节点作为其新的 next 节点,将其 child 链表上的最后一个节点作为其原本 next 节点的 prev 节点,递归调用整个过程即可。

class Solution {
public:
    Node *flatten(Node *head) {
        if (!head)
            return nullptr;
        Node *node = head, *next = nullptr;
        while (node) {
            if (node->child) {
                next = node->next;
                auto child = flatten(node->child);
                node->next = child;
                child->prev = node;
                node->child = nullptr;
                while (node->next)
                    node = node->next;
                node->next = next;
                if (next)
                    next->prev = node;
            }
            node = node->next;
        }
        return head;
    }
};

2. 链表反转

206 反转链表

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head)
            return nullptr;
        ListNode *prev = nullptr, *curr = head, *next = head->next;
        while (curr) {
            next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

92 反转链表 II

将链表中从 m 到 n 位置的节点反转。

先找到位置在 m - 1 的节点,将其后的节点截断,再找到位置在 n 的节点,将其后的节点截断,将中间的一段链表反转后再链接到愿链表上。

class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        ListNode *root = new ListNode(0), *r = root, *prev, *last;
        root->next = head;
        int cnt = 1;
        while (cnt < m && r)
            r = r->next, ++cnt;
        prev = r;
        while (cnt <= n && r && r->next)
            r = r->next, ++cnt;
        last = r->next;
        r->next = nullptr;
        prev->next = Reverse(prev->next);
        while (prev && prev->next)
            prev = prev->next;
        prev->next = last;
        return root->next;
    }

    ListNode *Reverse(ListNode *head) {
        ListNode *prev = nullptr, *curr = head, *next = nullptr;
        while (curr) {
            next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

369 给单链表加一

用一个单链表表示一个整数,计算将其加一的结果。

先将链表反转方便进位,然后进行加一和进位的操作,最后再反转一次链表。

class Solution {
public:
    ListNode* plusOne(ListNode* head) {
        if (!head)
            return nullptr;
        head = Reverse(head);
        auto root = head;
        while (head) {
            if (head->val == 9) {
                head->val = 0;
                if (!head->next) {
                    head->next = new ListNode(1);
                    break;
                }
                head = head->next;
            }
            else {
                ++head->val;
                break;
            }
        }
        return Reverse(root);
    }
    
    ListNode *Reverse(ListNode* head) {
        if (!head)
            return nullptr;
        ListNode *prev = nullptr, *curr = head, *next = head->next;
        while (curr) {
            next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

445 两数相加 II

给两个链表分别代表两个正数,计算两个链表之和。

先将两个链表反转,再依次按位进行相加,最后再将得到的结果反转并返回。

class Solution {
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        if (!l1 || !l2)
            return !l1 ? l2 : l1;
        l1 = Reverse(l1);
        l2 = Reverse(l2);
        int carry = 0, sum = 0;
        ListNode *head = l1, *prev = l1;
        while (l1 && l2) {
            prev = l1;
            sum = l1->val + l2->val + carry;
            carry = sum / 10;
            l1->val = sum - carry * 10;
            if (l2->next && !l1->next)
                l1->next = l2->next, l2->next = nullptr;
            l1 = l1->next;
            l2 = l2->next;
        }
        while (l1) {
            prev = l1;
            sum = l1->val + carry;
            carry = sum / 10;
            l1->val = sum - carry * 10;
            l1 = l1->next;
        }
        if (carry && prev)
            prev->next = new ListNode(1);
        return Reverse(head);
    }

    ListNode *Reverse(ListNode *head) {
        ListNode *prev = nullptr, *curr = head, *next = nullptr;
        while (curr) {
            next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

25 K 个一组翻转链表

给一个链表,每 k 个节点一组进行翻转。

用几个指针记录下需要反转的部分的起始,终止位置,依次反转即可。

class Solution {
    ListNode *ReverseLinkedList(ListNode *head) {
        ListNode *prev = nullptr, *curr = head, *next = nullptr;
        while (curr) {
            next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }

public:
    ListNode *reverseKGroup(ListNode *head, int k) {
        ListNode *root = new ListNode(0), *prev = root;
        prev->next = head;
        while (prev) {
            int i = 1;
            ListNode *start = prev->next, *end = prev->next, *curr = end, *next = nullptr;
            while (curr && i < k)
                curr = curr->next, ++i;
            if (i < k || !curr)
                break;
            next = curr->next;
            curr->next = nullptr;
            start = ReverseLinkedList(start);
            prev->next = start;
            end->next = next;
            prev = end;
        }
        return root->next;
    }
};

3. 双链表

328 奇偶链表

把一个链表中的奇数位节点和偶数位节点分别排在一起。

用两个头节点分别表示奇数位和偶数位节点的起始位置,遍历整个链表,将奇数位节点链接在奇数位起始节点后,将偶数位节点链接在偶数位起始节点后,最后将偶数位起始节点链接在奇数位最后节点后即可。

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if (!head || !head->next)
            return head;
        auto odd = head, even = head->next, node = even->next, even_head = even;
        bool flag = true;
        while (node) {
            if (flag) {
                odd->next = node;
                odd = odd->next;
                flag = false;
            } else {
                even->next = node;
                even = even->next;
                flag = true;
            }
            node = node->next;
        }
        odd->next = even_head;
        even->next = nullptr;
        return head;
    }
};

86 分隔链表

给一个链表和一个值 x,重新排列链表使得所有小于 x 的节点都在大于等于 x 的节点之前。

用两个头节点 sth 和 geq 分别表示小于 x 和大于等于 x 的节点的起始位置,遍历整个链表,分别将各个节点链接到两个头节点之后,最后将 geq 链接到 sth 之后,再将 geq 的末端设为空指针。

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        auto sth = new ListNode(0), sth_head = sth, geq = new ListNode(0), geq_head = geq;
        while (head) {
            if (head->val < x) {
                sth->next = head;
                sth = sth->next;
            } else {
                geq->next = head;
                geq = geq->next;
            }
            head = head->next;
        }
        sth->next = geq_head->next;
        geq->next = nullptr;
        return sth_head->next;
    }
};

725 分隔链表

给一个链表, 将其分隔为 k 个连续的部分。

先计算出链表的长度和 k 个连续部分中每个部分的长度,依次将每个部分的头节点放入数组中。

class Solution {
public:
    vector<ListNode *> splitListToParts(ListNode *root, int k) {
        vector<ListNode *> res(k, nullptr);
        int len = 0;
        ListNode *head = root, *temp = nullptr;
        while (root)
            root = root->next, ++len;
        int n = len / k, m = len % k, i = 0;
        while (head) {
            res[i] = head;
            for (int j = 0; j < n + (m > 0) - 1; ++j)
                head = head->next;
            temp = head;
            head = head->next;
            temp->next = nullptr;
            ++i;
            --m;
        }
        return res;
    }
};