笔试or面试——寻找单链表倒数第n个元素

在各种面试笔试中,单链表考察的最多。其中有一道经典的题目“寻找单链表倒数第n个元素”。

如果仅仅是这么问,大多数人想到这不是很简单么:先遍历整个链表获知整个链表长度L,然后再L-n+1个节点就是所求节点。

但很显然面试官不满足于这种问题,而是“一次遍历寻找单链表倒数第n个元素”。这样的话,该如何找到思路呢?

首先,我们来弄懂几个细节:

正序和逆序之间的关系:倒数第n个节点的位置=(链表长度-n+1)个节点所在位置。

比如说,链表为:3,4,5,6,7,8,9,我们要查找倒数5个节点,也就是正数第(7-5+1)=3节点处。到现在问题就变成了,如何合理的设置循环次数移动指针。

由于一个遍历链表时,我们通过指针只能控制是否到链表末尾(比如p->next!=NULL),因此我们考虑添加另外一个指针(p),通过设置这个指针的指向链表的不同位置,来控制另外一个指针(p1)移动的次数。因此思路为:

建立两个指针,第一个先走n步,然后第2个指针也开始走,两个指针步伐(前进速度)一致。当第一个结点走到链表末尾时,第二个节点的位置就是我们需要的倒数第n个节点的值。

代码如下:

#include
#include
#include

typedef int elemType;
typedef struct Node{//定义链接节点
	elemType element;
	Node* next;
}Node;
//1、初始化链表
void init(Node** pNode){
	*pNode = NULL;
	printf("------>链表初始化<------\n");
}
//1、创建链表
Node* create(Node* pHead){
	Node* p1;//代表新节点
	Node * p2;//代表尾节点
	p1 = p2 = (Node*)malloc(sizeof(Node));//申请节点
	if (p1 == NULL || p2 == NULL){
		printf("内存空间申请失败\n");
		exit(0);
	}
	memset(p1, 0, sizeof(Node));
	printf("输入节点值(非正数结束):");
	scanf_s("%d", &p1->element);//输入新节点
	p1->next = NULL;
	while (p1->element > 0){
		if (pHead == NULL){//空节点,接入表头
			pHead = p1;
		}
		else{
			p2->next = p1;//非空表,接入尾节点
		}
		p2 = p1;//重新让p2做尾节点
		p1 = (Node*)malloc(sizeof(Node));
		if (p1 == NULL || p2 == NULL){
			printf("内存分配失败\n");
			exit(0);
		}
		memset(p1, 0, sizeof(Node));
		printf("输入节点值(非正数结束):");
		scanf_s("%d", &p1->element);
		p1->next = NULL;

	}

	printf("链表创建成功\n");
	return pHead;
}

//打印链表
void print(Node* pHead){
	if (pHead == NULL){
		printf("链表为空\n");
	}
	else{
		while (pHead != NULL){
			printf("%d,", pHead->element);
			pHead = pHead->next;
		}
		printf("\n");
	}
}
//清空链表
void clear(Node* pHead){
	Node* pNext;
	if (pHead == NULL){
		printf("链表为空\n");
			return;
	}
	while (pHead->next != NULL){
		pNext = pHead->next;//保存下一个节点
		free(pHead);
		pHead = pNext;
	}
	printf("链表清空\n");
}
//链表长度
int size(Node* pHead){
	int size = 0;
	while (pHead != NULL){
		size++;
		pHead = pHead->next;
	}
	printf("链表长度%d\n", size);
	return size;
}
//链表是否为空
int isEmpty(Node* pHead){
	if (pHead == NULL){
		return 1;
	}
	printf("链表非空\n");
	return 0;
}
//获取指定位置的元素
elemType get(Node* pHead, int pos){
	int i = 0;
	if (pos < 1){
		printf("pos值不合法\n");
		return 0;
	}
	if (pHead == NULL){
		printf("链表为空\n");
		return 0;
	}
	while (pHead != NULL){
		++i;
		if (pos == i){
			break;
		}
		pHead = pHead->next;
	}
	if (i < pos){
		printf("pos值超出范围\n");
		return 0;
	}
	return pHead->element;
}

elemType *getAddr(Node* pHead, elemType x){
	if (NULL == pHead){
		printf("链表 为空\n");
		return NULL;
	}
	if (x < 0){
		printf("给定值x不合法");
		return NULL;
	}
	while ((pHead->element != x) && (pHead->next != NULL)){
		pHead = pHead->next;
		
	}
	if ((pHead->element != x) && pHead != NULL){
		printf("在链表中不存在值\n");
		return NULL;
	}
	if (pHead->element == x){
		printf("元素%d的地址为%x%x\n", x, &(pHead->element));
		
	}
	return &(pHead->element);
}
//修改指定位置的元素
int modify(Node* pNode, int pos, elemType x){
	Node* pHead;
	pHead = pNode;
	int i = 0;
	if (pNode == NULL){
		printf("modifyElem函数执行,链表为空\n");
		return 0;
	}
	if (pos < 1){
		printf("modifyElem函数执行,pos值非法\n");
		return 0;
	}
	while (pHead != NULL){
		++i;
		if (i == pos){
			break;

		}
		pHead = pHead->next;
	}
	if (i < pos){
		printf("getElement函数执行,pos值超出范围\n");
		return 0;
	}
	pNode = pHead;
	pNode->element = x;
	printf("modifyElem函数执行,修改%d出值为%d\n",pos,x);
	return 1;
}
//插入头节点
int insertHead(Node** pNode, elemType ele){
	Node* pInsert;
	pInsert = (Node*)malloc(sizeof(Node));
	pInsert->element = ele;
	pInsert->next = *pNode;
	*pNode = pInsert;//代表头节点
	printf("向表头插入元素:%d\n",ele);
	return 1;
}
//插入尾节点
int insertLast(Node** pNode, elemType ele){
	Node* pInsert;
	Node* pHead;
	Node* pTmp;

	pHead = *pNode;
	pTmp = pHead;
	pInsert = (Node* )malloc(sizeof(Node));
	memset(pInsert, 0, sizeof(Node));
	pInsert->element = ele;
	while (pHead->next != NULL){
		pHead = pHead->next;
	}
	pHead->next = pInsert;
	*pNode = pTmp;
	printf("向表尾插入元素:%d\n",ele);
		return 1;
}
//在指定位置插入元素
int insertMid(Node** pNode, int pos, elemType ele){
	Node* pHead;
	Node* nHead;
	pHead = *pNode;
	Node* pInsert;
	pInsert = (Node*)malloc(sizeof(Node));
	memset(pInsert, 0, sizeof(Node));
	pInsert->element = ele;	
	int i = 0;
	if (pNode == NULL){
		printf("链表为空\n");
		return 0;
	}
	if (pos < 1){
		printf("pos值非法\n");
		return 0;
	}
	while (pHead != NULL){
		++i;
		if (i == pos){
			break;

		}
		pHead = pHead->next;
	}
	if (i < pos){
		printf("pos值超出范围\n");
		return 0;
	}
	
	nHead = pHead->next;//保存后一个节点
	pInsert->next = pHead->next;
	pHead->next = pInsert;
	printf("插入中间节点\n");
	return 1;
}
//删除指定位置元素
int deleteMidEle(Node** pNode, int pos){
	Node* pHead;
	Node* de;
	pHead = *pNode;
	int i = 0;
	if (pNode == NULL){
		printf("pos值非法");
		return 0;
	}

	while (pHead != NULL){
		++i;
		if (i == (pos-1)){
			break;

		}
		pHead = pHead->next;
	}
	de = pHead->next;
	pHead->next=de->next;
	printf("删除元素成功\n");
	return 1;
}
//删除第一个节点
int delHead(Node** pNode){
	Node* pHead = *pNode;
	if (pNode == NULL){
		printf("链表为空,删除失败\n");
		return 0;
	}
	if (pHead->next == NULL){//只有一个节点,设置头指针为空
		printf("删除头节点成功\n");
		pHead = NULL;
		*pNode = pHead;
		return 1;
	}
	pHead = pHead->next;//设置原先头节点的下一个节点为头节点
	*pNode = pHead;
		printf("删除头节点成功\n");
		return 1;
}
//删除尾节点
int delLast(Node** pNode){
	Node* pHead = *pNode;
	Node* preDel=pHead;
	if (*pNode == NULL){
		printf("链表为空,删除失败\n");
		return 0;
	}
	while (pHead->next!= NULL){
		preDel = pHead;
		pHead = pHead->next;
	}
	printf("删除尾节点\n");
	
	free(pHead);
	preDel->next = NULL;
	return 1;

}
//逆序链表
void  reverse(Node** pNode){
	Node* current;
	Node* p;
	if (*pNode == NULL){
		return;
	}
	current = *pNode;//a1
	while (current->next!=NULL)
	{
		p = current->next;//a2
		current->next = p->next;//a1和a3链接,此时a1仍然为链表头节点
		p->next = (*pNode);//头插法,插入节点
		(*pNode) = p;//保持*pNode始终为头节点
	}
	printf("逆序链表\n");
}
/*
*判断链表中是否有环,同时设置两个指针,每个指针步长不一样,若有环则一定相遇
*/
int hasLoop(Node* pNode){
	int step1 = 1;
	int step2 = 2;
	Node* s1 =pNode;
	Node* s2 = pNode;
	while (s1 != NULL&&s2 != NULL&& s2->next != NULL){
		s1 = s1->next;
		if (s2->next != NULL){
			s2 = s2->next;
			if (s2->next != NULL){
				
				if (s2 == s1){
					printf("hasLoop函数执行,存在环\n");
					
					return 1;
				}
			}
			
		}

	}
	printf("hasLoop函数执行,不存在环\n");
	return 0;
}

int hasLoop2(Node* pNode){
	Node* s1 = pNode;//节点1
	int pos1 = 0;//记录节点1的步数
	while (s1!=NULL){
		Node* s2 = pNode;//节点2
		int pos2 = 0;//记录节点2 的步数
		pos1++;//节点1步数+1
		while (s2!=NULL){
			pos2++;//节点2步数+1
			
			if (s1 == s2){//到达相同节点时
				if (pos1 == pos2){//步数相同则不存在环
					continue;
				}
				else{
					printf("hasLoop2函数执行,存在环\n");//步数不同则存在环
					
					return 1;
				}
			}
			s2 = s2->next;
		}
		s1 = s1->next;
	}
	
	printf("hasLoop2函数执行,不存在环\n");
	return 0;
}
//一次遍历查找倒数第n个元素
int getNodeFromBack(Node* pHead, int n, elemType *e){
	int i = 0; 
	Node* p = pHead;
	while (i < n&& p->next != NULL){//查找正数第n个节点
		i++;
		p = p->next;
		printf("%d---%d\n", i,p->element);
	}

	if (p->next == NULL&&i < n - 1){
		printf("超出链表长度\n");
		return 0;

	}
	Node* p2 = pHead;
	while (p != NULL){//查找倒数第n个节点
		p2 = p2->next;
		p = p->next;

	}
	*e = p2->element;
	printf("倒数第%d个元素是:%d\n", n, *e);
	return 1;
}

void main(){
	Node* pList = NULL;
	//int length = 0;
	elemType el;


	init(&pList);
	pList = create(pList);
	print(pList);

	getNodeFromBack(pList,4,&el);
	/*
	size(pList);
	print(pList);
	isEmpty(pList);
	el = get(pList, 4);
	modify(pList, 4, 100);
	insertHead(&pList,1);
	print(pList);
	insertLast(&pList, 199);
	print(pList);
	insertMid(&pList, 40, 2);
	print(pList);
	deleteMidEle(&pList, 2);
	print(pList);
	delHead(&pList);
	print(pList);
	delLast(&pList);
	print(pList);
	reverse(&pList);
	print(pList);
	*/
	/*hasLoop2(pList);

	hasLoop(pList);*/
	system("pause");
}





你可能感兴趣的:(白话算法,移动,指针,链表,查找,面试)