【数据结构C++】链表(二)

2. 链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 相交链表: https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

    给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。
    如果两个链表不存在相交节点,返回 null 。
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    方法一:哈希集合
    思路和算法
    
    判断两个链表是否相交,可以使用哈希集合存储链表节点。
    首先遍历链表headA,并将链表headA 中的每个节点加入哈希集合中。
    然后遍历链表headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:
    如果当前节点不在哈希集合中,则继续遍历下一个节点;
    如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,
    因此在链表 headB 中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。
    如果链表headB 中的所有节点都不在哈希集合中,则两个链表不相交,返回null。
    
    class Solution {
    public:
        ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
            unordered_set<ListNode *> visited;  //创建一个哈希表
            ListNode *temp = headA;   
            cout<<  temp << endl ;  //输出的是headA的地址 
            while (temp != nullptr) {
                visited.insert(temp); //将链表A插入到哈希表中
                temp = temp->next;   //指向下一节点
            }
            temp = headB;
            while (temp != nullptr) {
                if (visited.count(temp)) {   //count计数
                    return temp;
                }
                temp = temp->next;
            }
            return nullptr;
        }
    };
    
  • 反转链表: https://leetcode-cn.com/problems/reverse-linked-list/

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

    在这里插入图片描述

    方法一:迭代
    假设链表为 123→∅,我们想要把它改成 3∅←123。
    在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。
    
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            ListNode* prev = nullptr;
            ListNode* curr = head;
            cout<<curr<<endl;  //输出地址
            while (curr) {
                ListNode* next = curr->next;
                curr->next = prev;
                prev = curr;
                curr = next;
            }
            return prev;
        }
    };
    
    复杂度分析
    时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
    空间复杂度:O(1)
  • 合并两个有序链表: https://leetcode-cn.com/problems/merge-two-sorted-lists/

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

    在这里插入图片描述
    在这里插入图片描述

    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
            if (list1 == nullptr)
            {
                return list2;
            } else if (list2 == nullptr) {
                return list1;
            } else if (list1->val < list2->val) {
                list1->next = mergeTwoLists(list1->next, list2);
                return list1;
            } else {
                list2->next = mergeTwoLists(list1, list2->next);
                return list2;
            }
        }
    };
    
    复杂度分析
    时间复杂度:O(n+m),其中 n 和 m 分别为两个链表的长度。因为每次调用递归都会去掉 l1 或者 l2 的头节点(直到至少有一个链表为空),函数 mergeTwoList 至多只会递归调用每个节点一次。因此,时间复杂度取决于合并后的链表长度,即O(n+m)。
    空间复杂度:O(n+m),其中 n 和 m 分别为两个链表的长度。递归调用 mergeTwoLists 函数时需要消耗栈空间,栈空间的大小取决于递归调用的深度。结束递归调用时 mergeTwoLists 函数最多调用 n+m 次,因此空间复杂度为 O(n+m)

    递归: https://www.yiibai.com/cplusplus/cpp-recursion.html

  • 删除排序链表中的重复元素: https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

    存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。
    返回同样按升序排列的结果链表。
    

    在这里插入图片描述
    在这里插入图片描述

    class Solution {
    public:
        ListNode* deleteDuplicates(ListNode* head) {
            if (!head) {
                return head;
            }
    
            ListNode* cur = head;
            while (cur->next) {            //遍历链表
                if (cur->val == cur->next->val) {   //判断连续两个元素是否相同
                    cur->next = cur->next->next;   //删除连续两个相同的元素
                }
                else {
                    cur = cur->next;
                }
            }
            return head;
        }
    };
    复杂度分析
    时间复杂度:O(n),其中 nn 是链表的长度。
    空间复杂度:O(1)
  • 删除链表的倒数第 N 个结点(???): https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
    在这里插入图片描述
    在这里插入图片描述

    class Solution {
    public:
        int getLength(ListNode* head) {
            int length = 0;
            while (head) {
                ++length;
                head = head->next;
            }
            return length;
        }
    
        ListNode* removeNthFromEnd(ListNode* head, int n) {
            ListNode* dummy = new ListNode(0, head);
            int length = getLength(head);
            ListNode* cur = dummy;
            for (int i = 1; i < length - n + 1; ++i) {
                cur = cur->next;
            }
            cur->next = cur->next->next;
            ListNode* ans = dummy->next;    // ???
            delete dummy;
            return ans;
        }
    };
    
    复杂度分析
    时间复杂度:O(L),其中 L 是链表的长度。
    空间复杂度:O(1)

    new的使用: https://www.runoob.com/cplusplus/cpp-dynamic-memory.html

  • 两两交换链表中的节点: https://leetcode-cn.com/problems/swap-nodes-in-pairs/
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    class Solution {
    public:
        ListNode* swapPairs(ListNode* head) {
            //递归的终止条件
            if (head == nullptr || head->next == nullptr) {
                return head;
            }
            //假设链表是 1->2->3->4
         	//这句就先保存节点2
            ListNode* newHead = head->next; 
            //继续递归,处理节点3->4
    	    //当递归结束返回后,就变成了4->3
    	    //于是head节点就指向了4,变成1->4->3  
            head->next = swapPairs(newHead->next);
            //将2节点指向1
            newHead->next = head;
            return newHead;
        }
    };
    
    复杂度分析
    时间复杂度:O(n),其中 nn 是链表的节点数量。需要对每个节点进行更新指针的操作。
    空间复杂度:O(n),其中 nn 是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。
    
  • 两数相加 II: https://leetcode-cn.com/problems/add-two-numbers-ii/
    在这里插入图片描述
    在这里插入图片描述

    /**
     * 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) {
            stack<int> s1, s2;            // stack库
            while (l1) {
                s1.push(l1 -> val);
                l1 = l1 -> next;
            }
            while (l2) {
                s2.push(l2 -> val);
                l2 = l2 -> next;
            }
            int carry = 0;
            ListNode* ans = nullptr;
            while (!s1.empty() or !s2.empty() or carry != 0) {
                int a = s1.empty() ? 0 : s1.top();   // ?并不是一个运算符,?:才是,这是C++种唯一一个三目运算符。
                int b = s2.empty() ? 0 : s2.top();   //<表达式1>?<表达式2>:<表达式3>
                if (!s1.empty()) s1.pop();       //它的意思是,如果表达式1成立,则输出表达式2的值,否则输出表达式3的值。
                if (!s2.empty()) s2.pop();
                int cur = a + b + carry;
                carry = cur / 10;
                cur %= 10;
                auto curnode = new ListNode(cur);
                curnode -> next = ans;
                ans = curnode;
            }
            return ans;
        }
    };
    复杂度分析
    时间复杂度:O(max(m,n)),其中 m 和 n 分别为两个链表的长度。我们需要遍历两个链表的全部位置,而处理每个位置只需要 O(1) 的
    时间。
    空间复杂度:O(m+n),其中 m 和 n 分别为两个链表的长度。空间复杂度主要取决于我们把链表内容放入栈中所用的空间。
    
  • 回文链表: https://leetcode-cn.com/problems/palindrome-linked-list/
    在这里插入图片描述
    在这里插入图片描述

    /**
     * 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:
        bool isPalindrome(ListNode* head) {
            vector<int> vals;
            while (head != nullptr)
            //emplace_back函数的作用是减少对象拷贝和构造次数,用于对临时对象的赋值。
            // 在使用push_back函数往容器中增加新元素时,必须要有一个该对象的实例才行,而 
            // emplace_back可以不用,它可以直接传入对象的构造函数参数直接进行构造,减少一次拷贝和赋值操作。
            {
                vals.emplace_back(head->val);  
                head = head->next;                 
            }
            for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {
                if (vals[i] != vals[j]) {
                    return false;
                }
            }
            return true;
        }
    };
    
    复杂度分析
    时间复杂度:O(n),其中 n 指的是链表的元素个数。
    第一步: 遍历链表并将值复制到数组中,O(n)。
    第二步:双指针判断是否为回文,执行了 O(n/2) 次的判断,即O(n)。
    总的时间复杂度:O(2n) =O(n)。
    空间复杂度:O(n),其中 n 指的是链表的元素个数,我们使用了一个数组列表存放链表的元素值。
    
  • 分隔链表: https://leetcode-cn.com/problems/split-linked-list-in-parts/
    在这里插入图片描述
    在这里插入图片描述

    class Solution {
    public:
        vector<ListNode*> splitListToParts(ListNode* head, int k) {
            int n = 0;
            ListNode *temp = head;
            while (temp != nullptr) {
                n++;
                temp = temp->next;
            }
            int quotient = n / k, remainder = n % k;
    
            vector<ListNode*> parts(k,nullptr);
            ListNode *curr = head;
            for (int i = 0; i < k && curr != nullptr; i++) {
                parts[i] = curr;
                int partSize = quotient + (i < remainder ? 1 : 0);
                for (int j = 1; j < partSize; j++) {
                    curr = curr->next;
                }
                ListNode *next = curr->next;
                curr->next = nullptr;
                curr = next;
            }
            return parts;
        }
    };
    
    复杂度分析
    时间复杂度:O(n),其中 nn 是链表的长度。需要遍历链表两次,得到链表的长度和分隔链表。
    空间复杂度:O(1)。只使用了常量的额外空间,注意返回值不计入空间复杂度
    
  • 奇偶链表: https://leetcode-cn.com/problems/odd-even-linked-list/
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    /**
     * 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* oddEvenList(ListNode* head) {
            if (head == nullptr) {
                return head;
            }
            ListNode* evenHead = head->next;
            ListNode* odd = head;
            ListNode* even = evenHead;
            while (even != nullptr && even->next != nullptr) {
                odd->next = even->next;
                odd = odd->next;
                even->next = odd->next;
                even = even->next;
            }
            odd->next = evenHead;
            return head;
        }
    };
    
    复杂度分析
    时间复杂度:O(n),其中 n 是链表的节点数。需要遍历链表中的每个节点,并更新指针。
    空间复杂度:O(1)。只需要维护有限的指针。
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

只搬烫手的砖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值