单链表逆序2.0(重新整理)

本文介绍两种单链表逆序的方法:迭代和递归。迭代通过三个指针完成链表节点的逆序链接,而递归则通过不断拆分和回溯实现。文章提供了详细的代码示例和流程解析。

背景

“单链表逆序”问题。很多公司的面试题库中都有这道题,有的公司明确题目要求不能使用额外的节点存储空间,有的没有明确说明,但是如果面试者使用了额外的节点存储空间做中转,会得到一个比较低的分数。如何在不使用额外存储节点的情况下使一个单链表的所有节点逆序?

一.单链表基本节点

typedef int DataType;

typedef struct node
{
    DataType data;
    struct node *next;
}LinkNode;

基本链表操作先看这篇文章,然后继续往下看

二.逆序实现的几种方式

1.1 head->A->B->C->NULL ==> NULL<-A<-B<-C<-head

思路:1.A变为最后一个节点,A->next = NULL; 然后head->B->A 这种思路

//head->A_node->B_node->C_node->NULL
int reverse_linklist_Debug(LinkNode *head)
{
    LinkNode *cur_node = head;
    LinkNode *next_node = NULL;
    //A_node B_node exist
    if(cur_node->next == NULL || cur_node->next->next == NULL)
    {
        return -1;
    }
    
    next_node = cur_node->next->next; //next_node = head->A_node->B_node
    cur_node->next->next = NULL; //cur_node = head->A_node->NULL
    while(next_node)  //B_node exist
    {
        cur_node = next_node->next;    //cur_node = C_node
        next_node->next = head->next; //B->A
        head->next = next_node;       //Head->B
        next_node = cur_node;         //next_node = cur_node = C_node
    }

    return 0;
}

1.2 head->A->B->C->NULL ==> NULL<-A<-B<-C<-head

但是1.2在1.1上做了优化,pre head next,思路更明确一些,有些不好理解,细细推敲一下。
我们先用迭代循环的思想来分析这个问题,链表的初始状态如图(1)所示:
在这里插入图片描述
图(1)

图(1)初始状态

pre->next = NULL;
Next_Node = head->next;

初始状态,prev是NULL,head指向当前的头节点A,next指向A节点的下一个节点B。首先从A节点开始逆序,将A节点的next指针指向prev,因为prev的当前值是NULL,所以A节点就从链表中脱离出来了,然后移动head和next指针,使它们分别指向B节点和B的下一个节点C(因为当前的next已经指向B节点了,因此修改A节点的next指针不会导致链表丢失)。逆向节点A之后,链表的状态如图(2)所示:
在这里插入图片描述
图(2)

图(2)经过第一次迭代后的状态
从图(1)的初始状态到图(2)状态共做了四个操作,这四个操作的伪代码如下:

head->next = prev;
prev = head;
head = Next_Node ;
Next_Node = head->next;

在这里插入图片描述
图(3)

这四行伪代码就是循环算法的迭代体了,现在用这个迭代体对图(2)的状态再进行一轮迭代,就得到了图(3)的状态.。图(3)经过第二次迭代后的状态,那么循环终止条件呢?现在对图(3)的状态再迭代一次得到图(4)的状态:
在这里插入图片描述
图(4)
此时可以看出,在图(4)的基础上再进行一次迭代就可以完成链表的逆序,因此循环迭代的终止条件就是当前的head指针是NULL。
现在来总结一下,循环的初始条件是:
prev = NULL;
循环迭代体是:

next = head->next;
head->next = prev;
prev = head;
head = next;

循环终止条件是:
head == NULL

根据以上分析结果,逆序单链表的循环算法如下所示:

LinkNode *ReverseLink1(LinkNode *head)
{
    LinkNode *next;
    LinkNode *prev = NULL;

    while(head != NULL)
    {
       next = head->next;
       head->next = prev;
       prev = head;
       head = next;
    }

    return prev;
}

2.0递归的思想

现在,我们用递归的思想来分析这个问题。先假设有这样一个函数,可以将以head为头节点的单链表逆序,并返回新的头节点指针,应该是这个样子

LinkNode *ReverseLink2(LinkNode *head)

现在利用ReverseLink2()对问题进行求解,将链表分为当前表头节点和其余节点,递归的思想就是,先将当前的表头节点从链表中拆出来,然后对剩余的节点进行逆序,最后将当前的表头节点连接到新链表的尾部。第一次递归调用ReverseLink2(head->next)函数时的状态如图(5)所示:
在这里插入图片描述
图(5)

图(5)第一次递归状态图
这里边的关键点是头节点head的下一个节点head->next将是逆序后的新链表的尾节点,也就是说,被摘除的头接点head需要被连接到head->next才能完成整个链表的逆序,递归算法的核心就是一下几行代码:

    newHead = ReverseLink2(head->next); /*递归部分*/
    head->next->next = head; /*回朔部分*/
    head->next = NULL;

现在顺着这个思路再进行一次递归,就得到第二次递归的状态图:
在这里插入图片描述
图(6)
第二次递归状态图
再进行一次递归分析,就能清楚地看到递归终止条件了:
在这里插入图片描述
图(7)
第三次递归状态图
递归终止条件就是链表只剩一个节点时直接返回这个节点的指针。可以看出这个算法的核心其实是在回朔部分,递归的目的是遍历到链表的尾节点,然后通过逐级回朔将节点的next指针翻转过来。递归算法的完整代码如下:

//head->A_node->B_node->C_node->NULL
LinkNode *ReverseLink2(LinkNode *head)
{
   LinkNode *newHead;
   if((head == NULL) || (head->next == NULL))
       return head;

    newHead = ReverseLink2(head->next); /*递归部分*/
    head->next->next = head; /*回朔部分*/
    head->next = NULL;
   return newHead;
}

循环还是递归?这是个问题。当面对一个问题的时候,不能一概认为哪种算法好,哪种不好,而是要根据问题的类型和规模作出选择。对于线性数据结构,比较适合用迭代循环方法,而对于树状数据结构,比如二叉树,递归方法则非常简洁优雅。

2.1 (2.0递归的思想备注说明)

后面仔细看了递归调用,总感觉哪里有问题,原作者的思想有部分没有表达出来,所有画了一幅图出来
在这里插入图片描述

三.完整代码

// 单链表
#include <stdio.h>
#include <stdlib.h>

typedef int DataType;

typedef struct node
{
    DataType data;
    struct node *next;
}LinkNode;

LinkNode *create_empty_linklist()
{
    LinkNode *head = NULL;
    head = (LinkNode *)malloc(sizeof(LinkNode));
    head->next = NULL;
    return head;
}


int is_empty_linklist(LinkNode *head)
{
    return head->next == NULL;
}

int insert_head_linklist(LinkNode *head,DataType data)
{
    LinkNode *temp = NULL;
    temp = (LinkNode *)malloc(sizeof(LinkNode));
    temp->data = data;
    temp->next = head->next;
    head->next = temp;
    return 0;
}

int insert_tail_linklist(LinkNode *head,DataType data)
{
    LinkNode *temp = NULL;
    LinkNode *p = head;
    temp = (LinkNode *)malloc(sizeof(LinkNode));
    temp->data = data;
    //找到尾结点
    while(p->next)
    {
        p = p->next;
    }

    //temp->next = NULL;
    temp->next = p->next;
    p->next = temp;
    return 0;
}

int insert_order_linklist(LinkNode *head,DataType data)
{
    LinkNode *temp = NULL;
    LinkNode *p = head;
    temp = (LinkNode *)malloc(sizeof(LinkNode));
    temp->data = data;
#if 0
    while(p->next)
    {
        if(p->next->data > data)
        {
            break;
        }
        p = p->next;
    }
#endif

    /*while(p->next != NULL && p->next->data < data)*/
    while(p->next && p->next->data < data)
    {
        p = p->next;
    }

    temp->next = p->next;
    p->next = temp;
    return 0;
}

int delete_assign_node(LinkNode *head,DataType data)
{

    LinkNode *temp = NULL;
    LinkNode *p = head;
    while(p->next && p->next->data != data)
    {
        p = p->next;
    }

    if(p->next == NULL)
    {
        return -1;
    }

    temp = p->next;
    p->next = p->next->next;
    free(temp);
    return 0;
}

int print_linklist(LinkNode *head)
{
    LinkNode *p = head->next;
    while(p)
    {
        printf("%d ",p->data);
        p = p->next;
    }

    putchar('\n');
    return 0;
}

int print_linklist2(LinkNode *head)
{
    LinkNode *p = head;
    while(p)
    {
        printf("%d ",p->data);
        p = p->next;
    }

    putchar('\n');
    return 0;
}
/*
int reverse_linklist(LinkNode *head)
{
    LinkNode *p = head,*q = NULL;
    if(p->next == NULL || p->next->next == NULL)
    {
        return -1;
    }

    q = p->next->next;
    p->next->next = NULL;
    while(q)
    {
        p = q->next;
        q->next = head->next;
        head->next = q;
        q = p;
    }

    return 0;
}*/

int reverse_linklist(LinkList *head)
{
    if(head->next == NULL || head->next->next == NULL)
    {
        return 0;
    }

    LinkList *tail = head->next;
    LinkList *current =  head->next->next;
    tail->next = NULL;

    LinkList  *temp = NULL;
    while(current)
    {
        temp = current->next;
        current->next = head->next;
        head->next = current;
        current = temp;
    }

    return 0;
}

//head->A_node->B_node->C_node->NULL
int reverse_linklist_1_1(LinkNode *head)
{
    LinkNode *cur_node = head;
    LinkNode *next_node = NULL;
    //A_node B_node exist
    if(cur_node->next == NULL || cur_node->next->next == NULL)
    {
        return -1;
    }


    next_node = cur_node->next->next; //next_node = head->A_node->B_node
    cur_node->next->next = NULL; //cur_node = head->A_node->NULL
    while(next_node)  //B_node exist
    {
        cur_node = next_node->next;    //cur_node = C_node
        next_node->next = head->next; //B->A
        head->next = next_node;       //Head->B
        next_node = cur_node;         //next_node = cur_node = C_node
    }

    return 0;
}

LinkNode *reverse_linklist_1_2(LinkNode *head)
{
    LinkNode *next;
    LinkNode *prev = NULL;

    while(head != NULL)
    {
       next = head->next;
       head->next = prev;
       prev = head;
       head = next;
    }

    return prev;
}

LinkNode *reverse_linklist_1_21(LinkNode *head)
{
    LinkNode *prev = NULL;
    LinkNode *cur = head;
    LinkNode *next = NULL;

    while(cur != NULL)
    {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }

    return prev;
}

//head->A_node->B_node->C_node->NULL
LinkNode *reverse_linklist_2(LinkNode *head)
{
   LinkNode *newHead;
   if((head == NULL) || (head->next == NULL))
       return head;

    newHead = reverse_linklist_2(head->next); /*递归部分*/
    head->next->next = head; /*回朔部分*/
    head->next = NULL;
   return newHead;
}

LinkNode * reverse_list3(LinkList *head)
{
    if(head->next == NULL || head->next->next == NULL)
    {
        return 0;
    }

    LinkList *pf = NULL, *pb = NULL, *tmp = NULL;
    pf = head;

    pb = pf->next;
    head->next = NULL;
    while(pb != NULL)
    {
       tmp = pb;
       pb = pb->next;
       tmp->next = pf;
       pf = tmp;
    }
    head = pf;
    return head;
}//https://blog.youkuaiyun.com/peng_apple/article/details/79614846

int printf_linklist3(LinkNode *head)
{
    struct node *p = head;

    while(p->next != NULL)
    {
        printf("%d ",p->data);
        p = p->next;
    }

    printf("\n");

    return 0;
}

int main()
{
    LinkNode *head = NULL;
    int a[] = {1,5,3,4,7,9};
    int i = 0;
    int ret = 0;

    head = create_empty_linklist();
    for(i = 0;i < sizeof(a) / sizeof(a[0]);i++)
    {
        /*insert_head_linklist(head,a[i]);*/
        /*insert_tail_linklist(head,a[i]);*/
        insert_order_linklist(head,a[i]);
    }

//    print_linklist(head);
//    ret = delete_assign_node(head,5);
//    if(ret < 0)
//    {
//        printf("Assign data %d is not exist.\n",5);
//    }

    print_linklist(head);
    //LinkNode *reverse_node = ReverseLink2(head);
   // print_linklist2(reverse_node);

    //ReverseLink1(head);
    LinkNode *reverse_node = reverse_linklist_2(head);
    print_linklist2(reverse_node);
    //print_linklist(reverse_node);
    
    LinkList *L3 = reverse_list3(head);
    printf_linklist3(L3);

    return 0;
}

打印如下:
在这里插入图片描述
最后一个数是一个小bug,不过那不是重点了。

参考博客

  1. 单链表逆序掌握这两种思路就够了
  2. Linux(内核剖析):14—内核数据结构之链表(struct list_head)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静思心远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值