力扣第206题-反转链表

反转链表的效果示意图

要改变链表结构时,通常加入一个创建的临时头结点会更容易操作

时间复杂度:遍历2遍,2n

空间复杂度:额外创建一个栈,n

(空间创建一个数组长度最大为5000,你说这个数组是栈也可以,只要光通过一端对它进行操作就可以了)

/**
 * 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* reverseList(ListNode* head) {
    ListNode h;//临时头结点
    h.next = head;

    //利用栈
    stack<int>s;//创建栈
    for(ListNode*p=head;p!=NULL;p=p->next)//从头遍历
    {
        //入栈
       s.push(p->val);//将val值挨个放入栈 
    }
    for(ListNode*p=head;p!=NULL;p=p->next)
    {
        //出栈
        p->val=s.top();
        s.pop();//让s.top出栈
    }

    return h.next;
    
    }
};

利用单链表的头插法进行逆置,将所有结点从后往前重新头插一遍就好了

但比上面的算法要慢一点,因为修改一两个指针的指向也要花时间的

指针p指向图中位置,将head->next断开,赋值为空,

然后利用头插,将每一个结点再重新头插到这个头结点的单链表里面

先插一个1,再插一个2,因为是头插,所以这个2就在1的前面;同理,4,3,2,1

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head==NULL||head->next==NULL)//只有0(无法反转)或1(转完还是自己)个点,
    //直接返回(反转要至少2个数据结点以上)
    return head;
    struct ListNode h;//临时头结点
    h.next=NULL;//头结点为空的单链表,等会用来头插
    //头插需要单独一个指针把后面标记起来,以免找不到数据了
    //struct ListNode*p=head->next;不用初始化,while里面有
    struct ListNode*p;//用p记录
    while(head!=NULL)//后面还有结点
    {
        //头插,先绑后面,再绑前面
        p=head->next;//初始化
        //把head头插到h中
        head->next=h.next;// head->next=NULL,右
        h.next=head;//左
        head=p;//结点往后走
    }

    return  h.next;
}

即双指针解法

一个指针current指向头结点head,正常正向链表head的next指向后(右)边下一个结点

现在要反转,就是说head的next要反过来指向前(左)边,即head成了最后一个结点指向空

则需要另一个指针pre,定义pre为cur指针结点的前一个位置的指针,方便让cur从指向后一位的方向改成指向前一位,所以pre定义在cur头结点的前面

/**
 * 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* reverseList(ListNode* head) {
    ListNode* temp; // 保存cur的下一个节点
        ListNode* cur = head;//初始化,cur指向头结点
        ListNode* pre = NULL;//初始化,
        //pre在cur的前一个,方便反转操作cur->next = pre;为空是cur第一次反转后就从头结点变成尾结点了
        while(cur)//cur!=NULL为循环终止条件 
        {
            temp = cur->next;  // 保存一下 cur的下一个节点,绑住后面,因为接下来要改变cur->next
            cur->next = pre; // 翻转操作
            // 更新pre 和 cur指针
            pre = cur;//pre先向后走移动,将2边连接住
            //同时,若先移动cur,即cur = temp;那么此时cur的值已经改变了,
            //就不能 pre = cur向后移动了,那么pre就不能移动到cur当初的位置了
            cur = temp;//cur再向后走
        }//出循环遍历则cur==NULL,此时pre指向反转完的新头结点,原尾结点
        return pre;//返回头结点
    }
};

/**
 * 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* reverse(ListNode* pre,ListNode* cur)//反转链表函数
     {
        if(cur == NULL)//循环终止条件,这里是递归终止条件
        return pre;
        ListNode* temp = cur->next;//暂存后面
        cur->next = pre;//反转方向
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        //向后移动在双指针里面是一层循环,在递归里面是进入下一层递归
        //下一层递归里面参数pre = cur;cur = temp;所以把(cur,temp)参数传进去
        return reverse(cur,temp);//reverse(pre,cur)的实参
    }
    ListNode* reverseList(ListNode* head)//力扣给的主函数,里面光调用反转函数就好了
    {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);//reverse(pre,cur)的实参
    }
};

上面两第二个图片的运行结果是错的

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值