ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-07-17 12:02:19  阅读:218  来源: 互联网

标签:node head NULL next 重新整理 2.0 LinkNode 节点 逆序


单链表逆序

背景

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

一.单链表基本节点

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.递归的思想

现在,我们用递归的思想来分析这个问题。先假设有这样一个函数,可以将以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;
}

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

完整代码

// 单链表
#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;
}

//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;
}

//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;
}

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);

    return 0;
}

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

标签:node,head,NULL,next,重新整理,2.0,LinkNode,节点,逆序
来源: https://blog.csdn.net/liuqingsongmsdn2014/article/details/118854036

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有