1. 程式人生 > >連結串列面試題小結(待完成)

連結串列面試題小結(待完成)

以下程式碼使用的公共結構:

連結串列節點:

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

輔助函式:

//make a list
ListNode *makeList(int *arr, int n)
{
	if(arr == NULL || n <= 0)
		return NULL;
	ListNode *phead = new ListNode;
	phead->data = arr[0];
	phead->next = NULL;
	ListNode *ptr = phead;
	for(int i = 1; i < n; i++)
	{
		ListNode *node = new ListNode;
		node->data = arr[i];
		node->next = NULL;
		ptr->next = node;
		ptr = node;
	}
	return phead;
}

//print a list
void printList(ListNode *phead)
{
	ListNode *node = phead;
	while(node != NULL)
	{
		cout << node->data << "\t";
		node = node->next;
	}
	cout << endl;
}
一,從尾到頭列印連結串列

題目:給定一個單鏈表的頭節點,從尾到頭打印出該連結串列的所有節點。

解答:

  1. 使用遞迴,在遞迴中,若節點有後續節點,則先處理後續節點,再列印本節點的資料
  2. 遞迴的本質就是使用棧,所以可以不是用遞迴,而是在遍歷連結串列的時候將連結串列資料壓入棧中,遍歷結束之後出棧就可以得到從尾到頭的所有節點值

程式碼:

/*******************************************************
**question 1
********************************************************/

//solution 1
void printListFromTail_1(ListNode *phead)
{
	if(phead == NULL) return;
	printListFromTail_1(phead->next);
	cout << phead->data << endl;
}

//solution
void printListFromTail_2(ListNode *phead)
{
	if(phead == NULL) return;
	stack<int> myStack;
	ListNode *node = phead;
	do
	{
		myStack.push(node->data);
		node = node->next;
	}while(node != NULL);

	while(myStack.size() > 0)
	{
		cout << myStack.top() << endl;
		myStack.pop();
	}
}

測試及其用例:

//test
/*********************************
arr[10] = {....}
arr[0] = {....}
arr[1] = {....}
arr = NULL
**********************************/
int main()
{
	int arr[1] = {1};
	ListNode *phead = makeList(arr, 1);
	printListFromTail_1(phead);
	printListFromTail_2(phead);
	return 0;
}

二,連結串列中的倒數第K個節點

題目:給出一個單鏈表的頭節點,寫一個函式獲得其倒數第K個節點

解答:

最簡單的做法是:先遍歷連結串列,獲得連結串列的長度n,然後再次遍歷連結串列,從頭節點開始的第n - K + 1即是所求節點。

優化以下的做法是:使用兩個指標,一個先向前遍歷K 個節點,另外一個指著頭節點,然後兩個指標一起向前遍歷連結串列直到連結串列結尾。這時後面一個節點所指的就是倒數第K個節點

程式碼:

/*******************************************************
**question 2
********************************************************/
ListNode *getKthNodeFromTail(ListNode *phead, int k)
{
	if(phead == NULL || k <= 0) return NULL;
	ListNode *pBegin = phead;
	ListNode *pEnd = phead;
	for(int i = 0; i < k - 1; i++)
	{
		if(pBegin->next != NULL)
			pBegin = pBegin->next;
		else
			return NULL;
	}

	while(pBegin->next != NULL)
	{
		pBegin = pBegin->next;
		pEnd = pEnd->next;
	}
	return pEnd;
}

測試及其用例:
//test
/*********************************
arr[10] = {....} k = 10
arr[10] = {....} k = 1
arr[10] = {....} k = 5
**********************************/
int main()
{
	int arr[1] = {1};
	ListNode *phead = makeList(arr, 1);
	getKthNodeFromTail(phead, 1);
	return 0;
}

三,反轉連結串列

題目:給定一個單鏈表的頭節點指標,請反轉該單鏈表,並返回反轉之後的單鏈表的頭節點指標

解答:使用兩個指標,反轉連結串列,這個比較簡單。

程式碼:

/*******************************************************
**question 3
********************************************************/
ListNode *reverseList(ListNode *phead)
{
	if(phead == NULL) return NULL;
	if(phead->next == NULL) return phead;
	ListNode *pBegin = phead->next;
	ListNode *pEnd = phead;
	pEnd->next = NULL;
	do
	{
		ListNode *pTemp = pBegin->next;
		pBegin->next = pEnd;
		pEnd = pBegin;
		pBegin = pTemp;
	}while(pBegin != NULL);
	return pEnd;
}

四,判斷單鏈表是否有環

五,合併兩個排序的連結串列

六,複雜連結串列的複製

七,兩個連結串列的第一個公共節點

題目:給定兩個單鏈表的頭節點,寫一個函式返回兩個單鏈表的第一個公共節點

解答:

由於是兩個單鏈表存在公共節點,那麼說明是Y型結構,第一個公共節點之後的節點都是公共節點,所以解決這個問題的方法就是利用這些公共節點。

  1. 將兩個連結串列從前到後遍歷,將他們的節點分別壓入兩個棧中,然後出棧比較,直到節點不一樣,說明上一個出棧的節點就是第一個公共子節點。時間複雜度是O(M + N),空間複雜度是O(M + N)
  2. 分別遍歷兩個節點,得到兩個節點的長度為:M、N。讓長的連結串列先往前走|M - N|步,然後兩個連結串列一起往前遍歷,直到兩個連結串列遇到的節點一樣,即為公共子節點

程式碼: