四道有趣的單鏈表面試題

四道有趣的單鏈表面試題(單鏈表反序、找出鏈表的中間元素、鏈表排序、判斷一個單鏈表是否有環)

以下給出鏈表結點的數據結構:

template <class T>
struct LinkNode{   //鏈表節點
T data;
LinkNode<T>* link;
LinkNode(LinkNode<T>* ptr=NULL){link=ptr;}
LinkNode(const T& x,LinkNode<T>* ptr=NULL){link=ptr;data=x;}
};

以下給出鏈表的定義(帶有附加頭結點):

template <class T>
class SList{
private:
LinkNode<T>* first;
public:
SList(){first=new LinkNode<T>;}   //構造函數
SList(T& x){first=new LinkNode<T>(x);} //構造函數
SList(SList<T>& L); //複製構造函數
~SList();  //析構函數
int Length()const; //鏈表長度
LinkNode<T>* getHead()const{return first;}
void setHead(LinkNode<T>* p){first=p;}
bool Search(T& x);//搜索x元素
LinkNode<T>* Locate(int i)const; //定位
T getData(int i)const;   //取出元素值
void setData(int i,const T& x); //設置元素值
bool Insert(int i,const T& x); //插入
bool Remove(int i); //刪除第i個元素
bool isEmpty()const{return first->link==NULL?true:false;}
bool isFull()const{return false;}
void Input(int endTag);
void vInput(int endTag);
void Output()const;
void Reverse();
LinkNode<T>* findMid();
SList<T>& operator=(SList<T>& L);
};

Q1 單鏈表的反序(cur爲鏈表的第一個元素,next爲cur的下一個元素,每次將next移到first後面之後,cur是一直不變的,next移動成功之後,重新設置next=cur->link)

(我將題目的意思理解錯了,下面的程序實在頭結點不變的情況下,對單鏈表進行反轉,但是實際上應該是頭結點變成尾節點,尾節點變成頭結點,哎呀,懶得改了)

template <class T>
void SList<T>::Reverse(){
if(first->link==NULL||first->link->link==NULL) {cout<<"不需要倒置"<<endl;return;};
LinkNode<T>* cur=first->link;
LinkNode<T>* next=cur->link;
while(cur->link!=NULL){
cur->link =next->link;
next->link=first->link;
first->link=next;
next=cur->link;
}
}

Q2 找出鏈表的中間元素

思路分析:
 單鏈表的一個比較大的特點用一句廣告語來說就是“不走回頭路”,不能實現隨機存取(random access)。如果我們想要找一個數組a的中間元素,直接a[len/2]就可以了,但是鏈表不行,因爲只有a[len/2 - 1] 知道a[len/2]在哪兒,其他人不知道。因此,如果按照數組的做法依樣畫葫蘆,要找到鏈表的中點,我們需要做兩步(1)知道鏈表有多長(2)從頭結點開始順序遍歷到鏈表長度的一半的位置。這就需要1.5n(n爲鏈表的長度)的時間複雜度了。有沒有更好的辦法呢?有的。想法很簡單:兩個人賽跑,如果A的速度是B的兩倍的話,當A到終點的時候,B應該剛到中點。這只需要遍歷一遍鏈表就行了,還不用計算鏈表的長度。

template <class T>
LinkNode<T>* SList<T>::findMid(){
if(first->link==NULL) return first;
LinkNode<T>* slow=first;
LinkNode<T>* fast=first;
while(fast!=NULL&&fast->link!=NULL){
slow=slow->link;
fast=fast->link->link;}
return slow;
}

(上面的程序,如果鏈表的長度爲奇數,顯然找到中間元素,如果鏈表的長度爲偶數,找的是中間兩個元素之中的第二個元素)

Q3  鏈表排序(沒看懂to be continued)

Q4  判斷一個單鏈表是否有環

思路分析:
   這道題是《C專家編程》中的題了。其實算法也有很多,比如說:我覺得進行對訪問過的結點進行標記這個想法也不錯,而且在樹遍歷等場合我們也經常使用。但是在不允許做標記的場合就無法使用了。在種種限制的條件下,就有了上面的這種算法,其實思想很簡單:就像兩個人在操場上跑步一樣,只要有個人的速度比另一個人的速度快一點,他們肯定會有相遇的時候的。不過帶環鏈表與操場又不一樣,帶環鏈表的狀態是離散的,所以選擇走得快的要比走得慢的快多少很重要。比如說這裏,如果一個指針一次走三步,一個指針一次走一步的話,很有可能它們雖然在一個環中但是永遠遇不到,這要取決於環的大小以及兩個指針初始位置相差多少了。

template <class T>
bool isLoop(SList<T>& L){
LinkNode<T>* slow=L.getHead();
LinkNode<T>* fast=L.getHead();
while(fast!=NULL&&fast->link!=NULL){
slow=slow->link;
fast=fast->link->link;
cout<<slow->data<<" "<<fast->data<<endl;
if(slow==fast) return true;
}
return false;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章