1. 程式人生 > >【連結串列面試題】【進階】

【連結串列面試題】【進階】

1、查詢倒數第 k 個連結串列


題目描述:給定一個單向連結串列 List ,要你設計演算法找出倒數第 K 個結點並列印

struct ListNode
{
    DataType m_Value;
    ListNode* m_pNext;
};
ListNode* FindKthToTail(ListNode* pHead);

首先我自己的思路:




1、首先我們能想到的是假如給定的是一個雙向連結串列就好了,我們可以從連結串列的尾部向前遍歷找到倒數第 K 個結點,但是對於我們這道題顯然是行不通的

2、根據連結串列的特性我們能想到的是,連結串列節點數假設為 n ,要找倒數第 K 個結點,則我們只需從前往後遍歷連結串列,走 (n - k + 1) 步就可以得到倒數第 K 個結點;至於連結串列總結點數,我們可以遍歷連結串列得出::為了實現只遍歷一次連結串列就得到倒數第 K 個結點,我們可以定義兩個指標第一個指標從頭節點遍歷走 (k - 1) 步,從第 K 步開始兩個指標同步向後遍歷,當快指標到達連結串列的尾節點是,慢指標剛好是倒數第 K 個結點;基於這種思路我們可以寫出簡單的程式碼;如下:


#include "SList.h"

/*
* struct ListNode
{
    DataType m_Value;
    ListNode* m_pNext;
};
*/
ListNode* FindKthToTail(ListNode* pHead, unsigned int k)
{
    if(pHead == nullptr || k == 0)
        return nullptr;
    ListNode* pFast = pHead;
    ListNode* pSlow = pHead;
    while(--k && pFast->m_pNext != nullptr)
        pFast = pFast->m_pNext;
    while(pFast->m_pNext != nullptr)
    {
        pFast = pFast->m_pNext;
        pSlow = pSlow->m_pNext;
    }
    cout << pSlow->m_Value << endl;
    return pSlow;
}

void FindKthToTailTest()
{
    cout << ">>查詢連結串列倒數第 K 個結點" << endl;
    ListNode* pHead;
    SListInit(&pHead);
    SListPushBack(&pHead, 1);
    SListPushBack(&pHead, 2);
    SListPushBack(&pHead, 4);
    SListPushBack(&pHead, 7);
    SListPushBack(&pHead, 3);
    SListPushBack(&pHead, 5);
    SListPushBack(&pHead, 9);
    SListPushBack(&pHead, 8);
    SListPushBack(&pHead, 12);
    cout << "倒數第 3 個結點 : " << FindKthToTail(pHead, 3) << endl;
    SListPrint(pHead);
}
int main()
{
    FindKthToTailTest();
    return 0;
}

執行結果:

解答這道題需注意的是:

1、輸入的連結串列為空

2、連結串列總結點數少於 K 或者連結串列的總結點數 K== 0 

優化後的程式碼為:

#include "SList.h"

/*
* struct ListNode
{
    DataType m_Value;
    ListNode* m_pNext;
};
*/

ListNode* __FindKthToTail(ListNode* pHead, unsigned int k)
{
    if(pHead == nullptr || k == 0)
        return nullptr;
    ListNode* pFast = pHead;
    ListNode* pSlow = pHead;
    for(unsigned int i = 0; i < k-1; ++i)
    {
        if(pFast->m_pNext != nullptr)
        {
            pFast = pFast->m_pNext;
        }
        else
        {
            return nullptr;
        }
    }
    while(pFast->m_pNext != nullptr)
    {
        pSlow = pSlow->m_pNext;
    }
    return pSlow;
}

void __FindKthToTailTest()
{
    cout << ">>優化版本:" << endl;
    cout << ">>查詢連結串列倒數第 K 個結點:" << endl;
    ListNode* pHead;
    SListInit(&pHead);
    SListPushBack(&pHead, 1);
    SListPushBack(&pHead, 2);
    SListPushBack(&pHead, 4);
    SListPushBack(&pHead, 7);
    SListPushBack(&pHead, 3);
    SListPushBack(&pHead, 5);
    SListPushBack(&pHead, 9);
    SListPushBack(&pHead, 8);
    SListPushBack(&pHead, 12);
    cout << "倒數第 3 個結點 : " << __FindKthToTail(pHead, 3) << endl;
    SListPrint(pHead); 
}
int main()
{
    //FindKthToTailTest();
    __FindKthToTailTest();
    return 0;
}
#include "SList.h"

/*
* struct ListNode
{
    DataType m_Value;
    ListNode* m_pNext;
};
*/

ListNode* __FindKthToTail(ListNode* pHead, unsigned int k)
{
    if(pHead == nullptr || k == 0)
        return nullptr;
    ListNode* pFast = pHead;
    ListNode* pSlow = pHead;
    for(unsigned int i = 0; i < k-1; ++i)
    {
        if(pFast->m_pNext != nullptr)
        {
            pFast = pFast->m_pNext;
        }
        else
        {
            return nullptr;
        }
    }
    while(pFast->m_pNext != nullptr)
    {
        pSlow = pSlow->m_pNext;
    }
    return pSlow;
}

void __FindKthToTailTest()
{
    cout << ">>優化版本:" << endl;
    cout << ">>查詢連結串列倒數第 K 個結點:" << endl;
    ListNode* pHead;
    SListInit(&pHead);
    SListPushBack(&pHead, 1);
    SListPushBack(&pHead, 2);
    SListPushBack(&pHead, 4);
    SListPushBack(&pHead, 7);
    SListPushBack(&pHead, 3);
    SListPushBack(&pHead, 5);
    SListPushBack(&pHead, 9);
    SListPushBack(&pHead, 8);
    SListPushBack(&pHead, 12);
    cout << "倒數第 3 個結點 : " << __FindKthToTail(pHead, 3) << endl;
    SListPrint(pHead); 
}
int main()
{
    //FindKthToTailTest();
    __FindKthToTailTest();
    return 0;
}

2、反轉連結串列




ListNode* ReverseList(ListNode* pHead)
{
    if(pHead == NULL)
    {
        return NULL;
    }
    if(pHead->next == NULL)
    {
        return pHead;
    }
    ListNode* pCur = pHead->next;
    ListNode* pPrev = pHead;
    ListNode* pNext = pCur->next;
    while(pCur->next)
    {
        pNext = pCur->next;
        pCur->next = pPrev;
        pPrev = pCur;
        pCur = pNext;
    }
    pCur->next = pPrev;
    //最後一個節點的指向它的前驅
    pHead->next = NULL;
    //最後一個結點的下一個結點置 NULL
    return pCur;
}

棧實現

ListNode* ReverseList(ListNode* pHead)
{
    if (pHead == nullptr || pHead->next == nullptr)
    {
        return pHead;
    }
    stack<ListNode*> node;
    ListNode* pCur = pHead;
    while(pCur->next)    //連結串列不為空入棧
    {
        node.push(pCur);
        pCur = pCur->next;
    }
    ListNode* pReversedNode = pCur;    //反轉後新連結串列的頭節點就是棧頂元素
    while(!node.empty())
    {
        pCur->next = node.top();
        pCur = pCur->next;
        node.pop();
    }
    pCur->next = NULL;
    return pReversedNode;
}

遞迴實現

ListNode* ReverseList(ListNode* pHead)
{
    if(pHead == NULL || pHead->next == NULL)
        return pHead;
    ListNode* pReversedNode = ReverseList(pHead->next);
    pHead->next->next = pHead;    //反轉節點
    phead = NULL;    //第一個節點反轉後置下一個為 NULL
    return pReversedNode;
}

從尾到頭列印連結串列

遞迴實現


void ReverseList(ListNode* pHead)
{
    if(pHead != NULL)
    {
        if(pHead->next != NULL)
        {
            ListNode* pReversedNode = ReverseList(pHead->next);
        }
        cout << pReversedNode->data;
    }
}

 

棧實現


j僅僅列印連結串列的值,不修改連結串列結構

先遍歷連結串列,再列印

void ReverseList(ListNode* pHead)    //先進後出利用棧的特性
{
    if(pHead == NULL || pHead->next == NULL);
        return phead;
    stack<ListNode*> node;    
    ListNode* pCur = pHead;
    while(pCur->next)    //pCur 一直遍歷直到 pCur->next == NULL,入棧結束
    {
        node.push(pCur);
        pCur = pCur->next;
    }
    while(!node.empty())    //棧不為空,pop 出棧並且列印
    {
        pCur = node.top();
        cout << pCur->data;
        node.pop();
    }
}

 

3、找出給定連結串列的公共結點


題目描述:給定兩個連結串列,找出它們的第一個公共節點::分三步

1、分別計算兩個連結串列的長度

2、比較出較長的那一個連結串列,長出多少,較長的連結串列從頭開始向後遍歷多少步

3、找公共節點(找第一個公共結點、ps:找出現的公共結點)


程式碼如下:SList.h


/*************************************************************************
	> File Name: SList.h
	> Author: 
	> Mail: 
	> Created Time: Tue Oct 23 20:19:58 2018
 ************************************************************************/

#pragma once

#include <iostream>

using namespace std;

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DataType;

//節點結構體
typedef struct ListNode
{
    DataType m_Value;
    struct ListNode* m_pNext;
}ListNode;
//查詢,找到了返回地址,沒有找到返回NULL

ListNode* SListFind(ListNode* p, DataType data)//不改變連結串列的值,為 ListNode*
{
    ListNode* cur = p;
    for(cur = p; cur != NULL; cur = cur->m_pNext)
    {
        if(cur->m_Value == data)
        {
            return cur;//返回地址
        }
    }
}
/*
void SListInsert(ListNode* *p,ListNode* pos, DataType m_Value)
{
    if(*p == pos)
    {
        SListPushFront(p, m_Value);
        return;
    }
    ListNode* newNode = CreatNode(m_Value);
    ListNode* cur = *p;
    while(cur->m_pNext != pos)
    {
        cur->m_pNext = newNode;
        newNode->m_pNext = pos;
        cur = cur->m_pNext;
    }


}
*/
void SListInit(ListNode* *p)
{
    assert(p != NULL);
    *p = NULL;

}
void SListDestroy(ListNode* *p)
{
    assert(p != NULL);

}
static ListNode* CreatNode(DataType m_Value)
{
    ListNode* pNode = (ListNode *)malloc(sizeof(ListNode));
    pNode->m_Value = m_Value;
    pNode->m_pNext = NULL;
    return pNode;
}
//尾刪
void SListPopBack(ListNode* *p)
{
    assert(p != NULL);
    assert(*p != NULL);//判斷連結串列是否為空
    if((*p)->m_pNext == NULL)
    {
        free(p);
        p = NULL;
        return ;
    }
    //連結串列中至少有兩個
    ListNode* cur = *p;
    while(cur->m_pNext != NULL)
    {
        //free(cur->m_pNext);
        cur = cur->m_pNext;
    }
    free(cur->m_pNext);
    cur->m_pNext = NULL;
}
//頭刪

//尾插
void SListPushBack(ListNode* *p, DataType m_Value)
{
    ListNode* pNode = CreatNode(m_Value);/* 申請節點,賦值且插入 */
    assert(p != NULL);//傳入地址不為空
    if(*p == NULL)/* 連結串列為空 */
    {
        *p = pNode;
        return ;
    }
    //找連結串列中的最後一個節點
    //ListNode* pNode = CreatNode(m_Value);//申請節點,賦值且插入
    ListNode* cur = *p;
    while(cur->m_pNext != NULL)
    {
        cur = cur->m_pNext;
    }
    cur->m_pNext = pNode;
}
//頭插
void SListPushFront(ListNode* *p, DataType m_Value)
{
    assert(p != NULL);
    //assert(*p != NULL);
    ListNode* pNode = (ListNode *)malloc(sizeof(ListNode));
    pNode->m_Value = m_Value;
    pNode->m_pNext = NULL;
    //pNode->m_pNext = *p;
    *p = pNode;
}

void SListPrint(ListNode* p)
{
    ListNode* cur = p;
    while(cur != NULL)
    {
        printf("%d ", cur->m_Value);
        cur = cur->m_pNext;
    }
    printf("\n");
}

void SListTest()
{
    ListNode* pFirst;

    SListInit(&pFirst);//初始化
    //連結串列頭插
    //SListPushFront(&pFirst, 0);
    SListPushFront(&pFirst, 1);
    SListPushFront(&pFirst, 2);
    SListPushFront(&pFirst, 3);
    SListPushFront(&pFirst, 4);
    SListPushFront(&pFirst, 5);
    SListPrint(pFirst);
    //連結串列尾插
    SListPushBack(&pFirst, 4);
    SListPushBack(&pFirst, 3);
    SListPushBack(&pFirst, 2);
    SListPushBack(&pFirst, 1);
    SListPrint(pFirst);//列印尾插之後的連結串列
    SListPopBack(&pFirst);
    SListPopBack(&pFirst);
    SListPopBack(&pFirst);
    SListPrint(pFirst);
    //SListInsert();
    //CreatNode(m_Value);


}
ListNode* SList(ListNode* pHead1)
{
    //if(pHead == pHead1)
    //{
    SListInit(&pHead1);
    SListPushBack(&pHead1, 0);
    SListPushBack(&pHead1, 1);
    SListPushBack(&pHead1, 2);
    SListPushBack(&pHead1, 3);
    SListPushBack(&pHead1, 4);
    SListPushBack(&pHead1, 5);
    SListPushBack(&pHead1, 6);
    SListPushBack(&pHead1, 7);
    SListPushBack(&pHead1, 8);
    SListPushBack(&pHead1, 9);
    //return pHead1;
    //}
    /*
    else
    {
        ListNode* pHead2;
        SListInit(&pHead2);
        SListPushFront(&pHead2, 0);
        SListPushFront(&pHead2, 1);
        SListPushFront(&pHead2, 2);
        SListPushFront(&pHead2, 3);
        SListPushFront(&pHead2, 4);
        SListPushFront(&pHead2, 5);
        SListPushFront(&pHead2, 6);
        SListPushFront(&pHead2, 7);
        SListPushFront(&pHead2, 8);
        SListPushFront(&pHead2, 9);
    }*/
    return pHead1;
}

找公共結點函式:FindSameNode.cpp

/*
struct ListNode {
	int data;
	struct ListNode *next;
};*/

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(pHead1 == nullptr || pHead2 == nullptr)
            return nullptr;
        size_t len1 = 0;
        size_t len2 = 0;
        ListNode* pCur1 = pHead1;
        ListNode* pCur2 = pHead2;
        //獲取連結串列長度
        while(pCur1)
        {
            
            pCur1 = pCur1->next;
            len1++;
        }
        while(pCur2)
        {
            
            pCur2 = pCur2->next;
            len2++;
        }
        pCur1 = pHead1;
        pCur2 = pHead2;
        //比較哪一個連結串列較長
        size_t sublen = 0;
        if(len1 < len2)
        {
            sublen = len2 - len1;
            while(sublen--)
                pCur2 = pCur2->next;
        }
        
        if(len1 > len2)
        {
            sublen = len1 - len2;
            while(sublen--)
                pCur1 = pCur1->next;
        }
        //找公共結點
        while(pCur1 != nullptr && pCur2 != nullptr)
        {
            if(pCur1 == pCur2)
                break;
            pCur1 = pCur1->next;
            pCur2 = pCur2->next;
        }
        return pCur1;
    }
};