31天刷完剑指offer——(第2天) 链表
剑指offer 06. 从尾到头打印列表 (easy)
#pragma once #include<vector> #include<stack> using std::vector; using std::stack; struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; /* (先反转链表(迭代)后打印) class Solution { public: vector<int> reversePrint(ListNode* head) { ListNode* p = head, * q = head; // p为原链表head,q为反转链表的headf (q起始为head,即原链表第一个头节点) while (p != nullptr) { // 遍历整个原链表 if (p == head) { // 如果是原链表第一个头节点,要特殊处理 p = p->next; // p先向后移动指向原链表下一个结点(即第二结点) q->next = nullptr; // q->next==nullptr(即将原链表的第一个头节点 作为此时反转链表的头节点,也是尾结点,next为nullptr) continue; } ListNode* t = p->next; // 保存原链表中p->next(即p指向结点的下一个结点地址) p->next = q; // 关键: 这一步将原链表p指向结点 指向反转链表头节点q,从而p指向结点变为 反转链表的头结点 (每次循环都将原链表中的头节点p 转移到反转链表中) q = p; // 此时反转链表头节点q为 原链表头节点p p = t; // 转移后的 原链表头节点为 t指向的结点 } vector<int> vec; while (q != nullptr) { // 遍历反转后的链表 并逐个将值val插入到vec中 vec.push_back(q->val); q = q->next; } return vec; } }; */ // 使用栈 class Solution { public: vector<int> reversePrint(ListNode* head) { stack<int> stk; while (head != nullptr) { // 顺序遍历链表,将每个结点值 push到栈stk中 stk.push(head->val); head = head->next; } vector<int> vec; while (!stk.empty()) { vec.push_back(stk.top()); // 利用栈的特性,将栈中的元素出栈 并存储在vec中 stk.pop(); } return vec; } };
剑指offer 24. 反转链表 (easy)
#pragma once 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* p = nullptr; while (head != nullptr) { // 遍历整个原链表 ListNode* t = head->next; // 保存原链表中head->next(即head指向结点的下一个结点地址) head->next = p; // 关键: 这一步将原链表head指向结点 指向反转链表头节点p,从而head指向结点变为 反转链表的头结点 (每次循环都将原链表中的头节点head 转移到反转链表中) p = head; // 此时反转链表头节点p为 原链表头节点head head = t; // 转移后的 原链表头节点为 t指向的结点 } return p; } }; */ // (递归) class Solution { private: ListNode* rhead; // 用来记录反转链表后的头节点,也就是原链表的尾结点 public: ListNode* dg(ListNode* head); ListNode* reverseList(ListNode* head) { dg(head); return rhead; } }; ListNode* Solution::dg(ListNode* head) { if (head == nullptr || head->next == nullptr) { // 递归到最后一个结点返回 (注意链表为空的情况) rhead = head; // 记录原链表的尾结点(即反转链表的头节点) return head; } ListNode* head_next = dg(head->next); // 函数dg返回的结点即head->next,即当前结点的下一个结点 head_next->next = head; // 当前结点的下一个结点指向自己 head->next = nullptr; // 自己的next设为nullptr (head此时是 当前反转链表的尾结点) return head; // 传进来是head,返回的也是head(该法的核心在于,必须从原链表尾部往前反转,而不能从原链表头部往后反转) }
剑指offer 35. 复杂链表复制 (medium)
#pragma once #include<unordered_map> #include<iostream> using std::unordered_map; class Node { public: int val; Node* next; Node* random; Node(int _val) { val = _val; next = NULL; random = NULL; } }; /* 直接法 (先构造复制链表,再查找并设置random指针) class Solution { public: Node* copyRandomList(Node* head) { Node* dummy = new Node(0); // 复制的链表的dummy结点 (即head头节点的前一个结点) Node* cur = head; // cur指向当前结点 Node* pre = dummy; // pre指向"当前复制链表"的最后一个结点,也就是下一个新结点p的前一个结点 while (cur != nullptr) { // 遍历整个原链表,构造复制的链表结点和next指针 (random指针无法在 构造链表的过程中 设置,因为random指向的结点 可能还未构造) Node* p = new Node(cur->val); // new一个新结点p,其值val与原链表中对应的结点相同 p->next = nullptr; // p的next设为nullptr (结点p将要是"当前复制链表"的尾结点) pre->next = p; // 将结点p接在"当前复制链表"尾部 pre = p; // pre赋值为p,即向后移动 (因此pre又指向"当前复制链表"的最后一个结点) cur = cur->next; } for (Node* p = head, *q = dummy->next; p != nullptr; p = p->next, q = q->next) { // p和q分别指向原链表和复制链表,同时遍历两条链表,设置复制链表的结点的random指针 Node* random = p->random; // random为原链表中 p指向结点的random指针 int distence = 0; for (Node* i = head; i != random; i = i->next) { // 计算random指针指向的结点 离头节点的距离distence ++distence; } Node* j = dummy->next; while (distence != 0) { // 根据距离distence在复制链表中 找到相应的结点的指针j j = j->next; --distence; } q->random = j; // 因此q指向结点 的random指针为j } return dummy->next; } }; */ /* 利用map存储 原链表结点指针和复制链表结点指针 的键值对 (相比上面方法更巧妙,无需计算距离和根据距离查找random,时间复杂度从N*N下降到N) class Solution { public: Node* copyRandomList(Node* head) { unordered_map<Node*, Node*> hash; for (Node* cur = head; cur != nullptr; cur = cur->next) { // 遍历原链表 Node* node_p = new Node(cur->val); // 先创建结点,并建立map中 原链表结点指针和复制链表结点指针 的映射关系 hash[cur] = node_p; } for (Node* cur = head; cur != nullptr; cur = cur->next) { // 遍历原链表 hash[cur]->next = hash[cur->next]; // 设置之前创建的 复制结点的next和random指针 (因为是复制链表,所以复制结点的next,random指针 指向结点的位置 与原链表相应结点的next,random指针指向结点位置 是相对应的) hash[cur]->random = hash[cur->random]; // 根据原结点和复制结点的 这种映射关系,可以用来设置 复制结点的next和random指针 } return hash[head]; } }; */ // 拼接+拆分(将复制结点插入 原链表中对应结点的后面,根据位置对应关系 并结合拼接链表,就能够通过一次遍历链表来设置 复制链表的结点的random,之后再拆分从而得到复制链表) class Solution { public: Node* copyRandomList(Node* head) { if (head == nullptr) { return nullptr; } for (Node* cur = head; cur != nullptr; cur = cur->next->next) { // 拼接(将复制结点插入原链表的 cur指向的结点后面) Node* node_p = new Node(cur->val); node_p->next = cur->next; cur->next = node_p; } for (Node* cur = head; cur != nullptr; cur = cur->next->next) { // 设置复制链表的结点的random指针(根据位置对应关系 并结合拼接链表知: 复制结点的random即 "cur指向结点的random指针指向结点 之后的结点的指针") if (cur->random != nullptr) { cur->next->random = cur->random->next; } } Node* pre = head, * cur = head->next, * copy_head = head->next; // 拆分 (将原链表和复制链表拆分) while (pre->next->next != nullptr) { pre->next = pre->next->next; cur->next = cur->next->next; pre = pre->next; cur = cur->next; } pre->next = nullptr; // 注意!虽然我们返回的是复制链表,但是拆分后的 原链表的尾结点next指针必须设置为nullptr,否则会出错! return copy_head; } };官方题解:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/fu-za-lian-biao-de-fu-zhi-by-leetcode-so-9ik5/
31天刷完剑指offer 文章被收录于专栏
数据结构与算法