1. 程式人生 > >連結串列相關面試題

連結串列相關面試題

連結串列的基本操作在連結的部落格中已經有實現https://blog.csdn.net/Damn_Yang/article/details/83689944

下面我們來看看連結串列相關的面試題,非常重要

下面的題目都會一個一個實現

#include<stdio.h>
typedef int DataType;

typedef struct Node
{
	DataType data;
	struct Node * next;
}Node, *pNode, LinkList, *pLinkList;

//比較順序表和連結串列的優缺點 

//從尾到頭列印連結串列
void PrintLinkTailToHead(pLinkList ppl, stack* s);

//刪除一個無頭單鏈表的非尾結點
void RmoveNodeNotTail(pLinkList *ppl, pNode pos);

//用連結串列實現約瑟夫環
void JosephCircle(pLinkList *ppl, int k);

//在無頭單鏈表的一個節點前插入一個節點(不能遍歷連結串列)
void InsertNodeNotHead(pLinkList *ppl, DataType data, pNode pos);

//連結串列的逆置
void Reverselist(pLinkList *ppl);

//連結串列的氣泡排序
void BubbleSort(pLinkList *ppl);

//合併兩個有序的連結串列,合併後依然有序
pNode Merge(pLinkList* list1, pLinkList* list2);

//合併兩個有序的連結串列(遞迴版本)
pNode Merge_R(pLinkList list1, pLinkList list2);

//求單鏈表中間結點
pNode FindMidNode(pLinkList plist);

//求單鏈表倒數第K個結點
pNode FindLastKNode(pLinkList plist, int k);

//判斷連結串列是否帶環,帶環返回相遇點,不帶環返回NULL
pNode IsCircle(pLinkList plist);

//求環的長度
int GetCircleLength(pNode meet);

//求環的入口點
pNode GetCircleEntryNode(pLinkList plist, pNode meet);

//判斷連結串列是否相交(假設連結串列不帶環),相交返回1,不相交返回0
int IsCross(pLinkList plist1, pLinkList plist2);

//求兩個連結串列的交點
pNode GetMeetNode(pLinkList plist1, pLinkList plist2);

//求兩個連結串列中相同的資料(交集)
void UnionSet(pLinkList plist1, pLinkList plist2);

1.比較順序表和連結串列的優缺點 

1.順序表:
原理:順序表儲存是將資料元素放到一塊連續的記憶體儲存空間,存取效率高,速度快。但是不可以動態增加長度
優點:儲存速度高效,可以通過下標來直接儲存,建立而簡單。
缺點:1.插入和刪除比較慢,2.不可以增長長度
2.連結串列:
原理:連結串列儲存是在程式執行過程中動態的分配空間,只要儲存器還有空間,就不會發生儲存溢位問題
優點:插入刪除比較快,可以用不連續的空間
缺點:查詢速度慢,因為需要遍歷。

2.從尾到頭列印單鏈表

//2.從尾到頭列印連結串列
void PrintLinkTailToHead(pLinkList ppl)
{
	pNode tail = NULL;
	while (ppl != tail)
	{
		pNode cur = ppl;
		while (cur->next != tail)
		{
			cur = cur->next;
		}
		printf("%d ", cur->data);
		tail = cur;
	}
}

3.刪除一個無頭單鏈表的非尾結點

//3.刪除一個無頭單鏈表的非尾結點
void RmoveNodeNotTail(pLinkList *ppl, pNode pos)
{
	pNode del = NULL;
	pos->data = pos->next->data;//複製資料
	del = pos->next;//改變要刪除的節點
	pos->next = del->next;//刪除下一個節點
	free(del);
	del = NULL;
}

4.用連結串列實現約瑟夫環

//4.用連結串列實現約瑟夫環
void JosephCircle(pLinkList *ppl, int k)
{
	pNode tmp = *ppl;
	pNode cur = *ppl;
	pNode del = NULL;
	//將單鏈表弄成一個環
	while (tmp->next)
	{
		tmp = tmp->next;
	}
	tmp->next = *ppl;

	while (cur != cur->next)
	{
		int count = k;
		//走k-1步,清除一個
		while (--count)
		{
			cur = cur->next;
		}
		del = cur->next;
		printf("刪除:%d \n", cur->data);
		cur->data = cur->next->data;//複製資料
		cur->next = del->next;//刪除下一個節點
		free(del);
		del = NULL;
	}
	printf("剩餘:%d\n", cur->data);
}

5.在無頭單鏈表的一個節點前插入一個節點(不能遍歷連結串列)

//5.在無頭單鏈表的一個節點前插入一個節點(不能遍歷連結串列)
void InsertNodeNotHead(pLinkList *ppl, DataType data, pNode pos)
{
	pNode newNode = NULL;
	newNode = BuyNode(pos->data);
	newNode->next = pos->next;
	pos->next = newNode;
	pos->data = data;
}

6.連結串列的逆置

//6.連結串列的逆置
void Reverselist(pLinkList *ppl)
{
	pNode cur = *ppl;
	pNode tmp = NULL;
	pLinkList head = NULL;
	if (*ppl == NULL || (*ppl)->next)
	{
		return;
	}
	while (cur)
	{
		tmp = cur->next;//儲存下一個節點,否則會丟失連結串列後面的內容
		cur->next = head;
		head = cur;
		cur = tmp;
	}
	*ppl = head;
}

7.連結串列的氣泡排序

//7.連結串列的氣泡排序
void BubbleSort(pLinkList *ppl)
{
	pNode cur = *ppl;
	pNode tail = NULL;
	while (cur != NULL)//趟數
	{
		while (cur->next != NULL)//比較次數
		{
			if (cur->data > cur->next->data)
			{
				DataType tmp = cur->data;
				cur->next = cur->next->data;
				cur->next->data = tmp;
			}
			cur = cur->next;
		}
	}
	tail = cur;
	cur = *ppl;
}

8.合併兩個有序的連結串列,合併後依然有序

//8.合併兩個有序的連結串列,合併後依然有序
pNode Merge(pLinkList list1, pLinkList list2)
{
	pLinkList newhead = NULL;
	//一個連結串列為空,一個不為空
	if (list1 == NULL)
	{
		return list2;
	}
	if (list2 == NULL)
	{
		return list1;
	}
	//兩個連結串列都為空
	if ((list1 == NULL) && (list2 == NULL))
	{
		return NULL;
	}
	if (list1->data < list2->data)
	{
		newhead = list1;
		list1 = list1->next;
	}
	else
	{
		newhead = list2;
		list2 = list2->next;
	}
	//儲存連結串列最後一個節點的位置
	pNode tail = newhead;
	//依次比較尾插
	while ((list1 != NULL) && (list2 != NULL))
	{
		if (list1->data < list2->data)
		{
			tail->next = list1;
			list1 = list1->next;
		}
		else
		{
			tail->next = list2;
			list2 = list2->next;
		}
		tail = tail->next;
	}
	//將剩餘的節點插入到新連結串列中
	if (list1 == NULL)
	{
		tail->next = list2;
	}
	else if (list2 == NULL)
	{
		tail->next = list1;
	}
	return newhead;
}

9.合併兩個有序的連結串列(遞迴版本)

//9.合併兩個有序的連結串列(遞迴版本)
pNode Merge_R(pLinkList list1, pLinkList list2)
{
	pLinkList newhead = NULL;
	if (list1 == NULL)
	{
		return list2;
	}
	if (list2 == NULL)
	{
		return list1;
	}
	if ((list1 == NULL) && (list2 == NULL))
	{
		return NULL;
	}
	if (list1->data < list2->data)
	{
		newhead = list1;
		newhead->next = Merge_R(list1->next, list2);
	}
	else
	{
		newhead = list2;
		newhead->next = Merge_R(list2->next, list1);
	}
	return newhead;
}

10.求單鏈表中間結點

//10.求單鏈表中間結點
pNode FindMidNode(pLinkList plist)
{
	pNode first = plist;
	pNode second = plist;
	//連結串列沒有節點或者只有一個節點返回
	if ((plist == NULL) || (plist->next == NULL))
	{
		return plist;
	}
	while ((second != NULL) && (second->next != NULL))
	{
		second = second->next->next;
		first = first->next;
	}
	return first;
}

11.求單鏈表倒數第K個結點

//11.求單鏈表倒數第K個結點
pNode FindLastKNode(pLinkList plist, int k)
{
	pNode slow = plist;
	pNode fast = plist;
	if (plist == NULL)
	{
		return plist;
	}
	//快指標走k-1步
	while (--k)
	{
		if (fast == NULL)
		{
			return NULL;
		}
		fast = fast->next;
	}
	//快慢指標一起走,快的到終點,慢指標則指向倒數第k個結點
	while (fast->next != NULL)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

12.判斷連結串列是否帶環,帶環返回相遇點,不帶環返回NULL

//12.判斷連結串列是否帶環,帶環返回相遇點,不帶環返回NULL
pNode IsCircle(pLinkList plist)
{
	pNode slow = plist;
	pNode fast = plist;
	if (plist == NULL)
	{
		return plist;
	}
	while ((fast != NULL) && (fast->next != NULL))
	{
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow)
		{
			return slow;
		}
	}
	return NULL;
}

13.求環的長度

//13.求環的長度
int GetCircleLength(pNode meet)
{
	pNode cur = meet->next;
	int len = 1;
	while (cur != meet)
	{
		len++;
		cur = cur->next;
	}
	return len;
}

14.求環的入口點

//14.求環的入口點
pNode GetCircleEntryNode(pLinkList plist, pNode meet)
{
	pNode cur = plist;
	if (plist == NULL)
	{
		return NULL;
	}
	while (cur != meet)
	{
		cur = cur->next;
		meet = meet->next;
	}
	return cur;
}

15.判斷連結串列是否相交(假設連結串列不帶環),相交返回1,不相交返回0

//15.判斷連結串列是否相交(假設連結串列不帶環),相交返回1,不相交返回0
int IsCross(pLinkList plist1, pLinkList plist2)
{
	pNode cur1 = plist1;
	pNode cur2 = plist2;
	if ((plist1 == NULL) && (plist2 == NULL))
	{
		return 0;
	}
	while (cur1->next != NULL)
	{
		cur1 = cur1->next;
	}
	while (cur2->next != NULL)
	{
		cur2 = cur2->next;
	}
	if (cur1 == cur2)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

16.求兩個連結串列的交點

//16.求兩個連結串列的交點
pNode GetMeetNode(pLinkList plist1, pLinkList plist2)
{
	int len1 = 0;
	int len2 = 0;
	int gaps = 0;
	pNode cur1 = plist1;
	pNode cur2 = plist2;
	while (cur1)//求連結串列一的長度
	{
		cur1 = cur1->next;
		len1++;
	}
	while (cur2)//求連結串列二的長度
	{
		cur2 = cur2->next;
		len2++;
	}
	cur1 = plist1;//長連結串列
	cur2 = plist2;//短連結串列
	gaps = len1 - len2;
	if (len1 < len2)
	{
		cur1 = plist2;
		cur2 = plist1;
	}
	//長連結串列走gaps步
	while (gaps--)
	{
		cur1 = cur1->next;
	}
	//一起走
	while (cur1 != cur2)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur1;
}

17.求兩個連結串列中相同的資料(交集)

//17.求兩個連結串列中相同的資料(交集)
void UnionSet(pLinkList plist1, pLinkList plist2)
{
	if ((plist1 == NULL) || (plist2 == NULL))
	{
		return;
	}
	while ((plist1 != NULL) && (plist2 != NULL))
	{
		if (plist1->data < plist2->data)
		{
			plist1 = plist1->next;
		}
		else if (plist1->data > plist2->data)
		{
			plist2 = plist2->next;
		}
		else
		{
			printf("%d ", plist1->data);
			plist1 = plist1->next;
			plist2 = plist2->next;
		}
	}
}