复试算法练习Day06——单项链表倒数第k个结点输出

复试算法练习Day06

题目描述

输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第1个结点为链表的尾指针。

链表结点定义如下:

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

正常返回倒数第k个结点指针,异常返回空指针.

要求:

(1)正序构建链表;

(2)构建后要忘记链表长度。

数据范围:链表长度满足 1 \le n \le 1000 \1≤n≤1000 , k \le n *kn* ,链表中数据满足 0 \le val \le 10000 \0≤val≤10000

本题有多组样例输入。

题目来源

(https://www.nowcoder.com/practice/e45e078701ab4e4cb49393ae30f1bb04?tpId=37&tqId=21235&rp=1&ru=/exam/oj/ta&qru=/exam/oj/ta&sourceUrl=%2Fexam%2Foj%2Fta%3Fpage%3D1%26tpId%3D37%26type%3D37&difficulty=undefined&judgeStatus=undefined&tags=&title=)

输入描述

输入说明
1 输入链表结点个数
2 输入链表的值
3 输入k的值

输出描述

输出一个整数

输入:	8
	  1 2 3 4 5 6 7 8
	  4
输出:
	  5

思路

由于采用单链表,所以不能反向输出倒数第k个节点,所以从头遍历其实效率不高。因此提供两种思路:

思路1:直接从头遍历单链表后得到节点个数n,由于采用的是包含头节点但是头结点无数据元素,因此不将其算为一个节点。之后再次遍历,从头结点的下一个节点开始的第n-k+1个节点即为所求的倒数第k个节点。因此只需向后移动n-k次指针则可以完成题目要求。

思路2:参考王道课后习题最优解,设置两个指针,使其相距为k-1,当后一个指针指向最后一个节点时,前一个指针则指向倒数第k个指针。只需遍历一遍链表即可得到输出结果。

具体实现

//思路1,采用两次遍历链表给出输出结果
LinkNode * FindReverNode(LinkNode* head,int k)    
 {    
   if(head==NULL || head->next==NULL)    
       return NULL;    
   int NodeCount=0;  
    //第一个指向即为头指针
    LinkNode*fir=head; 
    //一直往下遍历计数,得出节点个数
    while(head->next!=NULL)    
    {    
       fir=fir->next;   
         NodeCount++;   
    }   
     if(NodeCount<k)   
       return NULL;   
    fir=head->next;
    //遍历数组输出在第n-k+1个节点的值即为所求
    for(int i=0;i<NodeCount-k;i++)   
        fir=fir->next;   
    return fir;   
}
//思路2,采用两个指针,使其相距为k-1,后一个指向最后节点的时候,前一个找到所求结果
LinkNode* FindReverNode(LinkNode* head, int k)  { 
      if(head == NULL || head->next==NULL) 
            return NULL; 
 	   //不考虑头结点,即头结点为空
 	   //跳过头结点,指向第一个数据元素
       LinkNode *fir = head->next; 
       LinkNode *sec = head->next;
  	   //将second指针向后移动k-1个距离
       for(int i = 1; i < k; i++)  { 
             if(sec->next != NULL) 
                   sec = sec->next; 
             else { 
                  return NULL; 
            } 
      } 
  	  //当最后一个指针向后遍历到最后一个节点的时候
  	  //同时第一个指针也向后遍历了k次,
  	  //得到了当前第k个节点的结果,并且输出即可得到结果
       while(sec->next != NULL) { 
            sec = sec->next; 
            fir= fir->next; 
      } 
 
       return fir; 
 } 
 

时间复杂度

  • 思路1遍历了两次链表。虽时间复杂度是O(n),但如果链表的节点数比较多,比如不能一次将数据读入到内存,遍历后输出将会非常耗时。
  • 思路2只遍历了一次链表,时间复杂度是O(n),相比于思路1更加巧妙,不用多次读入数据,提高了算法读写速度。

小结

如果可以反向输出节点,利用reverse函数,如果链表过大可以节省效率,不然由于只能采用单链表,所以从头遍历其实效率一般。第一种思路直接遍历了两次单链表,属于暴力破解,对于单链表如果节点很多,其读写占用时间也很多,第二种思路比较巧妙,采用两个节点相距的距离要求可以同时移动指针,即当后一个节点移动到最后,前一个节点就找到目标节点可以直接输出,需要去理解同时并行的移动指针,思想很巧妙,不容易想到,第一种偏暴力破解,理解起来思路更简单。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值