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