题目:
输入一个链表,输出该链表中倒数第K个结点。为了符合大多数人的习惯,本题从1开始计数,即聊表的尾结点是倒数第一个结点。例如一个链表6个结点,从头结点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个结点是值为4的结点。
方法一:
分析:假设整个链表中有n个结点,那么倒数第K个结点就是从头结点开始的第n-k+1个结点。如果我们能得到链表中的结点个数n,那么只需要从前向后走n-k+1步就可以了!
源代码如下:
#include<iostream>
#include"List.h"
using std::endl;
using std::cout;
using std::cin;
using namespace ListSpace3;
int CalculateNoteNum(ListNode *pHead)
{
if (pHead == NULL)
return 0;
ListNode *temp = pHead;
int count = 0;
while (temp != NULL)
{
++count;
temp = temp->next;
}
return count;
}
void printNode(ListNode *pHead, int NodeNum)
{
if (pHead == NULL)
{
cout << "链表为空,不能够打印结点值!" << endl;
return;
}
cout << "请输入要打印的倒数第k个结点: ";
int k = 0;
cin >> k;
if (k > NodeNum || k < 1)
{
cout << "您输入的结点数超过了该链表所拥有的结点数或输入的数字小于1,不能打印该结点!" << endl;
return;
}
ListNode *temp = pHead;
for (int i = 0; i < NodeNum - k; i++)
{
temp = temp->next;
}
cout << "倒数第" << k << "个结点:";
printListNode(temp);
}
void test11()
{
cout << "===========测试1:有6个结点结点且输入任意个结点数===============" << endl;
ListNode *list1 = CreateListNode(1);
ListNode *list2 = CreateListNode(2);
ListNode *list3 = CreateListNode(3);
ListNode *list4 = CreateListNode(4);
ListNode *list5 = CreateListNode(5);
ListNode *list6 = CreateListNode(6);
ConnectListNodes(list1, list2);
ConnectListNodes(list2, list3);
ConnectListNodes(list3, list4);
ConnectListNodes(list4, list5);
ConnectListNodes(list5, list6);
ConnectListNodes(list6, NULL);
int NodeNum = CalculateNoteNum(list1);
cout << "该链表总共有" << NodeNum << "个结点" << endl;
for (int i = 0; i < 6; i++)
printNode(list1, NodeNum);
}
void test12()
{
cout << "===========测试1:链表为空且===============" << endl;
int NodeNum = CalculateNoteNum(NULL);
cout << "该链表总共有" << NodeNum << "个结点" << endl;
printNode(NULL, NodeNum);
}
int main1()
{
test11();
test12();
system("pause");
return 0;
}
运行结果:
===========测试1:有6个结点结点且输入任意个结点数===============
该链表总共有6个结点
请输入要打印的倒数第k个结点: 2
倒数第2个结点:The value of this node is 5
请输入要打印的倒数第k个结点: 4
倒数第4个结点:The value of this node is 3
请输入要打印的倒数第k个结点: 6
倒数第6个结点:The value of this node is 1
请输入要打印的倒数第k个结点: 8
您输入的结点数超过了该链表所拥有的结点数或输入的数字小于1,不能打印该结点!
请输入要打印的倒数第k个结点: 0
您输入的结点数超过了该链表所拥有的结点数或输入的数字小于1,不能打印该结点!
请输入要打印的倒数第k个结点: -2
您输入的结点数超过了该链表所拥有的结点数或输入的数字小于1,不能打印该结点!
===========测试1:链表为空且===============
该链表总共有0个结点
链表为空,不能够打印结点值!
请按任意键继续. . .
分析:方法一虽然也能够解决问题,但是它对链表遍历了两次,怎么样才能遍历依次就能输出结果呢?
方法二:
一次遍历输出结果。
分析:定义两个指针。第一个指针从头结点处向前移动k-1步,第二个指针在头结点处保持不动;从第k步开始,第一个结点向前走,直至第一个结点走到链表的尾部。
源代码如下:
#include<iostream>
#include"List.h"
using std::cout;
using std::endl;
using std::cin;
using namespace ListSpace3;
void PrintNode2(ListNode *pHead)
{
if (pHead == NULL)
{
cout << "链表为空不能打印" << endl;
return;
}
cout << "请输入要打印的倒数第k个结点: ";
int k = 0;
cin >> k;
if (k <= 0)
{
cout << "输入的将要打印的结点位置有误" << endl;
return;
}
ListNode *pAhead = pHead;
ListNode *pBehind = pHead;
for (int i = 0; i < k-1; i++)
{
if (pAhead->next != NULL)
pAhead = pAhead->next;
else
{
cout << "输入的结点数超出了链表的结点总数" << endl;
return;
}
}
while (pAhead->next != NULL)
{
pAhead = pAhead->next;
pBehind = pBehind->next;
}
printListNode(pBehind);
}
void test21()
{
cout << "===========测试1:有6个结点结点且输入任意个结点数===============" << endl;
ListNode *list1 = CreateListNode(1);
ListNode *list2 = CreateListNode(2);
ListNode *list3 = CreateListNode(3);
ListNode *list4 = CreateListNode(4);
ListNode *list5 = CreateListNode(5);
ListNode *list6 = CreateListNode(6);
ConnectListNodes(list1, list2);
ConnectListNodes(list2, list3);
ConnectListNodes(list3, list4);
ConnectListNodes(list4, list5);
ConnectListNodes(list5, list6);
ConnectListNodes(list6, NULL);
for (int i = 0; i < 5; i++)
PrintNode2(list1);
}
void test22()
{
cout << "===========测试1:链表为空且===============" << endl;
PrintNode2(NULL);
}
int main()
{
test21();
test22();
system("pause");
return 0;
}
===========测试1:有6个结点结点且输入任意个结点数===============
请输入要打印的倒数第k个结点: 6
The value of this node is 1
请输入要打印的倒数第k个结点: 1
The value of this node is 6
请输入要打印的倒数第k个结点: -1
输入的将要打印的结点位置有误
请输入要打印的倒数第k个结点: -6
输入的将要打印的结点位置有误
请输入要打印的倒数第k个结点: 0
输入的将要打印的结点位置有误
===========测试1:链表为空且===============
链表为空不能打印
请按任意键继续. . .
注意:
- 输入的整数不得超过链表的总结点数码;
- 当链表为空时的处理情况;
- 当输入的整数方位不对时的处理情况。
- 上述代码中都没有对链表进行销毁,或造成 内存泄漏
官方源代码:
#include<cstdlib>
#include<cstdio>
#include"List.h"
using namespace ListSpace3;
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
if (pListHead == NULL || k == 0)
return NULL;
ListNode *pAhead = pListHead;
ListNode *pBehind = NULL;
for (unsigned int i = 0; i < k - 1; ++i)
{
if (pAhead->next != NULL)
pAhead = pAhead->next;
else
{
return NULL;
}
}
pBehind = pListHead;
while (pAhead->next != NULL)
{
pAhead = pAhead->next;
pBehind = pBehind->next;
}
return pBehind;
}
// ====================测试代码====================
// 测试要找的结点在链表中间
void Test1()
{
printf("=====Test1 starts:=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
printf("expected result: 4.\n");
ListNode* pNode = FindKthToTail(pNode1, 2);
printListNode(pNode);
DestoryList(&pNode1);
}
// 测试要找的结点是链表的尾结点
void Test2()
{
printf("=====Test2 starts:=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
printf("expected result: 5.\n");
ListNode* pNode = FindKthToTail(pNode1, 1);
printListNode(pNode);
DestoryList(&pNode1);
}
// 测试要找的结点是链表的头结点
void Test3()
{
printf("=====Test3 starts:=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
printf("expected result: 1.\n");
ListNode* pNode = FindKthToTail(pNode1, 5);
printListNode(pNode);
DestoryList(&pNode1);
}
// 测试空链表
void Test4()
{
printf("=====Test4 starts:=====\n");
printf("expected result: NULL.\n");
ListNode* pNode = FindKthToTail(NULL, 100);
printListNode(pNode);
}
// 测试输入的第二个参数大于链表的结点总数
void Test5()
{
printf("=====Test5 starts:=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
printf("expected result: NULL.\n");
ListNode* pNode = FindKthToTail(pNode1, 6);
printListNode(pNode);
DestoryList(&pNode1);
}
// 测试输入的第二个参数为0
void Test6()
{
printf("=====Test6 starts:=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
printf("expected result: NULL.\n");
ListNode* pNode = FindKthToTail(pNode1, 0);
printListNode(pNode);
DestoryList(&pNode1);
}
int main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
system("pause");
return 0;
}
运行结果:
=====Test1 starts:=====
expected result: 4.
The value of this node is 4
=====Test2 starts:=====
expected result: 5.
The value of this node is 5
=====Test3 starts:=====
expected result: 1.
The value of this node is 1
=====Test4 starts:=====
expected result: NULL.
The value of this node is empty
=====Test5 starts:=====
expected result: NULL.
The value of this node is empty
=====Test6 starts:=====
expected result: NULL.
The value of this node is empty
请按任意键继续. . .
相关题目:
1 求链表的中间节点
题目:如果链表中结点总数为奇数,返回中间节点;如果结点总数是偶数,返回中间两个结点的任意一个结点。
分析:为了解决这个问题,我们可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。当走得快的指针走到链表的尾部时,走得慢的指针正好在链表的中间
2 判断一个单链表是否形成了环形结构
分析:和前面一个问题一样,定义两个指针,同时从链表的头结点出发,一个指针一次只走一步,另外一个指针一次走两步。如果走得快的指针能够追的上走得慢的指针,那么链表就是环形链表;如果走的快的指针走到链表的末尾都没有追上走得慢的指针,那么链表就不是环形链表。
总结:
当我们用一个指针遍历链表不能解决问题的时候,我们可以尝试用两个指针来遍历链表。可以让其中一个走的慢一些,另外一个指针走的快一些。