删除单链表的倒数第m个元素

[题目]:    给定一个单向链表(长度未知),请设计一个既节省时间又节省空间的算法来找出该链表中的倒数第m个元素。实现这个算法,并为可能出现的特例情况安排好处理措施。“倒数第m个元素”是这样规定的:当m=0时,链表的最后一个元素将被返回。
   
    如果沿从头至尾的方向从链表中的某个元素开始,遍历m个元素后刚好达到链表尾,那么该元素就是我们所要找的。这样,我们可以引出第一种办法:从头结点开始,依次对链表的每一个元素进行这样的测试,遍历m个元素,看是否到达链表尾。直到我们找到那个倒数第m个元素为止。很明显,这个方案的效率是很差的,因为我们将对同一批元素进行反复多次的遍历。仔细研究一下这个方案就会发现,对于链表中的大部分元素,我们都要遍历m个元素;如果这个链表的长度为n,这个算法的执行时间大概是O(mn)级的。我们应该尝试寻找一个优于O(mn)级的解决方案。


    如果我们首先遍历该链表,统计链表的长度(假定为n),那么要找的元素在该链表中的位置即n-m。这样,从链表的头开始,向后遍历n-m个元素即可。虽说整个过程需要对链表进行2次遍历,但它仍然是一个O(n)级的算法,而且它只需要很少的几个临时变量。相对于此前的方案,这一思路的时间和空间效率有了显著的改进。要是你能设法省下统计链表长度的那一次遍历,这个算法将更加有效了。


    首先不妨假设在前2种算法中我们用指针p1遍历链表。p1指向链表尾的时候,我们所要找的元素(假定为data-m)跟链表尾的相对距离为m。如果这时候有另外一个指针p2指向data-m,也就是说,p1与p2间隔m个元素。如果把该单向链表改为双向链表,并保持p1、p2之间的同步,当p2指针回溯到链表头结点的时候,p1指针刚好指向从头结点开始的第m个元素。根据这个相隔m个位置的相对距离,我们完全可以明确地再设置一个用来遍历单向链表的指针p2,让它与链表的第一个指针p1(前2种算法中用于统计链表长度的指针)间隔m个元素,然后让它们同步前进。当p1指向链表尾时,p2刚好指向倒数第m个元素。
    那么怎样才能让这2个指针保持正确的m间距呢?我们可以这样做:当p1从链表头开始统计链表元素个数(注意要从0开始计算),等数到第m个元素时,再让p2指针从链表头开始出发。这个,他们之间的距离就是m个元素了。
    在这一过程中,如果链表的长度不足m个元素,我们在试图使p1前进m个元素时,很可能就会在半路上遇见对“NULL”指针进行操作的情况。因此,我们需要在这2个指针的出发阶段检查p1指针是否已经到达了链表的尾端。

算法描述: element *FindMToLastElement (int head,int m)
                      {
                          element *p1,*p2;
                          int i;
                          p1 = head;
                          for(i=0;i<m;i++)
                              { 
                               if(p1->next)
                               p1 = p1->next;
                              else
                              return NULL;
                              }
                         p2 = head;
                         while(p1->next)
                           {
                            p1 = p1->next;
                            p2 = p2->next;
                           }
                        return p2;
                      }

            
C++实现:

/*show_MToLast_node.cpp     Amirural设计 */
#include <iostream.h>

class ListNode
{
private:
 int data;
 ListNode *next_node;
 static ListNode *now;                                                 //目前的结尾结点指针
 const static ListNode *head;                                    //连接链表的起始指针,head ->next_node指向第一个结点

public:
 ListNode(int node_data):data(node_data),next_node(NULL){}
 ListNode():next_node(NULL)
 {head=now=this;}
 static void add_node(int new_data);                      //增加新的结点
 static void show_all_node();                                   //显示所有的结点          
 static void show_MToLast_node(int m);              //显示倒数第m个结点
};

ListNode *ListNode::now;           
const ListNode *ListNode::head;

void ListNode::add_node(int new_data)
{
 now->next_node=new ListNode(new_data);
 now=now->next_node;
}

void ListNode::show_all_node()
{
 now=head->next_node;
 while(now!=NULL)
 {
 cout<<now->data;
 if(now->next_node!=NULL)
  cout<<"->";
 now=now->next_node;
 }
}

void ListNode::show_MToLast_node(int m)
{
        ListNode *index,*mBehind;
        int i;
        index = head->next_node;
        for(i=0;i<m;i++)
         { 
         if(index->next_node)
          index = index->next_node;
        }

        mBehind = head->next_node;
        while(index->next_node)
        {
          index = index->next_node;
          mBehind = mBehind->next_node;
        }
        cout<<"The m to last data is:"<<mBehind->data<<endl;
 }

void main()
{
 int data,m;
 char choice;
 ListNode startnode;               

 do{
  cout<<"Please input data:";
  cin>>data;

  startnode.add_node(data);  
       
  cout<<"Do you want to input another data?";
  cin>>choice;
 }while(choice=='y'||choice=='Y');

 ListNode::show_all_node();

 cout<<"/nPlease input m:";
 cin>>m;
 ListNode::show_MToLast_node(m);
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要查找单链表倒数第k个元素,有几种方法可以实现。其一种方法是遍历整个链表,获取链表的长度n,然后再遍历n-k个元素即可找到倒数第k个元素。这种方法的时间复杂度为O(n),其n为链表的长度。 另一种方法是从链表的某个元素开始,向后遍历k个元素,如果刚好到达链表的末尾,那么当前元素就是所要找的倒数第k个元素。这种方法只需要进行一次遍历,时间复杂度为O(n),其n为链表的长度。 需要注意的是,对于第二种方法,如果链表的长度不足k个元素,那么这种方法无法找到倒数第k个元素。 另外还有一种方法是通过两个指针来实现,一个指针先遍历k个元素,然后两个指针同时向后遍历,当第一个指针到达链表末尾时,第二个指针所指向的元素就是所要找的倒数第k个元素。这种方法只需要进行一次遍历,时间复杂度为O(n),其n为链表的长度。 综上所述,可以通过遍历链表、从某个元素开始遍历或者使用两个指针的方法来查找单链表倒数第k个元素。具体选择哪种方法取决于实际情况和需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [如何找出单链表倒数第K个元素](https://blog.csdn.net/nihaoma1203/article/details/106619691)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值