链表的面试题总结

来源:互联网 发布:js获取radio选中状态 编辑:程序博客网 时间:2024/05/16 19:36

链表的面试题总结

链表在面试中是非常容易的考点,所以在这里总结一下,希望对大家有所帮助

首先,我们给出链表的基本结构,和基本的操作,创建一个结点,打印链表的结点,尾插法加入结点。

#include <stdio.h>#include <assert.h>#include <stdlib.h>typedef int DataType;typedef struct SListNode{DataType _pData;struct SListNode* _pNext;}SListNode;SListNode* BuySListNode(DataType x); //void SListPrint(SListNode* pHead); // void SListPushBack(SListNode** ppHead, DataType x); SListNode* BuySListNode(DataType x){//创建一个新的结点SListNode* cur = (SListNode*)malloc(sizeof(SListNode));cur->_pData = x;cur->_pNext = NULL;return cur;}void SListPrint(SListNode* pHead){//打印链表中的结点SListNode* cur = pHead;if (pHead == NULL){printf("List is Full!\n");return;} else{while (cur){printf("%d ",cur->_pData);cur = cur->_pNext;}printf("\n");}}void SListPushBack(SListNode** ppHead, DataType x){//尾插法SListNode* newNode = BuySListNode(x);SListNode* pCur = *ppHead;if (pCur == NULL){*ppHead = newNode;}else{while (pCur->_pNext!= NULL){pCur = pCur->_pNext;}pCur->_pNext = newNode;}}int main(){SListNode* s = NULL;SListPushBack(&s,1);SListPushBack(&s,2);SListPushBack(&s,3);SListPushBack(&s,4);SListPushBack(&s,5);SListPrint(s);return 0;}
此时我们就创建好一个链表,链表的内容为:1 2 3 4 5 

①从尾到头打印链表

方法中我们定义一个tail指针,每次把tail指针当作结尾的标致向前移动,然后遍历链表打印尾结点

void PrintListTailToHead(SListNode* pHead){SListNode* tail = NULL;SListNode* cur = pHead;while (tail!=pHead){cur = pHead;while (cur->_pNext!= tail){cur = cur->_pNext;}printf("%d ",cur->_pData);tail = cur;}}
②删除无头单链表的非尾结点(不能遍历)

要删除某个结点,首先我们用Find函数查找出这个结点,然后把这个结点的地址返回,供下一次调用。

我们使用替换法删除结点,用next指针标记pos的下一个结点,用next->data覆盖pos->data,然后把next释放,逻辑上pos指的结点就被删除了

SListNode* Find(SListNode* pHead,DataType x){SListNode* cur = pHead;while (cur){if(cur->_pData == x)return cur;cur = cur->_pNext;}return NULL;}void DeleteNotTailNode(SListNode* pos){SListNode* next = pos->_pNext;pos->_pData = next->_pData;pos->_pNext = next->_pNext;free(next);}
③在无头单链表的一个结点前插入一个结点(不能遍历)

方法和之前的替换法相当,用后插法在结点的后面插入一个值,用要插入的值改变pos的值,那么在逻辑上就是在pos前面插入一个值

void InsertFrontNode(SListNode* pos,DataType x){SListNode* next = pos->_pNext;SListNode* newNode = BuySListNode(pos->_pData);newNode->_pNext = next;pos->_pNext = newNode;pos->_pData = x;}
④约瑟夫环问题

每次按照传入的参数,让cur走相应的步数,然后按照替换法删除在逻辑上删除cur指的结点

SListNode* JosephCircle(SListNode* pHead,int k){SListNode* cur = pHead;SListNode* next;int count = k;while (cur->_pNext != cur){count = k;while (count--){cur = cur->_pNext;}next = cur->_pNext;cur->_pData = next->_pData;cur->_pNext = next->_pNext;free(next);}return cur;}
⑤单链表的逆置(不开辟新空间)

方法一:

函数中,我们用n1,n2,n3三个指针联动跑,按照while循环里面的顺序,最后一次出来的时候n2,n3指的是空,n1指的是之前链表的尾部,现在返回n1就是返回新链表的头指针

SListNode* ReverseList(SListNode** ppHead){SListNode* n1;SListNode* n2;SListNode* n3;n1 = *ppHead;n2 = (*ppHead)->_pNext;n3 = n2->_pNext;n1->_pNext = NULL;while (n2!=NULL){n2->_pNext = n1;n1 = n2;n2 = n3;if(n3!=NULL){n3 = n3->_pNext;}}return n1;}
方法二:

SListNode* ReverseList(SListNode** ppHead){SListNode* newList = NULL;SListNode* cur = *ppHead;SListNode* next = cur->_pNext;while (cur!=NULL){cur->_pNext = newList;newList = cur;cur = next;if(next)next = next->_pNext;}return newList;}
⑥链表的冒泡排序

void SListBubbleSort(SListNode* pHead){SListNode* cur = pHead;SListNode* next = cur->_pNext;SListNode* tail = NULL;DataType temp;int flag = 0;while (tail!=pHead){cur = pHead;next = cur->_pNext;while (next != tail){if(cur->_pData > next->_pData){temp = cur->_pData;cur->_pData = next->_pData;next->_pData = temp;flag = 1;}cur = cur->_pNext;next = next->_pNext;}if(flag == 0){return;}tail = cur;}}
⑦把两个链表合成一个有序链表

注意情况:两个链表有可能不一样长,有可能为空

SListNode* SListMeget(SListNode** list1,SListNode** list2){SListNode* tail;SListNode* list;if(list1 == NULL)//任何一个链表为空,就返回另外一个链表,即使另外一个链表也为空,return *list2;if(list2 == NULL)return *list1;if((*list1)->_pData > (*list2)->_pData){//确定第一个值比较小的链表,作为新链表的开始list = tail = (*list2);(*list2) = (*list2)->_pNext;}if((*list1)->_pData < (*list2)->_pData){list = tail = (*list1);(*list1) = (*list1)->_pNext;}while (*list1 && *list2)//两个链表都没结束的时候,做比较,把新链表的tail->_pNext指向较小的链表{if((*list1)->_pData < (*list2)->_pData){tail->_pNext = *list1;(*list1) = (*list1)->_pNext;}else{tail->_pNext = (*list2);(*list2) = (*list2)->_pNext;}tail = tail->_pNext;}if(*list1){tail->_pNext = (*list1);}if(*list2){tail->_pNext = (*list2);}return list;}
⑧找到链表的中间链表(只能遍历一遍链表)

函数中,使用一个快指针fast,使用一个慢指针slow,slow走一步,fast走两步,那么在fast走到结尾的时候,slow走了fast的一半,所以slow就走在链表的中间

SListNode* FindMidNode(SListNode* pHead){SListNode* fast = pHead;SListNode* slow = pHead;while (fast  && fast->_pNext){fast = fast->_pNext->_pNext;slow = slow->_pNext;}return slow;}
⑨找到倒数第k个结点(只能遍历一次链表)

方法中首先让快指针和慢指针相差k个位置,那么在快指针到达尾结点的时候,慢指针就在倒数第K个结点

SListNode* FindNodeFromBackOfNumberK(SListNode* pHead,int k){SListNode* fast = pHead;SListNode* slow = pHead;while (k--){if(fast){fast = fast->_pNext;}}while (fast){fast = fast->_pNext;slow = slow->_pNext;}return slow;}
⑩判断链表是否带环

fast指针一次走两步,slow指针一次走一步,若带环,那么两个指针在环里面一定会相遇

SListNode* IsCircle(SListNode* pHead){SListNode* fast = pHead;SListNode* slow = pHead;while (fast && fast->_pNext){fast = fast->_pNext->_pNext;slow = slow->_pNext;
if(fast == slow){
return fast;}
}return NULL;}

⑪判断环的大小,也就是环里面有多少结点

我们使用之前判断是否带环的函数来确定指针在环里面相遇的点,在按照这个点跑一圈,就是这个环的长度。

int CircleLength(SListNode* pHead){SListNode* meet = IsCircle(pHead);SListNode* cur = meet->_pNext;int cnt =1;while (cur != meet){cnt++;cur = cur->_pNext;}return cnt;}

⑫判断环的入口点:

SListNode* EnterNode(SListNode* pHead){SListNode* cur = pHead;SListNode* meet = IsCircle(pHead);while (cur != meet){cur = cur->_pNext;meet = meet->_pNext;}return cur;}
判断环的入口点讲解图:

此后我还会继续在文章中补充关于链表的题目,敬请关注!

Tip:限于编者水平,文章有很多不足之处,欢迎指正。

如需转载,请注明出处。