C语言:从尾到头打印链表

双12买了本《剑指offer》,里面好多题目之前都做过,但仅限于做出来,当时没有思考太多,很多都是直接调类(用的java)。今天开始重新学习,想着用C再实现一遍,这次要考虑更深层的东西,比如时间复杂度这些。

言归正传,再次学习还是从链表开始,第一道关于链表的题目是从尾到头打印链表。看到这道题,我第一反应想的是:把链表反转,然后再依次输出每个结点的值就好了,正好还有一道链表反转的题,一次能完成两个呢。看了作者的书,原来反转之后链表的结构就变了,如果面试官要求不能反转链表,这个解法就是不正确的。

因此这道题目的思路应该有三个:

  1. 反转链表,依次输出节点上的值;
  2. 利用栈先进后出的特点,将链表中的值依次入栈,再依次出栈;
  3. 递归。

由于反转链表在另一道题目当中,因此这里只考虑后两种情况。

利用栈从尾到头打印链表

首先我得定义链表的结构,这个太熟了,我几乎想也不想就写出了下面的代码:

typedef struct Linknode
{
    int val;          //存放数据
    Linknode* next;   //存放指针
}Linknode;

然后,后面的运行中我就遇到了大麻烦,十几个错误都是Linknode未定义。查看了很多资料才知道,定义中的next不能只用Linknode,还需要加上struct。好吧,脑子熟和手熟是两回事,我还是得多练习。
所以,正确的定义应该是:

typedef struct Linknode
{
    int val;                 //存放数据
    struct Linknode* next;   //存放指针
}Linknode;

定义好之后,我准备用C语言写这个逆转函数。参考了剑指offer,很简洁很清晰。
但是,书上是用C++实现的。我最近java和C混着学已经要晕,现在再学一个C++(虽然大学学过,但后来就没有再用)怕是要赶不上春招。。。emmm要哭了。。。我为什么这么菜!!!
所以我打算用C实现(java会让我忽略很多底层的东西)。然后我就发现,C里面没有定义栈啊!!好吧,既然都决定了,就硬着头皮上吧。本题目用到的是创建栈、入栈、出栈、判断栈空、判断栈满、查找栈顶操作,下面依次定义。

栈的定义

typedef struct Stack
{
    int* arr;       //存放栈的首地址
    int len;        //栈的长度
    int top;        //栈顶的下标
}Stack;

创建栈

Stack* create_stack(int len)
{
    Stack* stack = malloc(sizeof(Stack));
    stack->arr = malloc(siezof(int)*len);
    stack->len = len;
    stack->top = -1;
    return stack;
}

入栈

bool push_stack(Stack* stack,int val)
{
    if(full_stack(stack))
        return false;
    stack->arr[++stack->top] = val;
    return true;
}

判断栈是否已满

bool full_stack(Stack* stack)
{
    return stack->top + 1 >= stack->len;
}

出栈

bool pop_stack(Stack* stack)
{
    if(empty_stack(stack))
        return false;
    stack->top--;
    return true;
}

判断栈是否为空

bool empty_stack(Stack* stack)
{
    return stack->top == -1;
}

查找栈顶

int top_stack(Stack* stack)
{
    if(!empty_stack(stack))
        return NULL;
    return stack->arr[stack->top];
}

注意:这些函数都是我从CSDN前辈那里学习的,查找栈顶函数原来的返回值是:

return stack->arr + stack->top;

我在后面调试的过程中发现打印出的全都是各个结点的地址,因此做了点改动。

至此,准备工作就结束了。开始写逆转函数:

void PrintListReversingly_iteratively(Listnode* phead)
{
    if(phead == NULL)
        return;
        
    int temp;
    Stack* stack = create_stack(10);
    
    while(phead != NULL)
    {
        push_stack(stack,phead->val);
        phead = phead->next;
    }
    while(!empty_stack(stack))
    {
        temp = top_stack(stack);
        printf("%d ",temp);
        pop_stack(stack);
    }
    printf("\n");
}

接下来,我们要写主函数来调用这个函数:

int main(void)
{
    //创建单链表
    Listnode* head = (Listnode*)malloc(sizeof(Listnode));
    head->next = NULL;
    
    for(int i = 0;i < 7;i++)
    {
        Listnode* s= (Listnode*)malloc(sizeof(Listnode));
        s->val = i + 1;
        s->next = h-> next;
        h->next = s;
    }
    PrintListReversingly_iteratively(head);
    return 0;
}

运行一下:
在这里插入图片描述
看到输出结果,我困惑了,难道输出结果不应该是 7,6,5,4,3,2,1 ???哪里又错了,还有,最后那堆数字是什么鬼???
这时,我用单步调试看了一下调用PrintListReversingly_iteratively前链表的结构:
在这里插入图片描述
这样子不像有错误呀,我把head展开:
在这里插入图片描述
显然,我第一个插进去的数值1在链表末尾,最后插入的数值7却在头指针后面,而头指针是一串乱数字。
这时候,大学数据结构老师的声音在我耳边回响:头插法和尾插法,要考的!!!还好我认真学了,返回主函数,我构造链表的方法确实是头插法。至于头节点的数据,创建链表的过程时我并未给它赋值,所以随机赋了一个值,入栈的时候这个值也参与了(以后给头节点赋初值NULL即可)。

知道了问题所在,接下来修改代码:

int main(void)
{
    //创建单链表
    Listnode* head = (Listnode*)malloc(sizeof(Listnode));
    Listnode* end; 
    head->val = NULL;
    head->next = NULL;
    end = head;
    
    for(int i = 0;i < 7;i++)
    {
        Listnode* s= (Listnode*)malloc(sizeof(Listnode));
        s->val = i + 1;
        //尾插法
        end->next = s;
        end = s;
    }
    PrintListReversingly_iteratively(head);
    return 0;
}

运行一下:
在这里插入图片描述
什么也没有输出!!

在逆转函数前设置断点可以看到:
在这里插入图片描述
用尾插法之后链表的值为0,1,2,3,4,5,6,7,说明尾插法建立单链表成功。

这两幅图片不一样的地方为链表结尾,头插法中,链表结尾的next为NULL,而尾插法中,链表结尾的next无值。从断点开始单步调试至val为7时弹出错误:在这里插入图片描述
说明确实是尾节点的next有问题。

所以在调用逆转函数之前,给end的next赋值NULL。调试一下,成功!!
在这里插入图片描述
所以最终的主函数为:

int main(void)
{
    //创建单链表
    Listnode* head = (Listnode*)malloc(sizeof(Listnode));
    Listnode* end; 
    head->val = NULL;
    head->next = NULL;
    end = head;
    
    for(int i = 0;i < 7;i++)
    {
        Listnode* s= (Listnode*)malloc(sizeof(Listnode));
        s->val = i + 1;
        //尾插法
        end->next = s;
        end = s;
    }
    //不加该句会出错!!!!
    end->next  = NULL;
    PrintListReversingly_iteratively(head);
    return 0;
}

递归实现从尾到头打印链表

仅需改变逆转函数即可,递归就是通过缩小问题的规模不断调用自己的过程,好理解,代码也简单。

void PrintListReversingly_Recursively(ListNode* phead)
{
    if(phead == NULL)
        return;
    if(phead->next != NULL)
        PrintListReversingly_Recursively(phead->next);
    printf("%d ",phead->val);
}

在主函数中添加调用语句:

PrintListReversingly_Recursively(head);

输出结果为:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191215003602967.png
OK !!!
总结一下,今天完成了用C语言从尾到头打印链表,其中包括链表和栈的定义,栈中常用方法的定义,复现了我今天遇到了错误以及解决方法,收获满满~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值