您的位置:首页 > 职场人生

面试题5:从尾到头打印链表

2018-01-25 20:39 309 查看
题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值。

链表结点定义如下:

struct ListNode
{
int        m_nKey;
ListNode*  m_pNext;
};


看到这道题后,很多人的第一反应是从头到尾输出将会比较简单,于是我们很自然的想到把链表中链接结点的指针反转过来,改变链表的方向,然后就可以从头到尾输出了。但该方法会改变原来链表的结构。是否允许在打印链表的时候修改链表的结构?这个取决于面试官的需求,因此在面试的时候我们要询问清楚面试官的要求。

通常打印是一个只读操作,我们不希望打印时修改内容。假设面试官也要求这个题目不能改变链表的结构。

接下来我们想到解决这个问题肯定要遍历链表。遍历的顺序是从头到尾的顺序,可输出的顺序却是从尾到头,也就是说第一个遍历到的结点最后一个输出,而最后一个遍历到的结点第一个输出。这就是典型的“后进先出”,我们可以用栈实现这种顺序。每经过一个结点的时候,把该结点放到一个栈中,当遍历完整个链表后,再从栈顶开始逐个输出结点的值,此时输出的结点的顺序已经反转过来了。这种思路的实现代码如下:

void PrintListReversingly_Iteratively(ListNode* pHead)
{
std::stack<ListNode*> nodes;

ListNode* pNode = pHead;
while(pNode != NULL)
{
nodes.push(pNode);
pNode = pNode->m_pNext;
}

while(!nodes.empty())
{
pNode = nodes.top();
printf("%d\t", pNode->m_nValue);
nodes.pop();
}
}


既然想到了用栈来实现这个函数,而递归在本质上就是一个栈结构,于是很自然的又想到了用递归来实现。要实现反过来输出链表,我们每访问到一个结点的时候,先递归输出它后面的结点,再输出该结点自身,这样链表的输出结果就反过来了。

基于这样的思路,不难写出如下代码:

void PrintListReversingly_Recursively(ListNode* pHead)
{
if(pHead != NULL)
{
if(pHead->m_pNext != NULL)
{
PrintListReversingly_Recursively(pHead->m_pNext);
}
printf(“%d\t”, pHead->m_nValue);
}
}


上面的基于递归的代码看起来很简洁,但有个问题:当链表非常长的时候,就会导致函数调用的层级很深,从而有可能导致函数调用栈溢出。显示用栈基于循环实现的代码的鲁棒性要好一些。

附上自己的代码:

(1) 头结点中的数据域不存放数据的

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

struct LinkNode
{
int data;
struct LinkNode *next;
};

typedef struct LinkNode List;
typedef List *LinkList;

void Insert(LinkList &Head)
{

for (int i = 0; i < 10; i++)
{
LinkList tmp = (LinkList)malloc(sizeof(List));
if (tmp == NULL)
printf("分配地址失败\n");
tmp->next = NULL;

if (Head->next == NULL)
{
tmp->data = i;
Head->next = tmp;
}
else
{
tmp->data = i;
tmp->next = Head->next;
Head->next = tmp;
}
}
}

// 由于 这种情况下 头结点 不存放数据;因此,打印时 不该打印头结点 的值
// 因此,先将 Head 指向 下一个结点 ,再调用 递归
void ReversePrint(LinkList Head)
{
if (Head != NULL && Head->next != NULL)
{
Head = Head->next;
ReversePrint(Head);
printf("%d\n", Head->data);
}
}

void print(LinkList Head)
{
LinkList tmp = Head->next;
while (tmp != NULL)
{
printf("%d\n", tmp->data);
tmp = tmp->next;
}
}

// 这个释放内存的函数 如有争议,可以直接留言
void myfree(LinkList &Head)
{
while (Head != NULL)
{
LinkList tmp = Head;
Head = Head->next;
free(tmp);
tmp = NULL;
}
}

int main()
{
LinkList Head = (LinkList)malloc(sizeof(List));
if (Head == NULL)
printf("分配地址失败\n");
Head->next = NULL;

Insert(Head);

print(Head);

printf("逆序打印\n");

ReversePrint(Head);
myfree(Head);

return 0;
}


(2)头结点中的数据域存放数据的

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

struct LinkNode
{
int data;
struct LinkNode *next;
};

typedef struct LinkNode List;
typedef List *LinkList;

void Insert(LinkList &Head)
{

for (int i = 1; i < 10; i++)
{
LinkList tmp = (LinkList)malloc(sizeof(List));
if (tmp == NULL)
printf("分配地址失败\n");
tmp->next = NULL;

if (Head->next == NULL)
{
tmp->data = i;
Head->next = tmp;
}
else
{
tmp->data = i;
tmp->next = Head->next;
Head->next = tmp;
}
}
}

void ReversePrint(LinkList Head)
{
if (Head != NULL)
{
if (Head->next != NULL)
{
ReversePrint(Head->next);
}
printf("%d\n", Head->data);
}
}

void print(LinkList Head)
{
LinkList tmp = Head;
while (tmp != NULL)
{
printf("%d\n", tmp->data);
tmp = tmp->next;
}
}

void myfree(LinkList &Head)
{
while (Head != NULL)
{
LinkList tmp = Head;
Head = Head->next;
free(tmp);
tmp = NULL;
}
}

int main()
{
LinkList Head = (LinkList)malloc(sizeof(List));
if (Head == NULL)
printf("分配地址失败\n");
Head->data = 0;
Head->next = NULL;

Insert(Head);

print(Head);

ReversePrint(Head);

myfree(Head);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: