1. 程式人生 > >連結串列常見面試題-C語言實現

連結串列常見面試題-C語言實現

前面的部落格已經介紹過了實現連結串列的一些相關的功能,所以在這裡就不對連結串列多加解釋說明了。

對於連結串列相關面試題這裡解釋幾個有特點的題:

1.對於求查詢連結串列的中間結點,要求只能遍歷一次連結串列?

方式:使用兩個指標,一個快指標,一個慢指標,快指標走兩步慢指標走一步,這樣當快指標指向結尾空指標的時候,慢指標剛好指向中間結點。

圖示:


2.查詢連結串列倒數第K個結點要求只能遍歷一次連結串列?

方式:同樣運用快慢指標,先讓快指標走K-1次然後讓快慢指標一起走,當fast->next==NULL時,慢指標剛好指向倒數第K個結點,注意這裡的快指標還是一次走兩步。

圖示:


3判斷一個連結串列是否帶環,帶環求環的入口點?

方式:還是對快慢指標的運用,讓兩個指標一直走,如果相遇則帶環,否則不帶環。因為快慢的指標速度不一樣,如果帶環他們都上了環相當於跑圈,速度不一樣肯定會相遇,如果不帶環快指標肯定先指向NULL,也不會相遇。

圖示:


4.求環的入口點?

方式:還是使用快慢指標,不過這裡需要用到快慢指標在環上的相遇點,具體過程如下圖

圖示:


當然這個題的解法還有很多,大家可以自己思考一下其他的方法

5.判斷兩個連結串列是否相交,求交點?

方式:這裡還是對快慢指標的應用,具體過程如下圖


接下來給出所有問題的程式碼

首先給出標頭檔案List.h

#define _CRT_SECURE_NO_WARNINGS 1
#ifndef __LIST_H__
#define __LIST_H__

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

typedef int Datatype;

typedef struct Node
{
	Datatype data;
	struct Node *next;
}Node,*PLinklist;

void initList(PLinklist* pplist);//初始化頭指標
void pushback(PLinklist* pplist,Datatype);//尾插法
void showlist(PLinklist pplist);//正順列印結點
void destory(PLinklist* pplist);//釋放所開闢的結點
void _showlist(PLinklist pplist);//逆序列印
void delnotetail(PLinklist pos);//刪除無頭非尾結點
PLinklist find_list(PLinklist pplist, Datatype d);//查詢連結串列
void inertfrontnode(PLinklist pos, Datatype d);//無頭結點非尾結點前插入一個元素
void josephcycle(PLinklist pplist,Datatype  k);//約瑟夫環
void rever(PLinklist* pplist);//逆序連結串列
void bubble(PLinklist pplist);//排序連結串列
PLinklist Merge(PLinklist* pplist, PLinklist* pplists);//合併兩個有序連結串列和並後也為空
PLinklist findmiddle(PLinklist pplist);//查詢中間結點
void deletk(PLinklist pplist,int k);//查詢倒數第K個結點.只能遍歷一次離岸邊
PLinklist checkcircle(PLinklist pplist);//判斷一個連結串列帶不帶環,帶環返回兩個指標相遇點的地址
int getcirclelong(PLinklist meet);//如果有環求環的長度
PLinklist findenterpoint(PLinklist pplist, PLinklist meet);//如果帶環則找入口點,//meet為相遇點
PLinklist checklist(PLinklist pplist, PLinklist pplists);//判斷兩條連結串列是否相交,相交返回交點

#endif
接下來給出具體實現函式的檔案List.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"

void initList(PLinklist* pplist)
{
	*pplist = NULL;//pplist裡面放的是指標變數head的地址,*pplist就是指標變數地址的值,在這裡賦值為空
}
void pushback(PLinklist* pplist,Datatype d)
{
	Node *p = *pplist;//P裡面存的是頭指標的地址
	PLinklist cur= (PLinklist)malloc(sizeof(Node));//開闢第一個結點
	if (cur == NULL)
	{
		perror("usr malloc ");
		exit(EXIT_FAILURE);
	}
	memset(cur, 0, sizeof(Node));
	cur->data = d;
	cur->next = NULL;
	if (p== NULL)
	{
		*pplist = cur;//頭結點的地址給頭指標
	}
	else
	{
		while (p->next != NULL)
		{
			p= p->next;
		}
		p->next = cur;
	}
}


void showlist(PLinklist pplist)
{
	Node *cur = pplist;
	while (cur != NULL)
	{
		printf("%d--", cur->data);
		cur = cur->next;
	}
}
void destory(PLinklist* pplist)
{
	Node *cur = *pplist;//cur裡面存的頭指標的地址
	while (cur!= NULL)
	{
		Node *p = cur;//儲存第一個結點的地址
		cur = cur->next;//cur儲存第二個結點的地址
			free(p);//
	}
	*pplist = NULL;
}
void _showlist(PLinklist pplist)
{
	if (pplist == NULL)
		return;
	else if (pplist->next == NULL)
		printf("%d ", pplist->data);
	else
	{
		_showlist(pplist->next);
		printf("%d ", pplist->data);
	}
}
PLinklist find_list(PLinklist pplist, Datatype d)//查詢連結串列返回地址
{
	Node *cur = pplist;
	while (cur)
	{
		if (cur->data == d)
		{
			return cur;
		}
		cur = cur->next;
	}
	return;
}
void delnotetail(PLinklist pos)//刪除無頭非尾結點
{
	Node *cur = NULL;
	assert(pos->next);
	cur = pos->next;
		pos->data = cur->data;
		pos->next = cur->next;
		free(cur);
}
void inertfrontnode(PLinklist pos, Datatype d)
{
	assert(pos);
	Node *p = (PLinklist)malloc(sizeof(Node));
	if (p == NULL)
	{
		perror("use malloc");
		exit(EXIT_FAILURE);
	}
	p->data = pos->data;
	pos->data = d;
	p->next = NULL;
	p->next = pos->next;
	pos->next = p;
}
void josephcycle(PLinklist pplist, Datatype  k)//約瑟夫環
{
	Node *cur = pplist;
	Node *del = NULL;
	int count = 0;
	while (1)
	{
		count = k;
		if (cur == cur->next)//剩一個數字退出
		{
			break;
		}
		while (count-2>0)
		{
			cur = cur->next;
			--count;
		}
		printf("%d  ", cur->next->data);
		del = cur->next;
		cur->next = del->next;
		cur = cur->next;
		free(del);
		
	}
	printf("%d\n", cur->data);
}
void rever(PLinklist* pplist)//逆序連結串列
{
	Node *cur = *pplist;
	Node *tem = NULL;
	Node *head = NULL;
	if ((cur == NULL) || (cur->next == NULL))
	{
		return;
	}
	while (cur)
	{
		tem = cur->next;
		cur->next = head;
		head = cur;
		cur = tem;
	}
	*pplist = head;
}
void bubble(PLinklist pplist)//排序演算法
{
	int count = 1;
	Node *cur = pplist;
	Node *src = pplist;
	while (src->next != NULL)
	{
		count++;
		src = src->next;
	}
	int i = 0,j=0;
	for (i = 0; i < count; i++)
	{
		Node *cur = pplist;
		for (j = 0; j < count - 1 - i; j++)
		{
			int temp = 0;
			if ((cur->data)>(cur->next->data))
			{
				temp = cur->data;
				cur->data = cur->next->data;
				cur->next->data = temp;
			}
			cur = cur->next;
		}
	}
}
PLinklist Merge(PLinklist* pplist, PLinklist* pplists)//連結兩個有序連結串列
{
	Node  *cur1 = *pplist;
	Node  *cur2 = *pplists;
	Node  *head = NULL;
	Node  *tail = NULL;
	if (cur1 == NULL&&cur2 == NULL)//兩條都為空不合並
		return NULL;
	if (cur1 == cur2)
		return cur1;
	if (cur1 == NULL)
		return cur2;
	if (cur2 == NULL)
		return cur1;
	if (cur1->data > cur2->data)
	{
		head = cur1;
		cur2 = cur2->next;
		head->next = NULL;
		tail = head;
	}
	else
	{
		head = cur1;
		cur1 = cur1->next;
	}
	head->next = NULL;
	tail = head;
	while (cur1 != NULL&&cur2 != NULL)
	{
		if (cur1->data < cur2->data)
		{
			tail->next = cur1;
			cur1 = cur1->next;
		}
		else
		{
			tail->next = cur2;
			cur2 = cur2->next;
		}
		tail->next->next = NULL;
		tail = tail->next;
	}
	if (cur1 == NULL)
	{
		tail->next = cur2;
	}
	else
		tail->next = cur1;
	return head;
}
PLinklist findmiddle(PLinklist pplist)//查詢中間結點的元素
{
	Node *slow = pplist;
	Node *quic = pplist;
	while (quic != NULL&&quic->next != NULL)//這裡的順序很重要不然就會越界程式崩潰
	{
		quic = quic->next->next;
		slow = slow->next;
	}
	return slow;
}
void deletk(PLinklist pplist, int k)//刪除倒數第K個結點
{
	Node *first = pplist;
	Node *second = pplist;
	while (first->next != NULL)
	{
		if (--k <= 0)
		{
			second = second->next;//指向刪除的結點
		}
		first = first->next;
	}
	if (k <= 0)
	{
		Node *src = second->next;
		second->data = second->next->data;
		second->next = second->next->next;//
		free(src);
		/*second->next->next = NULL;*/
	}
}
PLinklist checkcircle(PLinklist pplist)//判斷一個連結串列是否帶環
{
	Node *slow = pplist;
	Node *fast = pplist;
	while (fast&&fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow)
		{
			return fast;
		}
	}
		return NULL;
}
int getcirclelong(PLinklist meet)//meet為入口點
{
	Node *cur = meet;
	int count = 0;
	do
	{
		count++;
		cur=cur->next;
	} while (cur != meet);
		return count;
}
PLinklist findenterpoint(PLinklist pplist,PLinklist meet)//meet為相遇點點
{
	Node *cur = pplist;
	while (meet != cur)
	{
		meet = meet->next;
		cur = cur->next;
	}
	return meet;
}
PLinklist checklist(PLinklist pplist, PLinklist pplists)//判斷兩條連結串列是否相交,相交返回交點
{
	Node *src = pplist;
	Node *dest = pplists;
	assert(pplist&&pplist);
	while (src->next != NULL)
	{
		src = src->next;
	}
	while (dest ->next!= NULL)
	{
		dest = dest->next;
	}
	if (src == dest)//相交了
	{
		Node *meet = NULL;
		src->next = pplists;//接成一個環
		meet = checkcircle(pplist);//找環指標的相遇地點
		Node *ret=findenterpoint(pplist, meet);//找環的入口點
		return ret;
	}
	else
	{
		return NULL;
	}
}
最後給出test.c檔案主要是測試函式和main函式
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//void test2()
//{
//	PLinklist head;
//	initList(&head);
//	int i = 0;
//	for (i = 1; i < 42; i++)
//	{
//		pushback(&head, i);
//	}
//	find_list(head, 41)->next = head;
//	josephcycle(head, 3);
//	/*showlist(head);*/
//}
void test()
{
	PLinklist head;//定義頭指標
	//PLinklist heads;//定義頭指標
	initList(&head);//將頭指初始化為空避免也指標的存在
	//initList(&heads);//將頭指初始化為空避免也指標的存在,測試合併連結串列所定義的
	pushback(&head, 1);
	pushback(&head, 2);
	pushback(&head, 3);
	pushback(&head, 4);
	pushback(&head, 5);
	pushback(&head, 6);
	//pushback(&heads, 7);
	//pushback(&heads, 8);
	//pushback(&heads, 9);
	showlist(head);
	printf("\n");
	Node *ret = findmiddle(head);
	printf("%d", ret->data);
	
	//showlist(heads);
	//printf("\n");
	//find_list(heads, 9)->next = find_list(head, 5);
	//Node *ptr=checklist(head, heads);
	//printf("%d", ptr->data);
	//Node *src=checkcircle(head);//檢查帶不帶環
	//printf("%d", src->data);
	//int ret=getcirclelong(src);
	//printf("%d", ret);
	//Node *dest=findenterpoint(head, src);//找入口點函式
	//printf("%d", dest->data);
	//showlist(head);
	//printf("\n");
	//deletk(head, 3);
	//pushback(&head, 8);
	//showlist(head);
	//printf("\n");
	//rever(&head);
	//showlist(findmiddle(head)->dtaa);
	//Node *x = findmiddle(head);//列印中間函式
	//printf("%d",x->data );
	//PLinklist pos=find_list(head, 4);
	//inertfrontnode(pos, 10);
	//test2();
	//delnotetail(pos);
	//bubble(head);
	/* printf("\n");*/
	 //showlist(Merge(&head, &heads));
	//showlist(heads);
	//_showlist(head);//逆序列印
	//destory(&head);
	//showlist(head);

}
int main()
{
	test();
	system("pause");
	return 0;
}
以上測試函式是我自己編寫時按自己想法測試的,比較凌亂,你可以根據自己的想法去測試!

有什麼問題歡迎留言一起交流學習!