(纯C)迭代和递归的方式反转单链表时间复杂度为O(n)

//反转链表
//迭代的
//递归的

原题

cf479e60caf94945a91fb86b850f885d.png

 

运行结果

84c3202a93324ea2a7ca57afaf3230c3.png

 

迭代的思路

空间换时间,创建一个顺序表储存所有的链表地址

整体思路:创建一个指针使其遍历到链表最后,期间记录经过的节点地址

如图

cbb8484eec094352b2ccfdb7d38ac136.png

迭代代码如下:

时间复杂度O(n)

空间复杂度O(n)

struct ListNode* reverseList(struct ListNode* head)
{
    if (head == NULL)
    {
        return head;
    }
    int capacity = 10;//顺序表容量
    struct ListNode** all_p = (struct ListNode**)malloc(sizeof(struct ListNode*) * capacity);
    //在堆上开辟一块空间
    //这块空间的大小为一个地址的大小(4字节)
    //即模拟实现一个指针数组
    struct ListNode* back = NULL;//后面的指针
    struct ListNode* end = NULL;//最后面的指针
    int count = 0;//计节点数
    back = head;
    while (back->next)//使后面的指针指向链表最后一个节点//并且记录所有的节点
    {
        if (count >= capacity - 2)//顺序表扩容
        {
            all_p = (struct ListNode**)realloc(all_p, sizeof(struct ListNode*) * (capacity *= 2));
            //结构体指针数组扩容
        }
        all_p[count] = back;//把所有的链表地址记录在一个指针数组中,并依据序排列
        back = back->next;
        count++;
    }
    //循环结束时back是最后一个节点的地址
    //count是节点数 - 1
    //因为指针数组中,链表的最后一个地址没有记录
    //最后的有效数组下标为count- 1
    end = back;

    while (back != head)//把记录下来的地址写回去
    {
        back->next = all_p[count - 1];//倒着放回
        back = back->next;//倒着访问
        count--;
    }
    back->next = NULL;
    return end;
}

 

用于测试的创建的链表

int main()
{
    struct ListNode* test1 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test2 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test3 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test4 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test5 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test6 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test7 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test8 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test9 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test10 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test11 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test12 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* test13 = (struct ListNode*)malloc(sizeof(struct ListNode));
    test12->val = 12;
    test12->next = NULL;
    test11->val = 11;
    test11->next = test12;
    test10->val = 10;
    test10->next = test11;
    test9->val = 9;
    test9->next = test10;
    test8->val = 8;
    test8->next = test9;
    test7->val = 7;
    test7->next = test8;
    test6->val = 6;
    test6->next = test7;
    test5->val = 5;
    test5->next = test6;
    test4->val = 4;
    test4->next = test5;
    test3->val = 3;
    test3->next = test4;
    test2->val = 2;
    test2->next = test3;
    test1->val = 1;
    test1->next = test2;
    test13 = reverseList(test1);
    return 0;
}

 

递归思路如下

当为空链表和单链表时直接输出结果

只有两个链表的时候,只需要交换这两个链表的next

所以程序需要递归到只有一个链表的时候,

回归后

用提前保存之前的地址的之后的地址交换

如图

650baa51bf864095b2fd8b3768e3fabe.png

递归时head=tmp,从左向右

但回归时 

tmp刚好是head的上一个地址

所以,直接把tmp的地址放在head->next 就成功逆序了链表

 

 

代码如下

时间复杂度O(N)

 空间复杂度O(n)

 

    struct ListNode* end = NULL;
    struct ListNode* tmp = head;//记录当前的地址
    static int count = 0;//记录节点数
    int flag = 0;//标记第一次递归
    if (count == 0)
    {
        flag = 1;
    }
    if (head == NULL)//没有节点
    {
        return head;
    }
    else if (head->next == NULL)//只有一个节点
    {
        count++;//最后一个也要计数
        return head;
    }
    else//至少有两个节点
    {
        head = head->next;//头的地址改为下一个节点
        count++;//每次递归都会改变count的值,其值等于第几个节点,只有第一次为1
        end = reverseList(head);
        //if (head->next == NULL)//最后一个节点不会来到这里,所以需要倒数第二个节点进行处理最后一个节点
        //{
        //    head->next->next = tmp;
        //}
        head->next = tmp;//此时head已经是原来头地址的下一个地址,所以只要把源地址放入

    }
    if (flag)//第一次开始递归时的节点next置为空
    {
        tmp->next = NULL;
    }
    return end;

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值