剑指offer_面试题15_链表中倒数第k个节点(考虑问题要全面)
2015-08-09 20:34
627 查看
题目:输入一个链表,输出该链表中第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第一个节点。例如一个链表有6个节点,从头结点开始它们的值依次是1,2,3,4,5,6。这个链表的倒数第3个节点是值为4的结点。
本题收获:考虑问题要全面,保持代码的鲁棒性(健壮性)。
提高代码的鲁棒性的有效途径是进行防御性编程。
防御性编程 是 一种编程习惯,是指预见在什么地方可能会出现问题,并为这些可能出现的问题制定处理方式。
本题需要考虑的注意事项:
1、考虑传入的头指针为NULL;
2、考虑链表结点总数 少于 k;
3、考虑输入的 k 值小于等于0;
这三点一定要考虑,防止潜在的崩溃风险。
算法如下:
相关题目,可做思考:
1、求链表的中间结点。如果链表中结点总数为奇数,返回中间结点;若结点总数是偶数,返回中间结点的任意一个。
为了解决这个问题,我们可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。当走的快的指针走到链表的末尾时,走的慢的指针正好在链表的中间。
2、判断一个单向链表是否形成了环形结构。
和前面的问题一样,定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形链表;如果走得快得指针走到了链表的末尾(即pNext->NULL)都没有追上第一个指针,那么链表就不是环形链表。
通过这两个相关题目的解决思路,可以看出当我们用一个指针遍历链表不能解决问题时,可以尝试用两个指针来遍历链表,一个走得快些,另一个慢些。
因此对于本题,寻找倒数第k个结点,也可以这样做:
定义两个指针,第一个指针从链表的头指针开始遍历向前走 k-1,第二个指针保持不动,从第 k 步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在 k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第 k 个结点。
/*点滴积累,我的一小步O(∩_∩)O~*/
本题收获:考虑问题要全面,保持代码的鲁棒性(健壮性)。
提高代码的鲁棒性的有效途径是进行防御性编程。
防御性编程 是 一种编程习惯,是指预见在什么地方可能会出现问题,并为这些可能出现的问题制定处理方式。
本题需要考虑的注意事项:
1、考虑传入的头指针为NULL;
2、考虑链表结点总数 少于 k;
3、考虑输入的 k 值小于等于0;
这三点一定要考虑,防止潜在的崩溃风险。
算法如下:
#include <iostream> #include <cstdlib> using namespace std; typedef struct Node{ int m_nValue; struct Node *m_pNext; }ListNode; /*创建空链表*/ ListNode * creat_List() { ListNode *pHead; pHead = new ListNode; pHead->m_nValue = 0; /**头结点中保存数据节点总数*/ pHead->m_pNext = NULL; return pHead; } /*在链表尾部插入节点*/ ListNode * insert_Node_behind(ListNode *pHead,int pElem) { if(NULL == pHead) { cout << "头结点为空" << endl; exit(1); } ListNode *temp, *current; current = pHead; while(NULL != current->m_pNext) { current = current->m_pNext; } temp = new ListNode; temp->m_nValue = pElem; temp->m_pNext = NULL; current->m_pNext = temp; pHead->m_nValue += 1; return pHead; } /*输出整个链表*/ void show_List(ListNode *pHead) { if(NULL == pHead) { cout << "头结点为空" << endl; return; } ListNode *temp; temp = pHead->m_pNext; cout << "该 list 为:" ; while(NULL != temp) { cout << temp->m_nValue << ' '; temp = temp->m_pNext; } cout << endl; } /*找到第k个结点,并输出*/ void input_k_Node(ListNode *pHead,int k) { if(NULL == pHead || k <= 0) /**不要忘记头指针为空,和 k <= 0 的情况*/ { cout << "头结点为空 或 k 小于等于0" << endl; return; } if(k > pHead->m_nValue) { cout << "倒数第k个节点已超出链表范围" << endl; return; } int num = pHead->m_nValue - k; /*pHead->mValue里保存的链表结点总数,倒数第 k 个结点之前的结点数*/ int i = 0; ListNode *temp = pHead; while (i <= num) { temp = temp->m_pNext; i++; } cout << "倒数第k个结点: " << temp->m_nValue << endl; } int main() { int n_of_list = 6; int i = 1; ListNode *pHead; pHead = creat_List(); while(n_of_list > 0) { insert_Node_behind(pHead,i++); n_of_list--; } show_List(pHead); input_k_Node(pHead,10); /*改变这里的 k 值来测试*/ return 0; }部分结果如下:
相关题目,可做思考:
1、求链表的中间结点。如果链表中结点总数为奇数,返回中间结点;若结点总数是偶数,返回中间结点的任意一个。
为了解决这个问题,我们可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。当走的快的指针走到链表的末尾时,走的慢的指针正好在链表的中间。
2、判断一个单向链表是否形成了环形结构。
和前面的问题一样,定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形链表;如果走得快得指针走到了链表的末尾(即pNext->NULL)都没有追上第一个指针,那么链表就不是环形链表。
通过这两个相关题目的解决思路,可以看出当我们用一个指针遍历链表不能解决问题时,可以尝试用两个指针来遍历链表,一个走得快些,另一个慢些。
因此对于本题,寻找倒数第k个结点,也可以这样做:
定义两个指针,第一个指针从链表的头指针开始遍历向前走 k-1,第二个指针保持不动,从第 k 步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在 k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第 k 个结点。
/*点滴积累,我的一小步O(∩_∩)O~*/
相关文章推荐
- 黑马程序员---Foundation - NSString
- 剑指Offer面试题36(Java版):数组中的逆序对
- 黑马程序员---oc block
- Java笔试面试题004
- 程序员的谈判技巧
- 黑马程序员 oc随记 写一个手动内存释放
- 两会最新消息:两会时间看全面深化改革“关键棋”
- 2015公务员工资改革消息:广州人社局长称基层公务员工资肯定会增不少
- Hadoop 之面试题
- 黑马程序员——Java基础---多线程
- 黑马程序员——Java基础语法
- 操作系统面试题整理
- 剑指Offer面试题35(java版):第一个只出现一次的字符
- 黑马程序员——反射2:应用
- 答读者问(5):关于数学程序猿的作用、r \\ u0026研发工作的实践要求和问题,如求职的影响
- 面试准备--java垃圾回收机制、内存管理
- 黑马程序员——反射1:概述
- 剑指Offer面试题34(java版):丑数
- 黑马程序员——正则表达式3:练习
- 黑马程序员——正则表达式2:功能演示