复试算法练习Day17——从头到尾打印链表
题目描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
示例1
输入:
{1,2,3}
返回值:
[3,2,1]
示例2
输入:
{67,0,24,58}
返回值:
[58,24,0,67]
思路
思路一:虽然从头到尾输出比较简单,但是如果需要从尾到头打印链表,最好的方法就是直接把链表中的结点指针翻转过来,改变链表方向,就可以从头到尾输出,但是这样会改变链表数据结构。因此,可以考虑直接从头到尾输出数据后,把数据依次入栈,然后从栈中输出结果,这样就可以得到从尾到头的链表在数组中输出的结果了。
思路二:利用两个数组,首先建立链表然后把链表的数据输出到第一个数组,统计数组长度,然后利用两个相同的长度的数组,来互相首尾互换给出反转后的数据结果,输出即可得到从尾到头的数组。
具体实现
//利用栈FILO的原理,在不破坏链表结构的条件下,
//利用数组从尾到头逆序输出数据
#include <stdio.h>
#include <stdlib.h>
//链表初始化
typedef struct _list{
struct _list *next;
int data;
}List;
//数据栈初始化
typedef struct _stack{
int data[10];
int top;
}Stack;
//入栈函数
int InitStack(Stack *s){
if(s == NULL){
return 0;
}
s->top = -1;
return 1;
}
//链表插入函数
int Insert_list(List *list, int data){
//如果链表为空,返回0
if(list == NULL){
return 0;
}
//否则开辟空间存放链表指针和链表节点
List *node = (List *)malloc(sizeof(List)/sizeof(char));
//如果节点为空,则返回0
if(node == NULL){
return 0;
}
//利用尾插法插入节点,并插入数据
node->data = data;
node->next = list->next;
list->next = node;
//结束后返回1
return 1;
}
//利用栈打印链表元素
int Display(Stack *s, List *list) {
//在栈指针为空且链表节点为空,输出0
if(s == NULL || list == NULL){
return 0;
}
//将链表数据依次入栈
List *tmp = list->next;
while(tmp){
s->data[++s->top] = tmp->data;
tmp = tmp->next;
}
//当栈满后,依次输出栈底元素
while(s->top != -1){
printf("%4d",s->data[s->top--]);
}
printf("\n");
return 1;
}
//递归打印链表元素
void r_display(List *list){
//如果链表为空输出0
if(list->next == NULL){
return 0;
}
//否则多次递归输出节点内容
r_display(list->next);
printf("%4d",list->next->data);
}
int main(){
//栈空间初始化
Stack *s = (Stack *)malloc(sizeof(Stack)/sizeof(char));
if(s == NULL){
return 0;
}
//置空栈
InitStack(s);
//链表空间初始化
List *list = (List *)malloc(sizeof(List)/sizeof(char));
if(list == NULL){
return 0;
}
list->next = NULL;
//链表头插法输入元素,后输入
Insert_list(list,1);
Insert_list(list,2);
Insert_list(list,3);
Insert_list(list,4);
Insert_list(list,5);
Insert_list(list,6);
Insert_list(list,7);
r_display(list);
printf("\n");
//Display(s,list);
return 0;
}
//注意返回值必须初始化地址之后,才可以将链表节点释放
/**定义链表结构体
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//建立反向输出函数后递归调用
int* reversePrint(struct ListNode* head, int* returnSize){
//如果链表为空
if(NULL == head){
//返回值指针为0
* returnSize = 0;
return NULL;
}
//设置头结点指针
struct ListNode *p = head;
int count = 0;
int i = 0;
//数组空间初始化
int* temp = (int*)malloc(sizeof(int) * 10000);
int* res = (int*)malloc(sizeof(int) * 10000);
//依次出入链表中数据
while(NULL != p->next){
temp[count] = p->val;
count++;
p = p->next;
}
//数组内容计数
temp[count] = p->val;
count++;
*returnSize = count;
//将数组反转后调整内容对应输出
for(i = 0;i < count;i++){
res[i] = temp[count - 1 - i];
}
return res;
}
时间复杂度
对于方案一,采用栈的方法FILO输出,遍历一次即可,时间复杂度为O(n),设置栈空间与数组空间因此可以给出,本算法的空间复杂度为O(n)
对于方案二,采用建立两个数组的方法,输入一次链表内容时间复杂度为O(n),建立两个数组得到数组长度反转则其空间复杂度为O(n)
小结
总结:如果每次只考虑打印输出一个节点的值,那么这样的时间复杂度会为O(n^2),所以这种方法是不可取的。进而可以考虑采用栈这种数据类型就是先进后出,与题目中要求不谋而合,所以采用栈的方式去打印链表,利用递归方法,这样可以使你的代码变得很简洁,或者采用方法二,利用数组知道长度可以反转数组的特性,先顺序输出内容,后采用两个相同长度数组反转数组内容就可以得到从尾到头输出的链表中的数据,且数据保存在数组中,这种方法极大的考虑到了数组的用法,但是如果数组内容过大,其运算结果也会变慢,需要灵活处理。