关闭

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

标签: 算法nullinputc++class测试
6022人阅读 评论(2) 收藏 举报
分类:

[题目]:    给定一个单向链表(长度未知),请设计一个既节省时间又节省空间的算法来找出该链表中的倒数第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
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:108912次
    • 积分:1296
    • 等级:
    • 排名:千里之外
    • 原创:14篇
    • 转载:11篇
    • 译文:0篇
    • 评论:59条
    最新评论
    论坛
    在线杂志