• 链表题目一道


    原题链接
    来源:剑指offer, Hulu面试题

    题意很简单。给定一个单链表,反转这个单链表,返回翻转后的头节点。

    方法一 借助栈的性质

    要将链表翻转,很容易想到借助栈的后进先出的性质来改变链表的顺序。

    将链表节点顺序压入栈中,链表节点全部进栈以后,取栈顶元素作为新链表的头节点,然后将元素不断出栈,每出栈一个元素就连接到新链表的末尾。

    时间复杂度:将链表元素压入栈中需要遍历一次链表,将栈中所有元素连接起来需要遍历一遍栈,时间复杂度是O(n).
    空间复杂度:需要额外开一个栈存放所有元素,空间复杂度是O(n).

    代码如下:

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if(head == NULL) {
                return head;
            }
            stack<ListNode*> st;
            ListNode* cur = head;                        //记录当前节点
            while(cur != NULL) {                         //只要节点非空,就压入栈中,指针后移,处理下一个节点
                st.push(cur);
                cur = cur -> next;
            }
            auto dummy = new ListNode(-1);               //用一个附加头节点的next指针指向翻转后的链表的头节点,这样返回值就可以是dummy -> next
            if(!st.empty()) {                            
                cur = st.top();                                          
                dummy -> next = cur;                    //附加头节点的next指针指向栈顶元素
                st.pop();
            }
            while(!st.empty()) {                        
                cur = cur -> next = st.top();            //不断取出栈顶元素连接到新链表的末尾
                st.pop();
            }
            cur -> next = NULL;                          //链表尾节点的next指针指向空
            return dummy -> next;
        }
    };
    

    方法二 迭代

    翻转操作就是将链表中所有节点的next指针指向他们原来的前驱节点。

    由于链表是单链表,只有next指针没有pre指针指向前驱节点,因此我们需要额外用一个变量pre记录每个节点的前驱节点,并且将当前节点cur
    next指针指向pre,由于改变了curnext指针的值,但是在改变next的值之前我们还是需要记录cur的下一个节点(为了进行遍历/迭代),
    所以还需要额外用一个变量next来记录节点(按原来顺序的)下一个节点。

    时间复杂度:只遍历一次链表,时间复杂度是O(n).
    空间复杂度:额外开了三个变量pre, cur, next,空间复杂度是O(1).

    代码如下:

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            ListNode* pre = NULL;
            ListNode* cur = head;
            while(cur != NULL) {
                ListNode* next = cur -> next;      //在修改cur的next指针的值之前需要记录cur的下一个节点
                cur -> next = pre;                 //将next指针指向前驱节点
                pre = cur;                         //pre和cur都向后移动,翻转下一个节点
                cur = next;
            }
            return pre;                            //跳出上面的while循环时,cur为空,pre指向原链表的最后一个节点,也就是翻转后的链表的头节点
        }
    };
    

    方法三 递归

    reverseList函数的功能是给定一个链表的头节点,返回新链表的头节点(也就是原链表的尾节点),
    head最开始为头节点(只有一个节点,我们可以认为已经翻转好了,只不过还没修改next指针为空),
    我们可以递归处理head -> nexthead -> next是当前遍历到的链表长度的尾节点(也就是加入了一个新的需要翻转的节点),
    也就是新链表的头节点newHead, 这时需要先记录newHeadnext指针指向head(翻转):head -> next -> next = head;
    然后headnext指针要指向空(原来指向后继结点)。

    时间复杂度:链表中每个节点遍历一次,时间复杂度是O(n).
    空间复杂度:总共递归n层,总共需要O(n)的空间,系统栈的空间复杂度是O(n).

    代码如下:

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if(head == NULL || head -> next == NULL) {
                return head;
            }
            ListNode* newHead = reverseList(head -> next);            //递归
            head -> next -> next = head;
            head -> next = NULL;
            return newHead;
        }
    };
    
  • 相关阅读:
    快速排序法的C#实现
    SQL语句执行效率及分析(note)
    如何在C#中运行数学表达式字符串
    TSQL删除重复数据,保留一条
    C#对象序列化XML时报错:反射类型XXX时出错
    c#如何扩展类型的内置方法
    把数字转换成阿拉伯数字大写的程序
    使用C#格式化字符串
    Silverlight中自己定义实现的双击方法
    原来是这样:C#中new一个对象时,发生了什么事?
  • 原文地址:https://www.cnblogs.com/linrj/p/13277957.html
Copyright © 2020-2023  润新知