程序员面试题精选100题(19)-反转链表
字号:小|大
2019-09-22 FW.5VV.CN范文网
程序员面试题精选100题(19)-反转链表
题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道广为流传的微软面试题。由于这道题能够很好的反应出程序员思维是否严密,在微软之后已经有很多公司在面试时采用了这道题。
为了正确地反转一个链表,需要调整指针的指向。与指针操作相关代码总是容易出错的,因此最好在动手写程序之前作全面的分析。在面试的时候不急于动手而是一开始做仔细的分析和设计,将会给面试官留下很好的印象,因为在实际的软件开发中,设计的时间总是比写代码的时间长。与其很快地写出一段漏洞百出的代码,远不如用较多的时间写出一段健壮的代码。
为了将调整指针这个复杂的过程分析清楚,我们可以借助图形来直观地分析。假设下图中l、m和n是三个相邻的结点:
a?b?…?l mànà…
假设经过若干操作,我们已经把结点l之前的指针调整完毕,这些结点的m_pNext指针都指向前面一个结点。现在我们遍历到结点m。当然,我们需要把调整结点的m_pNext指针让它指向结点l。但注意一旦调整了指针的指向,链表就断开了,如下图所示:
a?b?…l?m nà…
因为已经没有指针指向结点n,我们没有办法再遍历到结点n了。因此为了避免链表断开,我们需要在调整m的m_pNext之前要把n保存下来。
接下来我们试着找到反转后链表的头结点。不难分析出反转后链表的头结点是原始链表的尾位结点。什么结点是尾结点?就是m_pNext为空指针的结点。
基于上述分析,我们不难写出如下代码:
///////////////////////////////////////////////////////////////////////
// Reverse a list iteratively
// Input: pHead - the head of the original list
// Output: the head of the reversed head
///////////////////////////////////////////////////////////////////////
ListNode* ReverseIteratively(ListNode* pHead)
{
ListNode* pReversedHead = NULL;
ListNode* pNode = pHead;
ListNode* pPrev = NULL;
while(pNode != NULL)
{
// get the next node, and save it at pNext
ListNode* pNext = pNode->m_pNext;
// if the next node is null, the currect is the end of original
// list, and it's the head of the reversed list
if(pNext == NULL)
pReversedHead = pNode;
// reverse the linkage between nodes
pNode->m_pNext = pPrev;
// move forward on the the list
pPrev = pNode;
pNode = pNext;
}
return pReversedHead;
}
另一种方法:以递归来实现反转链表
#include <iostream>
using namespace std;
struct ListNode
{
int data;
struct ListNode *next;
};
//创建链表
void createList(ListNode *&Head)
{
int num = 0;
int flag = 0;
ListNode *p = NULL;
cin >> num;
Head = (ListNode *)malloc(sizeof(ListNode));
while(num != 0)
{
if(flag == 0)
{
Head->data = num;
Head->next = NULL;
flag = 1;
p = Head;
}
else
{
ListNode *cur = (ListNode *)malloc(sizeof(ListNode));
cur->data = num;
cur->next = NULL;
p->next = cur;
p = cur;
}
cin >> num;
}
}
//打印链表
void printList(ListNode *Head)
{
if(Head == NULL)
{
cout << "Head empty"<<endl;
return ;
}
ListNode *p = Head;
while(p != NULL)
{
cout << p->data <<" ";
p = p->next;
}
}
//递归反转链表(关键代码)
ListNode* reverseLinkList_recursion(ListNode* pNode,ListNode*& head)
{
if(pNode == NULL || pNode->next == NULL)
{
head->next = NULL; //把反转后的最后一个节点的next域置为NULL
head = pNode;
return pNode;
}
ListNode* tmpNode = reverseLinkList_recursion(pNode->next,head);//返回原链表中pNode的下一个节点
tmpNode->next = pNode;
return pNode;
}
void main()
{
ListNode *List = NULL;
cout << "创建链表:";
createList(List);
cout << "链表元素:";
printList(List);
reverseLinkList_recursion(List,List);
cout <<endl<< "倒序后:";
printList(List);
}
————————————————
题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道广为流传的微软面试题。由于这道题能够很好的反应出程序员思维是否严密,在微软之后已经有很多公司在面试时采用了这道题。
为了正确地反转一个链表,需要调整指针的指向。与指针操作相关代码总是容易出错的,因此最好在动手写程序之前作全面的分析。在面试的时候不急于动手而是一开始做仔细的分析和设计,将会给面试官留下很好的印象,因为在实际的软件开发中,设计的时间总是比写代码的时间长。与其很快地写出一段漏洞百出的代码,远不如用较多的时间写出一段健壮的代码。
为了将调整指针这个复杂的过程分析清楚,我们可以借助图形来直观地分析。假设下图中l、m和n是三个相邻的结点:
a?b?…?l mànà…
假设经过若干操作,我们已经把结点l之前的指针调整完毕,这些结点的m_pNext指针都指向前面一个结点。现在我们遍历到结点m。当然,我们需要把调整结点的m_pNext指针让它指向结点l。但注意一旦调整了指针的指向,链表就断开了,如下图所示:
a?b?…l?m nà…
因为已经没有指针指向结点n,我们没有办法再遍历到结点n了。因此为了避免链表断开,我们需要在调整m的m_pNext之前要把n保存下来。
接下来我们试着找到反转后链表的头结点。不难分析出反转后链表的头结点是原始链表的尾位结点。什么结点是尾结点?就是m_pNext为空指针的结点。
基于上述分析,我们不难写出如下代码:
///////////////////////////////////////////////////////////////////////
// Reverse a list iteratively
// Input: pHead - the head of the original list
// Output: the head of the reversed head
///////////////////////////////////////////////////////////////////////
ListNode* ReverseIteratively(ListNode* pHead)
{
ListNode* pReversedHead = NULL;
ListNode* pNode = pHead;
ListNode* pPrev = NULL;
while(pNode != NULL)
{
// get the next node, and save it at pNext
ListNode* pNext = pNode->m_pNext;
// if the next node is null, the currect is the end of original
// list, and it's the head of the reversed list
if(pNext == NULL)
pReversedHead = pNode;
// reverse the linkage between nodes
pNode->m_pNext = pPrev;
// move forward on the the list
pPrev = pNode;
pNode = pNext;
}
return pReversedHead;
}
另一种方法:以递归来实现反转链表
#include <iostream>
using namespace std;
struct ListNode
{
int data;
struct ListNode *next;
};
//创建链表
void createList(ListNode *&Head)
{
int num = 0;
int flag = 0;
ListNode *p = NULL;
cin >> num;
Head = (ListNode *)malloc(sizeof(ListNode));
while(num != 0)
{
if(flag == 0)
{
Head->data = num;
Head->next = NULL;
flag = 1;
p = Head;
}
else
{
ListNode *cur = (ListNode *)malloc(sizeof(ListNode));
cur->data = num;
cur->next = NULL;
p->next = cur;
p = cur;
}
cin >> num;
}
}
//打印链表
void printList(ListNode *Head)
{
if(Head == NULL)
{
cout << "Head empty"<<endl;
return ;
}
ListNode *p = Head;
while(p != NULL)
{
cout << p->data <<" ";
p = p->next;
}
}
//递归反转链表(关键代码)
ListNode* reverseLinkList_recursion(ListNode* pNode,ListNode*& head)
{
if(pNode == NULL || pNode->next == NULL)
{
head->next = NULL; //把反转后的最后一个节点的next域置为NULL
head = pNode;
return pNode;
}
ListNode* tmpNode = reverseLinkList_recursion(pNode->next,head);//返回原链表中pNode的下一个节点
tmpNode->next = pNode;
return pNode;
}
void main()
{
ListNode *List = NULL;
cout << "创建链表:";
createList(List);
cout << "链表元素:";
printList(List);
reverseLinkList_recursion(List,List);
cout <<endl<< "倒序后:";
printList(List);
}
————————————————
相关文章
- 程序员面试题精选100题(03)-第一个只出现一次的字符[算法]
- 程序员面试题精选100题(21)-左旋转字符串[算法]
- 程序员面试题精选100题(24)-栈的push、pop序列[数据结构]
- 程序员面试题精选100题(26)-和为n连续正数序列[算法]
- 程序员面试题精选100题(32)-不能被继承的类[C/C++/C#]
- 程序员面试题精选100题(46)-对称子字符串的最大长度[算法]
- 程序员面试题精选100题(04)-把字符串转换成整数
- 程序员面试题精选100题(10)-排序数组中和为给定值的两个数字[算法]
- 程序员面试题精选100题(16)-O(logn)求Fibonacci数列[算法]
- 程序员面试题精选100题(17)-把字符串转换成整数[算法]
热门推荐
- 程序员面试题精选100题(03)-第一个只出现一次的字符[算法]
- 程序员面试题精选100题(21)-左旋转字符串[算法]
- 程序员面试题精选100题(24)-栈的push、pop序列[数据结构]
- 程序员面试题精选100题(26)-和为n连续正数序列[算法]
- 程序员面试题精选100题(32)-不能被继承的类[C/C++/C#]
- 程序员面试题精选100题(46)-对称子字符串的最大长度[算法]
- 程序员面试题精选100题(04)-把字符串转换成整数
- 程序员面试题精选100题(10)-排序数组中和为给定值的两个数字[算法]
- 程序员面试题精选100题(16)-O(logn)求Fibonacci数列[算法]
- 程序员面试题精选100题(17)-把字符串转换成整数[算法]