过年实在闲得无聊,还是撸几道算法,写写博文,打发打发时间。
微软的面试题,难度系数低,描述如下:
题目:输入一个单向链表,输出该链表中倒数第k 个结点。链表的倒数第0 个结点为链表
的尾指针。
链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
1、前几天和钧哥会晤的时候,刚好看到这道题,我想不只是我,多数人的第一想法,都是马上想到两次遍历(第一次遍历记录链表长度,第二次遍历到n-k+1节点),显然,空间考虑,我们需要一个指针变量,复杂度O(1),而时间上,因为两次都是线性遍历,所以是O(n)+O(n-k+1),时间复杂度O(n),尽管是线性,但是我们却遍历了两次,如果考虑将链表载入内存,那么显然,遍历两次是不可取的,链表的加载和释放会降低算法运行的效率。
2、那么,继而就有了这样一个想法,扩展空间,我们用两个指针来实现,将两个指针的差值记为k,当指针p1指向链表尾指针指向的NULL,指针p2如果能够与p1差k个节点,那么p2就指向了倒数第k个节点,显然,我们可以通过另p1先行k步,从而保持p2与p1的差值。显然时间复杂度也是O(n),只不过我们仅仅遍历了一次。
struct ListNode { char data; ListNode* next; }; ListNode* head,*p,*q; ListNode *pone,*ptwo; //@heyaming, 第一节,求链表倒数第k个结点应该考虑k大于链表长度的case。 ListNode* fun(ListNode *head,int k) { assert(k >= 0); pone = ptwo = head; for( ; k > 0 && ptwo != NULL; k--) ptwo=ptwo->next; if (k > 0) return NULL; while(ptwo!=NULL) { pone=pone->next; ptwo=ptwo->next; } return pone; }