從無頭單鏈表中刪除節點及單鏈表的逆置


題目:

        假設有一個沒有頭指針的單鏈表。一個指針指向此單鏈表中間的一個節點(非第一個節點, 也非最后一個節點)。請將該節點從單鏈表中刪除。

 

解答:

        典型的“狸貓換太子”, 若要刪除該節點,正常情況下,應該要知道該節點的前面節點的指針,但是由於單鏈表中沒有頭結點,所以無法追溯到該節點前面的那個節點,因此,這里采用了“移花接木”的方法。設該節點為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;
}   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM