在各种面试笔试中,单链表考察的最多。其中有一道经典的题目“寻找单链表倒数第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");
}