从尾到头打印单链表(剑指offer面试题5)

【题目】输入一个链表的头结点,从尾到头反过来打印出每个结点的值。


【分析】此题考察单链表的理解和应用,为了实现打印,必须先建立一个单链表,头结点可有可无,但有头结点和无头结点在编程上是应该注意的,反向打印思路不一样,这里以包含头结点举例分析,单链表以尾部插入方式建立,头结点数据域为空,头结点指针指向下一结点,反向打印的思想,有两种,一种以栈的形式,一种递归的形式。
1. 以栈的思想反向打印,主要是利用链表在访问时总是从头开始访问,那么在访问的同时,将结点数据压栈,边访问边压栈,栈的特点就是先进后出,所以压栈进去时就是链表的顺序读入,出栈时就是链表的反向输入。
2. 以递归形式反向打印,主要是利用链表访问的层层递归,每访问到一个结点,就递归访问输出后面的一个结点,然后再输出结点自身,最终就是反向输出。


【算法递归实现】

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

typedef int data_type;

typedef struct Node node_t;// 给struct Node取个别名node_t
typedef struct Node * node_ptr;//给struct Node*取个别名node_ptr

typedef struct Node
{
    data_type data;
    struct Node *node_next;
//node_next是一个指向结构的指针,告诉指针要指向的地址就要付给它一个
//结构类型地址
};



node_t * init()
{
    node_ptr p;
    p = (node_t *)malloc(sizeof(node_t));
    p->node_next = NULL;
    return p;
}
//在链表后面插入结点
node_t *insert_back(node_ptr p , data_type data)
{


    node_ptr pnew = (node_t *)malloc(sizeof(node_t));
    pnew ->node_next = NULL;
    pnew ->data = data;

    p->node_next = pnew;

    return pnew;
}
//反向打印
void  print_reverse(node_ptr p)
{
    if(p!=NULL)
    {
        if(p->node_next !=NULL)
        {
            print_reverse(p->node_next);
        }
        printf("%d\n",p->data);
    }

}

void main()
{
    node_ptr pnode, list;
    pnode = init();
    list = pnode;
    pnode = insert_back(pnode, 1);
    pnode = insert_back(pnode, 2);
    pnode = insert_back(pnode, 3);
    pnode = insert_back(pnode, 4);
    pnode = insert_back(pnode, 5);
    print_reverse(list->node_next);
}

【输出】
这里写图片描述


【说明】上例中所建单链表是利用两个单指针完成的,list指针用于指向链表头结点,pnode指向添加节点后的尾结点,访问时因为头结点数据域为内存随机数,按照反向打印判断是否为空时,应该移动到头节点的下一节点,如果不移动,直接从头节点开始判断,会发现,打印出来的多出了一个随机数,就是头结点的数据域,这是因为头结点地址不为空,最后递归回来时头结点就是第一个结点的前一节点,就会打印出来,可以实例操作一下,就会发现问题。


【注意】struct用法 ,typedef用法, 指针地址如何传递
【完整代码】

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

typedef int data_type;

typedef struct Node node_t;// 给struct Node取个别名node_t
typedef struct Node * node_ptr;//给struct Node*取个别名node_ptr

typedef struct Node
{
    data_type data;
    struct Node *node_next;//node_next是一个指向结构的指针,告诉指针要指向的地址就要付给它一个结构类型地址
};



node_t * init()
{
    node_ptr p;
    p = (node_t *)malloc(sizeof(node_t));
    p->node_next = NULL;
    return p;
}
//在链表后面插入结点
node_t *insert_back(node_ptr p , data_type data)
{


    node_ptr pnew = (node_t *)malloc(sizeof(node_t));
    pnew ->node_next = NULL;
    pnew ->data = data;

    p->node_next = pnew;

    return pnew;
}

//正常打印
void print(node_ptr p)
{
    if(!p)
        printf("no data, you think too much");

    while(p->node_next != NULL)
    {
        printf("%d ", p->data);
        p = p->node_next;
    }
    printf("%d ", p->data);
    printf("\n");
}
//利用递归反向输出
void  print_reverse(node_ptr p)
{
    if(p!=NULL)
    {
        if(p->node_next !=NULL)
        {
            print_reverse(p->node_next);
        }
        printf("%d ",p->data);
    }

}
//利用栈反向输出
void push_print(node_ptr p)
{
    std :: stack<node_t *> nodes;

    node_t *pnew = p;
    while(pnew!=NULL)
    {
        nodes.push(pnew);
        pnew = pnew->node_next;
    }
    while(!nodes.empty())
    {
        pnew = nodes.top(); //让pnew一直指向栈顶
        printf("%d ", pnew->data);
        nodes.pop(); //出栈
    }
        printf("\n");
        free(pnew);
}
void main()
{
    node_ptr pnode, list;
    pnode = init();
    list = pnode;

    pnode = insert_back(pnode, 1);
    pnode = insert_back(pnode, 2);
    pnode = insert_back(pnode, 3);
    pnode = insert_back(pnode, 4);
    pnode = insert_back(pnode, 5);
    pnode = insert_back(pnode, 6);

    printf("normal print:   ");
    print(list->node_next);

    printf("reverse print  recursively: ");
    print_reverse(list->node_next);
    printf("\n");

    printf("reverse print with stack:   ");
    push_print(list->node_next);
}

【输出】
这里写图片描述
链表顺序输出,递归反向打印,栈反向打印

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值