【連結串列面試題】【進階】
阿新 • • 發佈:2018-12-27
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;
}
};