zoukankan      html  css  js  c++  java
  • 从无头单链表中删除节点及单链表的逆置

    题目:

            假设有一个没有头指针的单链表。一个指针指向此单链表中间的一个节点(非第一个节点, 也非最后一个节点)。请将该节点从单链表中删除。

     

    解答:

            典型的“狸猫换太子”, 若要删除该节点,正常情况下,应该要知道该节点的前面节点的指针,但是由于单链表中没有头结点,所以无法追溯到该节点前面的那个节点,因此,这里采用了“移花接木”的方法。设该节点为B,下一个节点为C。那么,首先将B节点的内容替换为C节点的内容,然后,将C节点删除,这样就达到了我们的目的。代码如下:

    pcur->next = pnext->next;

    pcur->data = pnext->date;

    delete pnext;

     

    代码:

    void DeleteListNode(node* pCurrent)  
    {      
     assert(pCurrent != NULL);      
     node* pNext = pCurrent -> next;     
     if (pNext == NULL)      
      pCurrent = NULL;    
     else  
     {     
      pCurrent -> next = pNext -> next;  
      pCurrent -> data = pNext -> data;   
      delete pNext;     
     }
    }

    类似问题:

    http://hi.baidu.com/liangrt_fd/blog/item/4deb905028aa0c55d00906da.html

    1 从无头单链表中删除节点问题:假设有一个没有头指针的单链表,一个指针p指向单链表中的一个节点(不是第一个,也不是最后一个),请将该节点删除掉。

    2 向无头单链表中添加节点问题:假设有一个没有头指针的单链表,一个指针p指向单链表中的一个节点(不是第一个,也不是最后一个),请在该节点之前插入一个新的节点q

     

            由于链表是无头单向链表,所以我们无法由当前节点获得p的前一节点,而无论是删除当前节点还是向前面节点插入新节点都需要获得p的前一节点。在这里我们不妨换一下思路,对当前节点的后继结点进行操作,然后将前后节点的数据进行适当交换也可以得到相应效果。

    问题1解法:将p后继结点p->next的数据拷贝到p,然后删除p->next,这样就达到了相同的效果,代码如下:

                                    ListNode* p_next = p->next;

                                    p->value=p_next->value;

                                    p->next=p_next->next;

                                    delete   p_next;

    问题2解法:在p节点后添加q,然后交换p和q的数据即可。

                                    q->next=p->next;

                                    p->next=q;

                                    swap(&p->value, &q->value);

     

     

     

    假设一个没有头结点的单链表,一个指针指向此单链表中间的一个节点(不是第一个节点也不是最后一个节点),请将该节点从单链表中删除
    //删除不带头结点链表中的任意个节点

    #include<iostream>
    using namespace std;

    typedef struct  node
    {
    int data;
    struct node *next;
    }Node;

    class List
    {
    public:
    List();
    ~List();
    void CreateList();
    void DisplayList();
    void DeleteNode(Node *d);
    Node* GetNode(int n);
    private:
    Node * list;
    };

    List::List()
    {
    list=NULL;
    };

    List::~List()
    {
    if(list)
    {
    Node *p=list;
    while(p)
    {
    list=p->next;
    delete p ;
    p=list;
    }
    }
    }

    void List::CreateList()   //创建不带头结点的链表
    {
    int num;
    Node *p;
    cout<<"Enter digital number('Enter 'ctrl+z' to quit'):."<<endl;
    while(cin>>num)
    {
    if(!list)  //第一个节点特殊处理
    {
    list=new Node;
    list->data=num;
    list->next=NULL;
    p=list;
    }
    else
    {
    Node *temp=new Node;
    temp->data=num;
    temp->next=NULL;
    p->next=temp;
    p=temp;
    }
    }
    }

    void List::DisplayList()
    {
    Node *p=list;
    while(p)
    {
    cout<<p->data<<"  ";
    p=p->next;
    }
    cout<<endl;
    }

    Node* List::GetNode(int n)  //返回第n个节点
    {
    Node *p=list;
    while(p && --n)
    p=p->next;
    return p;
    }
    void List::DeleteNode(Node *d)  //删除节点d
    {
    Node *pCurrent=d;
    Node *pNext=pCurrent->next;  //删除该节点的下一个,然后将data赋给d,进行替换
    pCurrent->next=pNext->next;
    pCurrent->data=pNext->data;
    }

    int main()
    {
    List list;
    list.CreateList();
    list.DisplayList();
    Node *p=list.GetNode(2);
    cout<<p->data<<endl;
    list.DeleteNode(p);
    list.DisplayList();
    system("pause");
    return 0;
    }

    扩展问题:

            将一个单链表,在只遍历一遍的情况下,将单链表中的元素顺序反转过来。

    解答:

            我的想法是这样的,用三个指针进行遍历,在遍历的途中,进行逆置。

    这道题目有两种算法,既然是要反转,那么肯定是要破坏原有的数据结构的:算法:我们需要额外的两个变量来存储当前节点curr的下一个节点next、再下一个节点nextnext:

    Java代码  

    1. public static Link ReverseLink1(Link head)    
    2. {    
    3.     Link curr = head.Next;    
    4.     Link next = null;    
    5.     Link nextnext = null; //if no elements or only one element exists    
    6.     if (curr == null || curr.Next == null) {    
    7.         return head;    
    8.     } //if more than one element    
    9.     while (curr.Next != null) {    
    10.         next = curr.Next; //1    
    11.         nextnext = next.Next; //2    
    12.         next.Next = head.Next; //3    
    13.         head.Next = next; //4    
    14.         curr.Next = nextnext; //5    
    15.     }    
    16.     return head;    
    17. }  

    public static Link ReverseLink1(Link head)

    {

      Link curr = head.Next;

      Link next = null;

      Link nextnext = null; //if no elements or only one element exists

      if (curr == null || curr.Next == null) {

        return head;

      } //if more than one element

      while (curr.Next != null) {

        next = curr.Next; //1

        nextnext = next.Next; //2

        next.Next = head.Next; //3

        head.Next = next; //4

        curr.Next = nextnext; //5

      }

      return head;

    }

     

        算法的核心是while循环中的5句话 我们发现,curr始终指向第1个元素。此外,出于编程的严谨性,还要考虑2种极特殊的情况:没有元素的单链表,以及只有一个元素的单链表,都是不需要反转的。

     

    C语言实现

     

    非递归方式:这是一般的方法,总之就是用了几个临时变量,然后遍历整个链表,将当前节点的下一节点置为前节点

    C代码  

    1. void reverse(node*& head)   
    2.     {   
    3.         if ( (head == 0) || (head->next == 0) ) return;// 边界检测   
    4.         node* pNext = 0;   
    5.         node* pPrev = head;// 保存链表头节点   
    6.         node* pCur = head->next;// 获取当前节点   
    7.         while (pCur != 0)   
    8.         {   
    9.             pNext = pCur->next;// 将下一个节点保存下来   
    10.             pCur->next = pPrev;// 将当前节点的下一节点置为前节点   
    11.             pPrev = pCur;// 将当前节点保存为前一节点   
    12.             pCur = pNext;// 将当前节点置为下一节点   
    13.        }   
    14.         head->next = 0; //将旧head节点设置为尾部节点   
    15.         head = pPre;  //设置当前遍历的最后一个节点为新的头节点   
    16.     }  

    void reverse(node*& head)

        {

            if ( (head == 0) || (head->next == 0) ) return;// 边界检测

            node* pNext = 0;

            node* pPrev = head;// 保存链表头节点

            node* pCur = head->next;// 获取当前节点

            while (pCur != 0)

            {

                pNext = pCur->next;// 将下一个节点保存下来

                pCur->next = pPrev;// 将当前节点的下一节点置为前节点

                pPrev = pCur;// 将当前节点保存为前一节点

                pCur = pNext;// 将当前节点置为下一节点

           }

            head->next = 0; //将旧head节点设置为尾部节点

            head = pPre;  //设置当前遍历的最后一个节点为新的头节点

        }

      

    递归方式:这个方法是采用了递归算法,也就是在反转当前节点之前先反转其后继节点,利用函数的调用堆栈构建了一个临时链表。采用此算法需要注意的是,头结点必须要传入的是引用,因为在递归跳出的时候要切断链表,否则链表将会形成一个回环。

     

    C代码  

    1. node* reverse( node* pNode, node*& head)   
    2. {   
    3.     if ( (pNode == 0) || (pNode->next == 0) ) // 递归跳出条件   
    4.     {   
    5.         head = pNode; // 将链表切断,否则会形成回环   
    6.         return pNode;   
    7.     }   
    8.   
    9.     node* temp = reserve(pNode->next, head);// 递归   
    10.     temp->next = pNode;// 将下一节点置为当前节点,既前置节点   
    11.     return pNode;// 返回当前节点   
    12. }  

    编写一个函数,给定一个链表的头指针,只要求遍历一次,将单链表中的元素顺序反转过来

    #include <iostream>
    using namespace std;

    typedef struct node
    {
    int data;
    struct node *next;
    }Node;

    class List
    {
    public:
    List();
    ~List();
    void CreateList();
    void DisplayList() const;
    void ReverseList();
    private:
    Node *head;
    };

    List::List()
    {
    //分配头结点 
    head=new Node;
    head->next=NULL;
    head->data=0;
    }

    List::~List()
    {
    if(head)
    {
    Node *pCurrent=head;
    while(head)
    {
    pCurrent=head->next;
    head=pCurrent;
    delete pCurrent;
    }
    }
    }

    void List::CreateList()
    {
    //创建带头结点的链表 
    int num;
    cout<<"Enter digital numbers('ctrl+z' to quit):"<<endl;
    Node *pCurrent=head;
    while(cin>>num)
    {
    Node *pTemp=new Node;
    pTemp->data=num;
    pTemp->next=NULL;
    pCurrent->next=pTemp;
    pCurrent=pTemp;
    }
    }

    void List::DisplayList() const
    {
    Node *pCurrent=head->next;
    while(pCurrent)
    {
    cout<<pCurrent->data<<"  ";
    pCurrent=pCurrent->next;
    }
    cout<<endl;
    }

    void  List::ReverseList()  //思想是将指针的方向反向。将头节点指向尾节点 
    {
    Node *pCurrent,*pNext,*pTemp;
    pCurrent=head->next;  //指向第一个节点 
    pNext=pCurrent->next;  //第二个节点
    if(!pCurrent || !pNext)  //当链表为空或者只含有一个节点 
    return;
    pCurrent->next=NULL;     //将第一个节点变为为节点 
    while(pNext)
    {
    pTemp=pNext->next;    //保存下一个节点 
    pNext->next=pCurrent; //将指针方向 
    pCurrent=pNext;       //指向当前的节点 
    pNext=pTemp;          //下一个节点 
    }
    head->next=pCurrent;  //头结点指向最后一个节点 
    }

    int main()
    {
    List list;
    list.CreateList();
    list.DisplayList();
    cout<<"Reverse LinkList."<<endl; 
    list.ReverseList();
    list.DisplayList();
    system("pause");
    return 0;
    }   

  • 相关阅读:
    深入浅出计算机组成原理学习笔记:第五十五讲
    深入浅出计算机组成原理学习笔记:第五十四讲
    深入浅出计算机组成原理学习笔记:第五十三讲
    深入浅出计算机组成原理学习笔记:第五十一讲
    深入浅出计算机组成原理学习笔记:第四十九讲
    Linux性能优化实战学习笔记:第三十五讲
    Linux性能优化实战学习笔记:第三十四讲
    Linux性能优化实战学习笔记:第十八讲
    Linux性能优化实战学习笔记:第三十讲
    深入浅出计算机组成原理学习笔记:第五十讲
  • 原文地址:https://www.cnblogs.com/fickleness/p/3154950.html
Copyright © 2011-2022 走看看