1. 程式人生 > >面試常見程式設計題專題一:連結串列

面試常見程式設計題專題一:連結串列

最近在準備找實習,各種刷題。準備把刷過的題以專題的形式做個總結。

先說說連結串列,連結串列是一種動態的資料結構,其操作需要通過指標來完成,因此常被用作考察重點。

本文中的單向連結串列節點定義如下:

struct ListNode

{

int value;

ListNode *next;

ListNode(int x):value(x),next(NULL);

};

1、翻轉連結串列:定義一個函式,輸入一個連結串列的頭結點,反轉連結串列並輸出頭結點。

ListNode *reverselist(ListNode *p)

{

ListNode *pReversedHead = NULL;

ListNode

 *pNode = p;

ListNode *pPrev = NULL;

ListNode *pNext;

while (pNode != NULL)

{

pNext = pNode->next;

if (pNext == NULL)    pReversedHead =pNode;

pNode->next = pPrev;

pPrev = pNode;

pNode = pNext;

}

return pReversedHead;

}

2、從尾到頭輸出連結串列(通過遞迴或者棧來實現,遞迴的本質就是棧,遞迴在連結串列非常長的時候,有可能造成函式呼叫棧溢位)

void PrintListReversely(ListNode *head){
stack<ListNode *> node;
ListNode *p = head;
while (p!=NULL)
{
node.push(p);
p = p->next;
}
while (!node.empty())
{
p = node.top();
cout<<p->value<<endl;
node.top();
}
}

3、合併兩個有序連結串列

ListNode *merge(ListNode *head1ListNode *head2){

    if (head1 == NULL)

         return head2;

    else

    {

         if (head2 == NULL)

             return head1;

    }

    ListNode *p=NULL;

    for (; head1 != NULL&&head2 != NULL; p = p->next){

         if (head1->value > head2

->value){

             p = head2;

             head2 = head2->next;

         }

         else{

             p = head1;

             head1 = head1->next;

         }

    }

    p->next = head1!= NULL ? head1:head2;

    /*遞迴法

    if (head1->value > head2->value){

         p = head2;

         p->next = merge(head1,head2->next);

    }

    else{

         p = head1;

         p->next = merge(head1->next,head2);

    }

    */

    return p;

}

4、找出連結串列倒數第K個數

ListNode *findKthend(ListNode *head, unsigned int k){
if (head == NULL || k == 0) return NULL;
ListNode *first=head;        
ListNode *second=head;
for (int i = 1; i < k; i++){
if (first->next != NULL){
first = first->next;
}
else
{
return NULL;
}
}
while (first->next!=NULL)
{
first = first->next;
second = second->next;
}
return second;
}

5、刪除連結串列倒數第K個數並返回頭結點(思路:找出倒數k+1個節點並使其指向倒數k-1個節點)

 ListNode* removeNthFromEnd(ListNode* head, int k) {

            ListNode* dummy=new ListNode(-1);
            dummy->next=head;
            ListNode* record=dummy;
            for (int i = 0; i < k; i++){
if (first->next != NULL){
first = first->next;
}
else
{
return NULL;
}
}
            ListNode* result=dummy;
            while(record->next){
                result=result->next;
                record=record->next;
            }
            result->next=result->next->next;
            return dummy->next;
        }

6、從無頭單鏈表中刪除節點

void deleterandnode(ListNode *p){
if (p== NULL)
return;
ListNode *pNext = p->next;
if (pNext != NULL){
p->value = pNext->value;
p->next = pNext->next;
delete pNext;
}
}

7、新增元素

void addtotail(ListNode **L, int value){
ListNode pNode=ListNode(value);
ListNode *p = &pNode;
if (*L == NULL){
*L = p;
}
else{
ListNode *pnode = *L;
while (pnode->next!=NULL){
pnode = pnode->next;
}
pnode->next = p;
}

}

8、判斷是否有環最好的方法是時間複雜度O(n),空間複雜度O(1) 的。設定兩個指標,一個快一個慢,快的指標每次走兩步,慢的指標每次走一步,如果快指標和慢指標相遇,則說明有環。

bool hascycle(ListNode *head){

i
ListNode *slow = head, *fast = head;
while (fast&&fast->next) //考慮節點個數為奇數和偶數的情況
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast) return true;
}
return false;
}
9、判斷一個單鏈表的環入口,如果沒有環,則返回NULL;(下面為我複製的 各位可以自己畫個示意圖理解下)

設整個連結串列長L,入口環與相遇點距離為x,起點到環入口點的距離為a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)為相遇點到環入口點的距離,由此可知,從連結串列頭到環入口點等於(n-1)迴圈內環+相遇點到環入口點,於是我們從連結串列頭、與相遇點分別設一個指標,每次各走一步,兩個指標必定相遇,且相遇第一點為環入口點。程式描述如下:

ListNode *FindLoopPort(ListNode *head){
if (head == NULL || head->next == NULL){
return NULL;
}
ListNode *slow = head, *fast = head;
while (fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast) break;
}
if (fast != slow) returnNULL;
slow = head;
while (slow != fast){
slow = slow->next;
fast = fast->next;
}
return fast;
}