leetcode LCR24反转单链表

本文介绍了如何使用迭代和递归两种方法实现单链表的反转,包括Java和C语言版本的代码示例及详细解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反转单链表

题目描述

题目分析 

先来说迭代的思想:

上面next = cur->next应该放在cur->next = pre前面执行,这里笔误 

再来说递归的思想:

 题目代码

这个代码里面我加了我自己写的测试数据,自己可以去找对应的部分,直接粘贴赋值就可以在leetcode提交成功,我们就没有把迭代与递归单独分开了,里面还有大量的注释说明,请结合题目分析仔细观看

java版本

package com.pxx.test;

import sun.awt.image.ImageWatched;

class LinkList {
    private ListNode head;
    private int length;//长度

    //链表结点的一个定义
    static class ListNode {
        int value;
        ListNode next;

        //初始化一个结点
        public ListNode(int value) {
            this.value = value;
            this.next = null;
        }
    }

    public ListNode getHead() {
        return this.head;
    }

    //实现数据的尾插
    public void insertEnd(int value) {
        ListNode newNode = new ListNode(value);
        if (newNode != null) {
            if (head == null) {
                head = newNode;//指向第一个结点
            } else {
                ListNode cur = head;
                //移动到最后一个结点的位置
                while (cur.next != null) {
                    cur = cur.next;
                }

                //最后一定是在cur加入结点
                cur.next = newNode;
            }
            length++;
        }
    }

    //打印
    public void printList(ListNode head) {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    //利用递归反转链表
    //返回反转之后的链表头
    public ListNode reverse(ListNode head) {
        //安全检查都必须先进行操作
        if (head == null || head.next == null) {
            return head;
        } else {
            ListNode pre = head , cur = head.next;
            //接收最后一次的返回,就是最后一个节点,中间是没有结点要返回的
            //这里为什么传入pre.next,而不是cur.next
            //目的其实就是为了每一个值都能被链上,为什么那么说呢
            //1(pre) 2(cur) 3 4 5
            //如果按照cur.next来进行移动
            //1 <- 2这一部分递归了之后,就会进入 3(pre(cur会直接到3变成pre))   4(内部的cur)
            //那么我请问2 3中间这条线被狗吃了吗?所以以pre.next往下递归
            //上面pre循环到5号结点,也就是最后一组递归就要结束,因此有head.next = null就结束递归
            ListNode res = reverse(pre.next);
            cur.next = pre;
            //目的把第一个结点的next指向null
            //这里千万不能吧pre.next 与 cur.next进行比较
            //否则会造成最开头的两个数据一直轮替
            //这样来说 1  2  3  4(pre)  5(cur)假设当前指针是这样来指的
            //4 <- 5 此时4也还是4->5的,进入pre.next把指向变为了null(注意这是递归所以从后往前分析)
            //不太懂的请结合我的递归分析图来看
            //null <- 4 <- 5
            //那么就行往前递归3(pre) 4(cur) 5,也就是null <- 3 <- 4 <- 5
            //我们注意到一个问题就是4的pre已经在中间的时候被改向了第一个结点
            //那么对于1(pre) <- 2(cur)来说,1的.next又等于cur,所以1.pre = null
            //null <- 1 <- 2......这个时候,程序已经全部执行完
            if (pre.next == cur) {
                pre.next = null;
            }
            return res;
        }
    }

    //利用迭代思想实现
    public ListNode reverse1(ListNode head) {
        //空结点,直接返回一个null
        if (head == null) {
            return null;
        }
        //定义三个指针进行迭代
        ListNode pre = head, cur = head.next, next;//next用于在中间轮替指针可以先不用初值
        //没循环之前先把第一个结点next指向null
        head.next = null;
        while (cur != null) {
            //这里迭代就是
            // null <- 1(pre) 2(cur) 3(next) 4        5
            // null <- 1 <-   2(pre) 3(cur)  4(next)  5
            // null <- 1 <-   2 <-   3(pre)  4(cur)   5(next)
            // null <- 1 <-   2 <-   3 <-    4(pre)   5(cur)   next(这个时候还会进入循环里面更改cur.next指针)
            //这个时候cur == null,结束,pre指向最后一个结点返回

            //保留next指向
            next = cur.next;//这一步必须放在cur.next前面,不然cur.next就被先改变了,next的位置就不对了
            cur.next = pre;
            //改变pre与cur
            pre = cur;//这一步放在cur=next,这里就是先赋值pre,在改变cur
            cur = next;
        }
        //最后一定是pre指向了最后一个结点
        return pre;
    }

}


public class Test4 {

    public static void main(String[] args) {
        LinkList linkList = new LinkList();

        //开始插入数据
        linkList.insertEnd(1);
        linkList.insertEnd(2);
        linkList.insertEnd(3);
//        linkList.insertEnd(4);
//        linkList.insertEnd(5);


        linkList.printList(linkList.getHead());

        //反转链表
        LinkList.ListNode head = linkList.reverse(linkList.getHead());

        linkList.printList(head);

    }
}

 c语言版本测试代码,仅做参考

#include <stdio.h>
#include <stdlib.h>

// 链表结点的定义
struct ListNode {
    int value;
    struct ListNode* next;
};

// 链表的定义
struct LinkedList {
    struct ListNode* head;
    int length;
};

// 初始化链表
void initLinkedList(struct LinkedList* list) {
    list->head = NULL;
    list->length = 0;
}

// 在链表末尾插入一个新节点
void insertEnd(struct LinkedList* list, int value) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode != NULL) {
        newNode->value = value;
        newNode->next = NULL;

        if (list->head == NULL) {
            list->head = newNode;
        } else {
            struct ListNode* cur = list->head;
            while (cur->next != NULL) {
                cur = cur->next;
            }
            cur->next = newNode;
        }
        list->length++;
    }
}

// 打印链表的元素
void printList(struct ListNode* head) {
    struct ListNode* cur = head;
    while (cur != NULL) {
        printf("%d ", cur->value);
        cur = cur->next;
    }
    printf("\n");
}

// 递归反转链表
struct ListNode* reverse(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    } else {
        struct ListNode* pre = head;
        struct ListNode* cur = head->next;
        struct ListNode* res = reverse(pre->next);
        cur->next = pre;
        if (pre->next == cur) {
            pre->next = NULL;
        }
        return res;
    }
}

int main() {
    struct LinkedList linkList;
    initLinkedList(&linkList);

    // 开始插入数据
    insertEnd(&linkList, 1);
    insertEnd(&linkList, 2);
    insertEnd(&linkList, 3);

    printList(linkList.head);

    // 反转链表
    linkList.head = reverse(linkList.head);

    printList(linkList.head);

    return 0;
}

好了,祝早安,午安,晚安

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值