以下代码使用的公共结构:
链表节点:
struct ListNode
{
int data;
ListNode *next;
};
辅助函数:
//make a list
ListNode *makeList(int *arr, int n)
{
if(arr == NULL || n <= 0)
return NULL;
ListNode *phead = new ListNode;
phead->data = arr[0];
phead->next = NULL;
ListNode *ptr = phead;
for(int i = 1; i < n; i++)
{
ListNode *node = new ListNode;
node->data = arr[i];
node->next = NULL;
ptr->next = node;
ptr = node;
}
return phead;
}
//print a list
void printList(ListNode *phead)
{
ListNode *node = phead;
while(node != NULL)
{
cout << node->data << "\t";
node = node->next;
}
cout << endl;
}
一,从尾到头打印链表
题目:给定一个单链表的头节点,从尾到头打印出该链表的所有节点。
解答:
- 使用递归,在递归中,若节点有后续节点,则先处理后续节点,再打印本节点的数据
- 递归的本质就是使用栈,所以可以不是用递归,而是在遍历链表的时候将链表数据压入栈中,遍历结束之后出栈就可以得到从尾到头的所有节点值
代码:
/*******************************************************
**question 1
********************************************************/
//solution 1
void printListFromTail_1(ListNode *phead)
{
if(phead == NULL) return;
printListFromTail_1(phead->next);
cout << phead->data << endl;
}
//solution
void printListFromTail_2(ListNode *phead)
{
if(phead == NULL) return;
stack<int> myStack;
ListNode *node = phead;
do
{
myStack.push(node->data);
node = node->next;
}while(node != NULL);
while(myStack.size() > 0)
{
cout << myStack.top() << endl;
myStack.pop();
}
}
测试及其用例:
//test
/*********************************
arr[10] = {....}
arr[0] = {....}
arr[1] = {....}
arr = NULL
**********************************/
int main()
{
int arr[1] = {1};
ListNode *phead = makeList(arr, 1);
printListFromTail_1(phead);
printListFromTail_2(phead);
return 0;
}
二,链表中的倒数第 K个节点
题目:给出一个单链表的头节点,写一个函数获得其倒数第K个节点
解答:
最简单的做法是:先遍历链表,获得链表的长度n,然后再次遍历链表,从头节点开始的第n - K + 1即是所求节点。
优化以下的做法是:使用两个指针,一个先向前遍历K 个节点,另外一个指着头节点,然后两个指针一起向前遍历链表直到链表结尾。这时后面一个节点所指的就是倒数第K个节点
代码:
/*******************************************************
**question 2
********************************************************/
ListNode *getKthNodeFromTail(ListNode *phead, int k)
{
if(phead == NULL || k <= 0) return NULL;
ListNode *pBegin = phead;
ListNode *pEnd = phead;
for(int i = 0; i < k - 1; i++)
{
if(pBegin->next != NULL)
pBegin = pBegin->next;
else
return NULL;
}
while(pBegin->next != NULL)
{
pBegin = pBegin->next;
pEnd = pEnd->next;
}
return pEnd;
}
测试及其用例:
//test
/*********************************
arr[10] = {....} k = 10
arr[10] = {....} k = 1
arr[10] = {....} k = 5
**********************************/
int main()
{
int arr[1] = {1};
ListNode *phead = makeList(arr, 1);
getKthNodeFromTail(phead, 1);
return 0;
}
三,反转链表
题目:给定一个单链表的头节点指针,请反转该单链表,并返回反转之后的单链表的头节点指针
解答:使用两个指针,反转链表,这个比较简单。
代码:
/*******************************************************
**question 3
********************************************************/
ListNode *reverseList(ListNode *phead)
{
if(phead == NULL) return NULL;
if(phead->next == NULL) return phead;
ListNode *pBegin = phead->next;
ListNode *pEnd = phead;
pEnd->next = NULL;
do
{
ListNode *pTemp = pBegin->next;
pBegin->next = pEnd;
pEnd = pBegin;
pBegin = pTemp;
}while(pBegin != NULL);
return pEnd;
}
四,判断单链表是否有环
五,合并两个排序的链表
六,复杂链表的复制
七,两个链表的第一个公共节点
题目:给定两个单链表的头节点,写一个函数返回两个单链表的第一个公共节点
解答:
由于是两个单链表存在公共节点,那么说明是Y型结构,第一个公共节点之后的节点都是公共节点,所以解决这个问题的方法就是利用这些公共节点。
- 将两个链表从前到后遍历,将他们的节点分别压入两个栈中,然后出栈比较,直到节点不一样,说明上一个出栈的节点就是第一个公共子节点。时间复杂度是O(M + N),空间复杂度是O(M + N)
- 分别遍历两个节点,得到两个节点的长度为:M、N。让长的链表先往前走|M - N|步,然后两个链表一起往前遍历,直到两个链表遇到的节点一样,即为公共子节点
代码: