C/C++ 《剑指offer》——(3)从尾到头打印链表

问题描述:

输入一个链表,按链表值从尾到头的顺序打印结点值。

解题思路及知识点

由题意选择三种思路:

  • 采用循环方法从尾到头打印单链表
  • 采用递归方法从尾到头打印单链表
  • 借助栈的特性从尾到头打印单链表

链接方式存储的线性表简称为链表(Linked List),data域--存放结点值的数据域,next域--存放结点的直接后继的地址(位置)的指针域(链域),链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的,每个结点只有一个链域的链表称为单链表(Single Linked List)。

typedef struct node{ //结点类型定义

DataType data; //结点的数据域

struct node *next;//结点的指针域

}ListNode,*LinkList;
  • LinkList和ListNode是不同名字的同一个指针类型(命名的不同是为了概念上更明确);
  • *LinkList类型的指针变量head表示它是单链表的头指针;
  • ListNode类型的指针变量p表示它是指向某一结点的指针;
  • 生成结点变量的标准函数

p=( ListNode *)malloc(sizeof(ListNode));//函数malloc分配一个类型为ListNode的结点变量的空间,将其首地址放入指针变量p中

单链表的建立有头插法、尾插法两种方法。

头插法

从一个空表开始,每次读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直至读入结束标志为止。头插法建表过程如下图所示:

尾插法

头插法建立链表虽然算法简单,但生成的链表中结点的次序和输人的顺序相反。若希望二者顺序一致, 可采用尾插法建表。 该方法是将新结点插到当前单链表的表尾上。为此需增加一个尾指针r,使之指向当前单链表的表尾。在这里采用尾插法建立单链表,尾插法建表过程如下图所示:

 栈(stack)又名堆栈,它是一种运算受限的线性表,特点“后进先出”。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

  • 进栈(PUSH)算法

①若TOP≥n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);

②置TOP=TOP+1(栈指针加1,指向进栈地址);

③S(TOP)=X,结束(X为新进栈的元素);

  • 退栈(POP)算法

①若TOP≤0,则给出下溢信息,作出错处理(退栈前先检查是否已为空栈, 空则下溢;不空则作②);

②X=S(TOP),(退栈后的元素赋给X):

③TOP=TOP-1,结束(栈指针减1,指向栈顶)。

思路一:采用循环方法从尾到头打印单链表

该方法为定义一个自己需要找到的指针,最开始为NULL;然后每当找到一个位置时该指针前移,直至找到头指针。

#include<stdio.h>
#include<stdlib.h>
typedef struct node            //结点类型定义
{
	int data;
	struct node* next;
}node,*list;                 //list为结构指针类型
void csh(list *l)				//初始化单链表
{
	*l = (list)malloc(sizeof(node));     //建立头结点
	(*l) ->next = NULL;					//建立空的单链表
}
void wcjb(list l,int i)          //尾插法建立单链表
{
	node *r, *s;              
	s =(node*)malloc(sizeof(node));         //建立新节点
	if (s == NULL) //验证空间申请是否成功
	{
		printf("内存分配失败\n");
		return;      //若分配内存不成功,则返回继续分配。
	}
	r = l;
	while (r->next)    //找到最后一个节点
	{
		r = r->next;
	}
	s->data = i;         
	r->next = s;            //将s结点插入表尾
	s->next = NULL;
}
void xhfxdy(list l)
{
	node *r, *h;
	r = NULL;
	if (l == NULL)          //判断输入链表是否为空
	{
		printf("\n\t原链表为空请重新输入\n\n");
		return;
	}
	printf("采用循环方法从尾到头打印单链表:\t");
	while(r !=l)          //在从尾到头前执行循环,直到到头结点
	{
		h = l;
		while (h->next!= r)      //当未找到自己需要的结点时执行循环
		{
			h = h->next;
		}
		if (h == l)
		{
			printf("\n");
			return;
		}
		printf("%d\t", h->data);     
		r = h;                //每次指定自己需要找得节点位置
	}
	
}
void dylb(list l)      //顺序打印输入的链表,为验证链表建立的正确性,可自行删除
{
	node *r;
	r = l ->next;
	printf("顺序打印通过尾插法建立的单链表:\t\t");
	while (r)                     //执行循环输出每个节点值
	{
		printf("%d\t", r->data);
		r = r->next;
	}
	printf("\n\n");
}
int main()
{
	node *l;               //定义及初始化链表
	csh(&l);
	wcjb(l, 1);                 //尾插法建表
	wcjb(l, 3);
	wcjb(l, 2);
	wcjb(l, 4);
	wcjb(l, 8);
	wcjb(l,6);
	wcjb(l, 5);
	dylb(l);
	xhfxdy(l);             //采用循环方法从尾到头打印单链表
	xhfxdy(NULL);      //验证链表为空
	return 0;
}

 思路二:采用递归方法从尾到头打印单链表

该方法为通过递归找到表尾,然后依次向前输出,直至头指针。

#include<stdio.h>
#include<stdlib.h>
typedef struct node            //结点类型定义
{
	int data;
	struct node* next;
}node,*list;                 //list为结构指针类型
void csh(list *l)				//初始化单链表
{
	*l = (list)malloc(sizeof(node));     //建立头结点
	(*l) ->next = NULL;					//建立空的单链表
}
void wcjb(list l,int i)          //尾插法建立单链表
{
	node *r, *s;              
	s =(node*)malloc(sizeof(node));         //建立新节点
	if (s == NULL) //验证空间申请是否成功
	{
		printf("内存分配失败\n");
		return;      //若分配内存不成功,则返回继续分配。
	}
	r = l;
	while (r->next)    //找到最后一个节点
	{
		r = r->next;
	}
	s->data = i;         
	r->next = s;            //将s结点插入表尾
	s->next = NULL;
}
void dgfxdy(list l,list l1)
{
	if (l== NULL)         //判断输入链表是否为空
	{ 
		printf("\n\t原链表为空请重新输入\n\n");
		return;
	}
	if (l->next != NULL)   //找到最后一个节点
	{
		dgfxdy(l->next,l1);
		printf("%d\t", l->next->data);   //通过递归实现从尾到头输出
	}
	if (l->next == NULL)
	{
		printf("采用递归方法从尾到头打印单链表:\t");
	}
	if(l==l1)                  //为了最后测试结果清晰,可选择删除
		printf("\n");
}
void dylb(list l)      //顺序打印输入的链表,为验证链表建立的正确性,可自行删除
{
	node *r;
	r = l ->next;
	printf("顺序打印通过尾插法建立的单链表:\t\t");
	while (r)                     //执行循环输出每个节点值
	{
		printf("%d\t", r->data);
		r = r->next;
	}
	printf("\n\n");
}
int main()
{
	node *l;               //定义及初始化链表
	csh(&l);
	wcjb(l, 1);                 //尾插法建表
	wcjb(l, 3);
	wcjb(l, 2);
	wcjb(l, 4);
	wcjb(l, 8);
	wcjb(l,6);
	wcjb(l, 5);
	dylb(l);
	dgfxdy(l, l);        //采用递归方法从尾到头打印单链表
	dgfxdy(NULL, NULL);   //验证链表为空
	return 0;
}

 思路三:借助栈的特性从尾到头打印单链表

该方法为借助栈的特性“后进先出”,将链表数据域从头到尾依次压入栈中,然后再进行出栈。

#include<iostream>
#include<cstdlib>
#include<stack>
using namespace std;
typedef struct node            //结点类型定义
{
	int data;
	struct node* next;
}node, *list;                 //list为结构指针类型
void csh(list *l)				//初始化单链表
{
	*l = (list)malloc(sizeof(node));     //建立头结点
	(*l)->next = NULL;					//建立空的单链表
}
void wcjb(list l, int i)          //尾插法建立单链表
{
	node *r, *s;
	s = (node*)malloc(sizeof(node));         //建立新节点
	if (s == NULL) //验证空间申请是否成功
	{
		printf("内存分配失败\n");
		return;      //若分配内存不成功,则返回继续分配。
	}
	r = l;
	while (r->next)    //找到最后一个节点
	{
		r = r->next;
	}
	s->data = i;
	r->next = s;            //将s结点插入表尾
	s->next = NULL;
}
void dylb(list l)      //顺序打印输入的链表,为验证链表建立的正确性,可自行删除
{
	node *r;
	r = l->next;
    cout<<"顺序打印通过尾插法建立的单链表:\t\t"
	while (r)                     //执行循环输出每个节点值
	{
		printf("%d\t", r->data);
		r = r->next;
	}
	printf("\n\n");
}
void jzfxdy(list l)
{
	if (l == NULL)          //判断输入链表是否为空
	{
		cout<< "\n\t原链表为空请重新输入\n"<<endl;
		return;
	}
	node* p ;
	p = l;
	stack<int>a;                  //定义一个容器a
	while (p)                     //将链表从头到尾依次压入栈
	{
		a.push(p->data);
		p = p->next;
	}
	cout << "借助栈的特性从尾到头打印单链表:\t";
	int len = a.size();           //栈元素个数
	for (int i = 1; i < len; i++)    //出栈即从尾到头打印原链表
	{
		cout << a.top()<<"\t";
		a.pop();
	}
	cout<<"\n\n";
}
int main()
{
	node *l;               //定义及初始化链表
	csh(&l);
	wcjb(l, 1);                 //尾插法建表
	wcjb(l, 3);
	wcjb(l, 2);
	wcjb(l, 4);
	wcjb(l, 8);
	wcjb(l, 6);
	wcjb(l, 5);
	dylb(l);
	jzfxdy(l);        //采用递归方法从尾到头打印单链表
	jzfxdy(NULL);   //验证链表为空
	return 0;
}

牛客网在线编程剑指offer测试通过

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
	vector<int> printListFromTailToHead(ListNode* head) {
		vector <int>  result;
		stack<int> arr;
		ListNode* p = head;
		while (p)
		{
			arr.push(p->val);
			p = p->next;
		}
		int len = arr.size();
		for (int i = 0; i < len; i++)
		{
			result.push_back(arr.top());
			arr.pop();
		}
		return  result;
	}
};

注意问题:

  • 在查找时要注意链表为NULL的情况;
  • 建立新结点需调用函数malloc, 头文件要加 #include<stdlib.h>;
  • 此处建立的是带头结点的单链表,输出结果时要去除头结点;
  • 使用stack<int> a函数需要添加头文件  #include<stack>。

 

 

 

      请多多指教!如发现有错误,请及时告知。谢谢!

      有疑问可评论或者私信,在能力范围内有问必答!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值