剑指offer面试题15

面试题15:链表中倒数第k个结点

题目:输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第一个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个结点是值为4的结点。

预备知识:

链表结点的定义如下:

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

思路:定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1,第二个指针保存不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。因为两个指针的距离保存在k-1,因此当第一个指针达到链表的尾部时,第二个指针正好是倒数第k个结点。

算法实现:

/*******链表结点定义****/
struct ListNode
{
	int		  m_nValue;
	ListNode* m_pNext;
};

/***************链表操作****************/
//创建链表结点
 ListNode* CreateListNode(int value);
//链接链表结点
 void ConnectListNodes(ListNode* pCurrent, ListNode* pNext);
//打印链表结点
void PrintListNode(ListNode* pNode);
//遍历链表的所有结点
void PrintList(ListNode* pHead);
//销毁链表
void DestroyList(ListNode* pHead);
//在链表尾部添加结点
void AddToTail(ListNode** pHead, int value);
//移除结点
 void RemoveNode(ListNode** pHead, int value);


#include "stdafx.h"
#include "List.h"
#include 
#include 

//创建链表结点
ListNode* CreateListNode(int value)
{
	ListNode* pNode = new ListNode(); //头指针
	pNode->m_nValue = value;
	pNode->m_pNext = NULL;

	return pNode;
}
//链接链表结点
void ConnectListNodes(ListNode* pCurrent, ListNode* pNext)
{
	if(pCurrent == NULL)
	{
		printf("Error to connect two nodes.\n");
		exit(1);
	}

	pCurrent->m_pNext = pNext;
}
//打印链表结点
void PrintListNode(ListNode* pNode)
{
	if(pNode == NULL)
	{
		printf("The node is NULL\n");
	}
	else
	{
		printf("The key in node is %d.\n", pNode->m_nValue);
	}

}
//遍历链表的所有结点
void PrintList(ListNode* pHead)
{
	printf("PrintList starts.\n");

	ListNode* pNode = pHead;
	while(pNode != NULL)
	{
		printf("%d\t", pNode->m_nValue);
		pNode = pNode->m_pNext;
	}

	printf("\nPrintList ends.\n");
}

//销毁链表
void DestroyList(ListNode* pHead)
{
	ListNode* pNode = pHead;
	while(pNode !=NULL)
	{
		pHead = pHead->m_pNext;
		delete pNode;
		pNode = pHead;
	}
}
//在链表尾部添加结点
void AddToTail(ListNode** pHead, int value)
{
	ListNode* pNew = new ListNode();
	pNew->m_nValue = value;
	pNew->m_pNext = NULL;

	if(*pHead == NULL)
	{
		*pHead = pNew;
	}
	else
	{
		ListNode* pNode = *pHead;
		while(pNode->m_pNext != NULL)
			pNode = pNode->m_pNext;

		pNode->m_pNext = pNew;
	}
}
//移除结点
void RemoveNode(ListNode** pHead, int value)
{
	if(pHead == NULL || *pHead == NULL)
		return;

	ListNode* pToBeDeleted = NULL;
	if((*pHead)->m_nValue == value)
	{
		pToBeDeleted = *pHead;
		*pHead = (*pHead)->m_pNext;
	}
	else
	{
		ListNode* pNode = *pHead;
		while(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue != value)
			pNode = pNode->m_pNext;

		if(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue == value)
		{
			pToBeDeleted = pNode->m_pNext;
			pNode->m_pNext = pNode->m_pNext->m_pNext;
		}
	}
}


main.cpp

// 面试题15.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "..\List.h"

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->m_pNext != NULL)
			pAhead = pAhead->m_pNext;
		else
		{
			return NULL;  //链表结点小于k
		}
	}

	pBehind = pListHead;  //第二个指针

	while(pAhead->m_pNext != NULL)
	{
		pAhead = pAhead->m_pNext;
		pBehind = pBehind->m_pNext;
	}

	return pBehind;

}

// ************测试代码******

void Test1()
{
	printf("+++++++++++++\n");
	//创建5个结点
	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);

	DestroyList(pNode1);
}

int _tmain(int argc, _TCHAR* argv[])
{
	Test1();
	return 0;
}

注意事项:

1. 输入的链表头指针可能为NULL

2. 输入的k可能为0

3 输入的链表结点个数小于k


你可能感兴趣的:(剑指offer)