从无头单链表中删除节点及单链表的逆置

题目:

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

 

解答:

        典型的“狸猫换太子”, 若要删除该节点,正常情况下,应该要知道该节点的前面节点的指针,但是由于单链表中没有头结点,所以无法追溯到该节点前面的那个节点,因此,这里采用了“移花接木”的方法。设该节点为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;
}   

 

posted @ 2013-06-25 16:33  夜雨阑珊  阅读(5298)  评论(0编辑  收藏  举报