- 浏览: 913438 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (537)
- Java SE (114)
- Struts (18)
- Hibernate (25)
- Spring (3)
- Page_Tech (41)
- Others (87)
- Database (29)
- Server (24)
- OpenSource_Tools (15)
- IDE_Tool (22)
- Algorithm (28)
- Interview (22)
- Test (28)
- Hardware (1)
- Mainframe (25)
- Web application (4)
- Linux (3)
- PHP (17)
- Android (1)
- Perl (6)
- ubuntu (1)
- Java EE (9)
- Web Analysis (5)
- Node.js (2)
- javascript (2)
最新评论
-
一键注册:
request.getRequestURL()和request.getRequestURI() -
SuperCustomer:
...
SED的暂存空间和模式空间 -
juyo_ch:
讲得挺好理解的,学习了
java 死锁及解决 -
chinaalex:
最后一题答案正确,但是分析有误.按照如下过程,上一行为瓶,下一 ...
zz智力题 -
liaowuxukong:
多谢博主啦,弱弱的了解了一点。
C++/Java 实现多态的方法(C++)
题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。链表结点定义如下: struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:为了得到倒数第k个结点,很自然的想法是先走到链表的尾端,再从尾端回溯k步。可是输入的是单向链表,只有从前往后的指针而没有从后往前的指针。因此我们需要打开我们的思路。
既然不能从尾结点开始遍历这个链表,我们还是把思路回到头结点上来。假设整个链表有n个结点,那么倒数第k个结点是从头结点开始的第n-k-1个结点(从0开始计数)。如果我们能够得到链表中结点的个数n,那我们只要从头结点开始往后走n-k-1步就可以了。如何得到结点数n?这个不难,只需要从头开始遍历链表,每经过一个结点,计数器加一就行了。
这种思路的时间复杂度是O(n),但需要遍历链表两次。第一次得到链表中结点个数n,第二次得到从头结点开始的第n-k-1个结点即倒数第k个结点。
如果链表的结点数不多,这是一种很好的方法。但如果输入的链表的结点个数很多,有可能不能一次性把整个链表都从硬盘读入物理内存,那么遍历两遍意味着一个结点需要两次从硬盘读入到物理内存。我们知道把数据从硬盘读入到内存是非常耗时间的操作。我们能不能把链表遍历的次数减少到1?如果可以,将能有效地提高代码执行的时间效率。
如果我们在遍历时维持两个指针,第一个指针从链表的头指针开始遍历,在第k-1步之前,第二个指针保持不动;在第k-1步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点。
这种思路只需要遍历链表一次。对于很长的链表,只需要把每个结点从硬盘导入到内存一次。因此这一方法的时间效率前面的方法要高。
思路一的参考代码:
- ///////////////////////////////////////////////////////////////////////
- // Find the kth node from the tail of a list
- // Input: pListHead - the head of list
- // k - the distance to the tail
- // Output: the kth node from the tail of a list
- ///////////////////////////////////////////////////////////////////////
- ListNode* FindKthToTail_Solution1(ListNode* pListHead, unsigned int k)
- {
- if(pListHead == NULL)
- return NULL;
- // count the nodes number in the list
- ListNode *pCur = pListHead;
- unsigned int nNum = 0;
- while(pCur->m_pNext != NULL)
- {
- pCur = pCur->m_pNext;
- nNum ++;
- }
- // if the number of nodes in the list is less than k
- // do nothing
- if(nNum < k)
- return NULL;
- // the kth node from the tail of a list
- // is the (n - k)th node from the head
- pCur = pListHead;
- for(unsigned int i = 0; i < nNum - k; ++ i)
- pCur = pCur->m_pNext;
- return pCur;
- }
/////////////////////////////////////////////////////////////////////// // Find the kth node from the tail of a list // Input: pListHead - the head of list // k - the distance to the tail // Output: the kth node from the tail of a list /////////////////////////////////////////////////////////////////////// ListNode* FindKthToTail_Solution1(ListNode* pListHead, unsigned int k) { if(pListHead == NULL) return NULL; // count the nodes number in the list ListNode *pCur = pListHead; unsigned int nNum = 0; while(pCur->m_pNext != NULL) { pCur = pCur->m_pNext; nNum ++; } // if the number of nodes in the list is less than k // do nothing if(nNum < k) return NULL; // the kth node from the tail of a list // is the (n - k)th node from the head pCur = pListHead; for(unsigned int i = 0; i < nNum - k; ++ i) pCur = pCur->m_pNext; return pCur; }
思路二的参考代码:
- ///////////////////////////////////////////////////////////////////////
- // Find the kth node from the tail of a list
- // Input: pListHead - the head of list
- // k - the distance to the tail
- // Output: the kth node from the tail of a list
- ///////////////////////////////////////////////////////////////////////
- ListNode* FindKthToTail_Solution2(ListNode* pListHead, unsigned int k)
- {
- if(pListHead == NULL)
- return NULL;
- ListNode *pAhead = pListHead;
- ListNode *pBehind = NULL;
- for(unsigned int i = 0; i < k; ++ i)
- {
- if(pAhead->m_pNext != NULL)
- pAhead = pAhead->m_pNext;
- else
- {
- // if the number of nodes in the list is less than k,
- // do nothing
- return NULL;
- }
- }
- pBehind = pListHead;
- // the distance between pAhead and pBehind is k
- // when pAhead arrives at the tail, p
- // Behind is at the kth node from the tail
- while(pAhead->m_pNext != NULL)
- {
- pAhead = pAhead->m_pNext;
- pBehind = pBehind->m_pNext;
- }
- return pBehind;
- }
/////////////////////////////////////////////////////////////////////// // Find the kth node from the tail of a list // Input: pListHead - the head of list // k - the distance to the tail // Output: the kth node from the tail of a list /////////////////////////////////////////////////////////////////////// ListNode* FindKthToTail_Solution2(ListNode* pListHead, unsigned int k) { if(pListHead == NULL) return NULL; ListNode *pAhead = pListHead; ListNode *pBehind = NULL; for(unsigned int i = 0; i < k; ++ i) { if(pAhead->m_pNext != NULL) pAhead = pAhead->m_pNext; else { // if the number of nodes in the list is less than k, // do nothing return NULL; } } pBehind = pListHead; // the distance between pAhead and pBehind is k // when pAhead arrives at the tail, p // Behind is at the kth node from the tail while(pAhead->m_pNext != NULL) { pAhead = pAhead->m_pNext; pBehind = pBehind->m_pNext; } return pBehind; }
讨论:这道题的代码有大量的指针操作。在软件开发中,错误的指针操作是大部分问题的根源。因此每个公司都希望程序员在操作指针时有良好的习惯,比如使用指针之前判断是不是空指针。这些都是编程的细节,但如果这些细节把握得不好,很有可能就会和心仪的公司失之交臂。
另外,这两种思路对应的代码都含有循环。含有循环的代码经常出的问题是在循环结束条件的判断。是该用小于还是小于等于?是该用k还是该用k-1?由于题目要求的是从0开始计数,而我们的习惯思维是从1开始计数,因此首先要想好这些边界条件再开始编写代码,再者要在编写完代码之后再用边界值、边界值减1、边界值加1都运行一次(在纸上写代码就只能在心里运行了)。
扩展:和这道题类似的题目还有:输入一个单向链表。如果该链表的结点数为奇数,输出中间的结点;如果链表结点数为偶数,输出中间两个结点前面的一个。如果各位感兴趣,请自己分析并编写代码。
发表评论
-
IGT JAVA Test
2012-06-18 10:18 01. static synchronized vs synch ... -
zz IBM 面试问题
2012-05-30 14:31 9651.JAVA内存回收机制 2.抽象类与接口的区别 3. ... -
面试的准备与发挥
2012-04-26 10:00 869面试是一场智力的较 ... -
栈内存和堆内存
2010-10-21 11:55 790堆:顺序随意栈:先进 ... -
淘宝面试的几个算法题
2010-10-21 11:27 1475一、给你1副扑克牌,你 ... -
程序员面试题精选(18)-用两个栈实现队列
2010-10-18 22:45 1089题目:某队列的声明如下: C++代码 t ... -
程序员面试题精选(17)-把字符串转换成整数
2010-10-18 22:44 998题目:输入一个表示整 ... -
程序员面试题精选(16)-O(logn)求Fibonacci数列
2010-10-18 22:41 1824题目:定义Fibonacci数列如下: / ... -
程序员面试题精选(15)-含有指针成员的类的拷贝
2010-10-18 22:41 883题目:下面是一个数组类的声明与实现。请分析这个类有什么问题,并 ... -
程序员面试题精选(14)-圆圈中最后剩下的数字
2010-10-18 22:40 1449题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始 ... -
程序员面试题精选(13)-第一个只出现一次的字符
2010-10-18 22:38 906题目:在一个字符串中找到第一个只出现一次的字符。如输入abac ... -
程序员面试题精选(12)-从上往下遍历二元树
2010-10-18 22:37 965题目:输入一颗二元树,从上往下按层打印树的每个结点,同一层中按 ... -
程序员面试题精选(11)-求二元查找树的镜像
2010-10-18 22:36 1339题目:输入一颗二元查 ... -
程序员面试题精选(10)-在排序数组中查找和为给定值的两个数字
2010-10-18 22:33 1029题目:输入一个已经按升序排序过的数组和一个数字,在数组中查找两 ... -
程序员面试题精选(08)-求1+2+...+n
2010-10-18 22:31 980题目:求1+2+…+n,要求不能使用乘除法、for、while ... -
程序员面试题精选(07)-翻转句子中单词的顺序
2010-10-18 22:30 1173题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺 ... -
程序员面试题精选(06)-判断整数序列是不是二元查找树的后序遍历结果
2010-10-18 22:27 779题目:输入一个整数数 ... -
程序员面试题精选(05)-查找最小的k个元素
2010-10-18 22:24 1438题目:输入n个整数,输出其中最小的k个。 例如输入1,2,3, ... -
程序员面试题精选(04)-在二元树中找出和为某一值的所有路径
2010-10-18 22:22 849题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到 ... -
程序员面试题精选(03)-求子数组的最大和
2010-10-18 22:20 850题目:输入一个整形数组,数组里有正数也有负数。数组中连续的一个 ...
相关推荐
本题的目标是删除链表中的倒数第N个节点。下面我们将深入探讨这个问题,分析解决方案,并学习相关的链表知识。 链表是一种线性数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的引用。与数组不同...
- **找出倒数第k个元素**: 使用双指针法,先移动一个指针k步,然后两个指针同时移动直到第一个指针到达链表尾部。 - **检测单链表是否有环**: 使用快慢指针技术,快指针每次移动两步,慢指针每次移动一步,如果有...
5. 如何找出单链表中的倒数第 k 个元素 6. 如何检测一个较大的单链表是否有环 7. 如何把链表相邻元素翻转 8. 如何把链表以 k 个结点为一组进行翻转 9. 如何合并两个有序链表 栈与队列篇 1. 如何实现栈 2. 如何实现...
删除链表的倒数第N个节点、面试题02.07.链表相交和142.环形链表II。记录了清晰的文字题解+图示以及Java参考代码。 适合人群:链表算法感兴趣的程序员或学生,想要打好数据结构与算法基础的人。 能学到什么:掌握链表...
15链表中倒数第k个结点 16反转链表 17合并两个排序的链表 18树的子结构 19二叉树的镜像 20顺时针打印矩阵 21包含min函数的栈 22栈的压入弹出序列 23从上往下打印二叉树 24二叉搜索树的后序遍历序列 25二叉树中和为某...
- 删除链表倒数第n个节点。 - 实现思路:使用快慢指针,快指针先走n步,当快指针到达尾部时,慢指针指向待删除节点的前一个节点。 - **2.2.8 Swap Nodes in Pairs** - 两两交换链表中的节点。 - 实现思路:使用...
5. 链表:包括链表中倒数第K个结点,链表中环的入口结点,反转链表,合并两个排序的链表等。 6. 查找算法:如数值的整数次方,数组中出现次数超过一半的数字等。 7. 动态规划:例如把数组排成最小的数,把数字翻译成...
13. 链表中倒数第k个节点:找到链表中倒数第k个节点。 14. 反转链表:将链表的连接方向逆转。 15. 合并两个排序的链表:将两个已经排序好的链表合并成一个新的排序链表。 16. 树的子结构:判断一个树是否包含另一...