zoukankan      html  css  js  c++  java
  • 链表反转总结

    1. 反转整个链表

    递归反转链表

    比如现在有这样一个链表:
    (K_1 ightarrow K_2 ightarrow ... ightarrow K_{i} ightarrow K_{i+1} ightarrow ... ightarrow K_{n})
    (K_{i})之后的链表都已经完成了反转,如下:
    (K_1 ightarrow K_2 ightarrow ... ightarrow K_{i}leftarrow K_{i+1}leftarrow ... leftarrow K_{n})

    下一步该如何操作呢?
    Ki->next->next = ki

    因此可以据此写出递归代码:

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if (head == nullptr || head->next == nullptr)
                return head;
            
            ListNode* p = reverseList(head->next);
            head->next->next = head;
            head->next = nullptr;
            return p;
        }
    };
    

    时间复杂度:(O(n))
    空间复杂度:(O(n))

    迭代反转链表

    定义一个prev指针作为新链表头指针,使用头插法将待反转链表中的节点按顺序插入到prev链表中,返回。

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

    时间复杂度:(O(n))
    空间复杂度:(O(1))

    2. 链表中部分节点的反转

    对于链表中部分节点的反转,分为三步:

    • 定位到待反转的节点
    • 断链+反转
    • 连接断链

    代码实现:

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode* reverseBetween(ListNode* head, int m, int n) {
            if (head == nullptr || head->next == nullptr || m == n) 
                return nullptr;
    
            // 定位,使curr指向第m个节点
            ListNode *prev = nullptr, *curr = head;
            while (m > 1) {     
                prev = curr;
                curr = curr->next;
                --m;
                --n;
            }
    
            // 断链反转,得到反转后的链表third
            ListNode *tail = curr, *third = nullptr, *tmp;
            while (n > 0) {     
                tmp = curr->next;
                curr->next = third;
                third = curr;
                curr = tmp;
                --n;
            }
    
            // 重新连接上断链
            tail->next = tmp;
            if (prev != nullptr) {  
                prev->next = third;
            } else {          
                head = third;    // 若m=1,将断链的头指针赋给head
            } 
                
            return head;
        }
    };
    

    时间复杂度:(O(n))
    空间复杂度:(O(1))

    总结

    对于链表反转的题目,最简单的办法就是新建一个数组来保存链表,然后逆序地重新构建新链表,但是这种做法的时间和空间开销都比较大;更好的办法是在原链表上直接反转,即前述的两种方法:迭代头插法与递归反转,迭代头插法效率高,而递归反转的代码实现非常简洁。
    在做链表题的时候在纸上画一下图能够使操作过程变得更清晰。

    CS专业在读,热爱编程。
    专业之外,喜欢阅读,尤爱哲学、金庸、马尔克斯。
  • 相关阅读:
    程序员,你有多久没关爱自己了?
    如何优化 Java 性能?
    想让安卓 APP 如丝般顺滑?
    用 OneAPM Cloud Insight 监控 Docker 性能
    盘点 OSX 上最佳的 DevOps 工具
    荣誉,还是苦逼?| 也议全栈工程师和DevOps
    小程序基础知识点讲解-WXML + WXSS + JS,生命周期
    第二十一节:Java语言基础-关键字,标识符,注释,常量和变量,运算符
    第二十一节:Java语言基础-关键字,标识符,注释,常量和变量,运算符
    第二十一节:Java语言基础-关键字,标识符,注释,常量和变量,运算符
  • 原文地址:https://www.cnblogs.com/jmhwsrr/p/14149745.html
Copyright © 2011-2022 走看看