C语言实现单链表面试题汇总

这篇博客只有针对单链表的不同面试题的不同函数,没有对单链表的具体实现方法的介绍。
单链表的具体实现方法(创建,初始化,前插,后插,删除,插入,销毁等),可以参考我的另一边博客:
http://blog.csdn.net/ljx_5489464/article/details/50893430

以下是单链表面试题的具体实现:

1、从尾到头打印单链表

void PrintListTailToHead(PSListNode pHead)
{if (NULL != pHead){//递归实现PrintListTailToHead(pHead->pNextNode);printf("%d  ", pHead->data);}
}

2、删除一个无头单链表的非尾节点

void DelNotTailNode(PSListNode pos)
{PSListNode pNode = NULL;assert(pos);if (NULL == pos->pNextNode){return;}else{DataType temp = 0;//交换pos和pos->pNextNode的数据(相当于交换了两个结点的位置),使问题转换为删除pos指向的结点的下一个结点temp = pos->data;pos->data = pos->pNextNode->data;pos->pNextNode->data = temp;pNode = pos->pNextNode;pos->pNextNode = pos->pNextNode->pNextNode;free(pNode);pNode = NULL;}
}

3、在无头单链表的一个非头节点前插入一个节点

void InsertNotHead(PSListNode pos, DataType data)
{if (NULL == pos){return;}else{PSListNode pNewNode = ByeNode(data);if (NULL == pNewNode){printf("开辟结点空间失败!\n");return;}else{//交换pos和pNewNode的数据(相当于交换了两个结点的位置),使问题转换为在pos指向的结点的下一个结点处插入新结点pNewNode->data = pos->data;pos->data = data;pNewNode->pNextNode = pos->pNextNode;pos->pNextNode = pNewNode;}}
}

4、单链表实现约瑟夫环(JosephCircle)

//使链表形成一个环
void FormCyc(PSListNode *pHead)
{if (NULL == pHead){return;}else{PSListNode pNode = *pHead;while (NULL != (pNode->pNextNode)){pNode = pNode->pNextNode;}pNode->pNextNode = *pHead;}
}PSListNode JosephCircle(PSListNode pHead, int M)
{if ((NULL == pHead) || (M <= 0)){return NULL;}else{//让链表中所有元素形成一个环FormCyc(&pHead);PSListNode pPreNode = NULL;PSListNode pCurNode = pHead;PSListNode pDesNode = NULL;int temp = M;while (pCurNode->pNextNode != pCurNode){temp = M;pPreNode = pCurNode;while (--temp){pPreNode = pCurNode;pCurNode = pCurNode->pNextNode;}//记住要从链表中删除的节点的位置,把它的空间释放了pDesNode = pCurNode;pCurNode = pCurNode->pNextNode;pPreNode->pNextNode = pCurNode;free(pDesNode);pDesNode = NULL;}//如果M=1,就说明所有结点都要被删除,那么就返回空,否则就返回剩下的那个结点的指针if (1 == M){free(pCurNode);pCurNode = NULL;}else{pCurNode->pNextNode = NULL;}return pCurNode;}
}

5、逆置/反转单链表

逆置链表:方法一:前插法
//void ReverseList(PSListNode* pHead)
//{
//  if (NULL == *pHead)
//  {
//      return;
//  }
//  else
//  {
//      //创建一个新的空链表,遍历pHead指向的链表里的所有节点,每找到一个,就前插到新链表里
//      PSListNode pNewHead = *pHead;
//      *pHead = (*pHead)->pNextNode;
//      //第一次前插到新链表里的结点,是新链表的尾节点,所以要使它的下一个结点为空
//      pNewHead->pNextNode = NULL;
//      while (NULL != *pHead)
//      {
//          PSListNode pNode = *pHead;
//          *pHead = (*pHead)->pNextNode;
//          pNode->pNextNode = pNewHead;
//          pNewHead = pNode;
//      }
//      *pHead = pNewHead;
//  }
//}
//逆置链表:方法二:三指针法
void ReverseList(PSListNode* pHead)
{assert(pHead);if ((*pHead == NULL) || ((*pHead)->pNextNode) == NULL){return;}else{PSListNode pPreNode = *pHead;PSListNode pCurNode = (*pHead)->pNextNode;PSListNode pOffNode = (*pHead)->pNextNode->pNextNode;PSListNode pNode = NULL;while (1){pCurNode->pNextNode = pPreNode;pPreNode->pNextNode = pNode;pNode = pPreNode;if (pOffNode == NULL){break;}else{pPreNode = pCurNode;pCurNode = pOffNode;pOffNode = pOffNode->pNextNode;}}*pHead = pCurNode;}
}

6、单链表排序(冒泡排序)

void SortList(PSListNode pHead)
{if (NULL == pHead){return;}else{int flag = 0;PSListNode pTailNode = NULL;//当设置的尾节点与头结点指向同一个节点时,说明只有一个元素为排序,那么冒泡完成while (pTailNode != pHead){PSListNode pPreNode = pHead;//每次参与比较的都是尾节点前面的结点while (pPreNode->pNextNode != pTailNode){PSListNode pCurNode = pPreNode->pNextNode;if (pPreNode->data > pCurNode->data){DataType dTemp = pPreNode->data;pPreNode->data = pCurNode->data;pCurNode->data = dTemp;flag = 1;}pPreNode = pPreNode->pNextNode;}//对冒泡的优化,只要有一趟比较没有发生结点交换,说明冒泡完成,就可以退出冒泡的代码块了if (0 == flag){break;}pTailNode = pPreNode;}}
}

7、合并两个有序链表,合并后依然有序

PSListNode MergeList(PSListNode pL1, PSListNode pL2)
{PSListNode pNewNode = NULL;PSListNode pListNode1 = pL1;PSListNode pListNode2 = pL2;PSListNode pNode = NULL;if (NULL == pListNode1){return pListNode2;}else if (NULL == pListNode2){return pListNode1;}else{//先把新链表的头结点的指针找到,每次取两个链表中保存的数据较小的结点后插到新链表中if (pListNode1->data > pListNode2->data){pNode = pListNode2;pListNode2 = pListNode2->pNextNode;pNewNode = pNode;}else{pNode = pListNode1;pListNode1 = pListNode1->pNextNode;pNewNode = pNode;}while ((NULL != pListNode1) && (NULL != pListNode2)){if (pListNode1->data > pListNode2->data){pNode->pNextNode = pListNode2;pListNode2 = pListNode2->pNextNode;pNode = pNode->pNextNode;}else{pNode->pNextNode = pListNode1;pListNode1 = pListNode1->pNextNode;pNode = pNode->pNextNode;}}if (NULL == pListNode1){pNode->pNextNode = pListNode2;return pNewNode;}else{pNode->pNextNode = pListNode1;return pNewNode;}}
}

8、查找单链表的中间节点,要求只能遍历一次链表

PSListNode FindMidNode(PSListNode pHead)
{if (NULL == pHead){return NULL;}else{//快慢指针PSListNode pSlow = pHead;PSListNode pFast = pHead;//注意结束条件得加上NULL != pFast->pNextNode,否则当NULL == pFast->pNextNode时,进入循环,//执行pFast = pFast->pNextNode->pNextNode时会崩溃while ((NULL != pFast) && (NULL != pFast->pNextNode)){pSlow = pSlow->pNextNode;pFast = pFast->pNextNode->pNextNode;}return pSlow;}
}

9、查找单链表的倒数第k个节点,要求只能遍历一次链表

PSListNode FindLastKNode(PSListNode pHead, int K)
{if ((NULL == pHead) || (K <= 0)){return NULL;}else{PSListNode pFast = pHead;PSListNode pSlow = pHead;//利用快慢指针,让快指针先走K-1步,然后两指针同时走,直到快指针指向的下一个结点为空为止while (--K){pFast = pFast->pNextNode;if (NULL == pFast){return NULL;}}while (NULL != pFast->pNextNode){pFast = pFast->pNextNode;pSlow = pSlow->pNextNode;}return pSlow;}
}

10、判断单链表是否带环?若带环,求环的长度?求环的入口点?

PSListNode HasCycle(PSListNode pHead)
{if ((NULL == pHead) || (NULL == pHead->pNextNode)){return NULL;}else{PSListNode pFast = pHead->pNextNode->pNextNode;PSListNode pSlow = pHead->pNextNode;//利用快慢指针,让快指针每次走两步,慢指针每次走一步,要是快指针没有走到NULL,且快指针与慢指针指向相同就说明是有环while (pFast != pSlow){//快指针要是没有指向为空,那么慢指针就不可能指向空(快指针走得快)if (NULL == pFast){return;}else{pFast = pFast->pNextNode;pSlow = pSlow->pNextNode;   if (NULL == pFast){return;}else{pFast = pFast->pNextNode;}}}return pFast;}
}int GetCyleLen(PSListNode pMeetNode)
{//默认传的参数是HasCycle函数返回的环中的一个结点if (NULL == pMeetNode){return 0;}else{int nCount = 1;PSListNode pNode = pMeetNode;while (pMeetNode != pNode->pNextNode){pNode = pNode->pNextNode;nCount++;}return nCount;}
}
//pMeetNode参数是用HasCycle函数求链表是否有环时pFast指针与pSlow指针的碰撞点
//定律:在链表头,pFast指针与pSlow指针的碰撞点分别设定一个指针,每次各走一步,两个指针必定相遇,则相遇第一点为环入口点
PSListNode FindEnterNode(PSListNode pHead, PSListNode pMeetNode)
{PSListNode pNode = pHead;if ((NULL == pHead) || (NULL == pMeetNode)){return NULL;}while (pNode != pMeetNode){pNode = pNode->pNextNode;pMeetNode = pMeetNode->pNextNode;}return pNode;
}

11、判断两个链表是否相交,若相交,求交点。(假设链表不带环)

int IsListCrose(PSListNode pL1, PSListNode pL2)
{if ((NULL == pL1) || (NULL == pL2)){return 0;}else{PSListNode PSList1 = pL1;PSListNode PSList2 = pL2;while (NULL != PSList1->pNextNode){PSList1 = PSList1->pNextNode;}while (NULL != PSList2->pNextNode){PSList2 = PSList2->pNextNode;}//不带环的两个链表相交,那么它们的最后一个结点的指针的值一定是相等的if (PSList1 == PSList2){return 1;}else{return 0;}}
}

12、判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

int IsListCroseWithCycle(PSListNode pL1, PSListNode pL2)
{PSListNode pMeetNode1 = HasCycle(pL1);PSListNode pMeetNode2 = HasCycle(pL2);if ((NULL == pL1) || (NULL == pL2)){return 0;}//两个链表都没环的情况else if ((NULL == pMeetNode1) && (NULL==pMeetNode2)){PSListNode PSList1 = pL1;PSListNode PSList2 = pL2;while (NULL != PSList1->pNextNode){PSList1 = PSList1->pNextNode;}while (NULL != PSList2->pNextNode){PSList2 = PSList2->pNextNode;}//不带环的两个链表相交,那么它们的最后一个结点的指针的值一定是相等的if (PSList1 == PSList2){return 1;}else{return 0;}}// 两个链表都有环的情况(这种情况肯定是两个链表共用一个环)else if ((NULL != pMeetNode1) && (NULL != pMeetNode2)){while (pMeetNode1->pNextNode != pMeetNode1){//找到一个链表中处于环中的点,遍历另一个链表中环中的点,看他们是否会出现相等的情况,出现,则可能相交if (pMeetNode1 == pMeetNode2){return 1;}pMeetNode1 = pMeetNode1->pNextNode;}//跳出循环时,遍历的组后一个结点还没进行比较,此处处理这种情况if (pMeetNode1 == pMeetNode2){return 1;}else{return 0;}}// 一个链表有环,一个没环,这种情况不会相交else{return 0;}
}

13、求链表相交时的交点

//链表相交时的交点
PSListNode IntersectionNode(PSListNode pL1, PSListNode pL2)
{int count1 = 0;int count2 = 0;PSListNode PSList1 = pL1;PSListNode PSList2 = pL2;PSListNode pMeetNode1 = HasCycle(pL1);PSListNode pMeetNode2 = HasCycle(pL2);if ((NULL == pL1) || (NULL == pL2)){return NULL;}else{//先求每个链表的长度//两个链表都没环if ((NULL == pMeetNode1) && (NULL == pMeetNode2)){while (NULL != PSList1){PSList1 = PSList1->pNextNode;count1++;}while (NULL != PSList2){PSList2 = PSList2->pNextNode;count2++;}}//两个链表都有环else if ((NULL != pMeetNode1) && (NULL != pMeetNode2)){PSListNode pInNode1 = FindEnterNode(PSList1, pMeetNode1);PSListNode pInNode2 = FindEnterNode(PSList2, pMeetNode2);//先计算头指针到环入口结点的长度,再计算环的长度while (PSList1 != pInNode1){PSList1 = PSList1->pNextNode;count1++;}while (PSList1->pNextNode != PSList1){PSList1 = PSList1->pNextNode;count1++;}count1++;;while (PSList2 != pInNode2){PSList2 = PSList2->pNextNode;count2++;}while (PSList2->pNextNode != PSList1){PSList2 = PSList2->pNextNode;count2++;}count2++;;}//一个有环,一个没环,不会相交else{return NULL;}//让长度长的链表的头指针先走它长于另一个链表的结点数//在计算链表长度时修改了这两个指针的值,在这儿需要把它们改回来PSList1 = pL1;PSList2 = pL2;if (count1 > count2){int temp = count1 - count2;while (0 == temp--){PSList1 = PSList1->pNextNode;}}else{int temp = count2 - count1;while (0 == temp--){PSList2 = PSList2->pNextNode;}}//此时,让两个链表的头指针同时移动,直到它们相等就找到了交点//因为题目是找交点,那么交点就存在,所以这儿不用怕死循环while (1){if (PSList1 = PSList2){break;}PSList1 = PSList1->pNextNode;PSList2 = PSList2->pNextNode;}return PSList1;}
}

14、求两个已排序单链表中相同的数据。void UnionSet(Node* l1, Node* l2);

PSListNode ByeNode(DataType data)
{PSListNode pNewNode = (PSListNode)malloc(sizeof(struct SListNode));if (NULL != pNewNode){pNewNode->data = data;//注意使开辟的新节点的指向为空pNewNode->pNextNode = NULL;}return pNewNode;
}PSListNode UnionSet(PSListNode pL1, PSListNode pL2)
{PSListNode PSList1 = pL1;PSListNode PSList2 = pL2;PSListNode pNewHead = NULL;PSListNode pNode = NULL;//每次比较两个链表头指针指向的数据是否相等,不相等,就让数据小的头指针后移,相等,则把该数据保存起来,//两个头指针同时后移,直到其中一个指向空为止while ((NULL == PSList1) || (NULL == PSList2)){if (PSList1->data > PSList2->data){PSList2 = PSList2->pNextNode;}else if (PSList1->data < PSList2->data){PSList1 = PSList1->pNextNode;}//用一个新的链表来保存两个链表中相同的数据else{if (pNewHead == NULL){pNewHead = ByeNode(PSList1->data);pNode = pNewHead;PSList1 = PSList1->pNextNode;PSList2 = PSList2->pNextNode;}else{pNode = pNode->pNextNode;pNode = ByeNode(PSList1->data);PSList1 = PSList1->pNextNode;PSList2 = PSList2->pNextNode;}}}return pNewHead;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526186.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线性表之顺序表与单链表的区别及优缺点

这里比较的是基于C语言实现的顺序表与单链表&#xff0c;与其他语言的实现可能会有差异&#xff0c;但我相信语言是相通的&#xff0c;它们的实现机制应该也差不多。 1、What 什么是顺序表和单链表 ①顺序表&#xff1a; 顺序表是在计算机内存中以数组的形式保存的线性表&a…

前置++与后置++之一道简单的题目引发的思考

引言 昨晚一时兴起&#xff0c;我脑子就问自己下面的代码会输出什么&#xff0c;也不知道我脑子为什么有这个代码模型&#xff0c;只是模糊的有些印象&#xff1a; #include <stdio.h> #include <stdlib.h>int main(int argc,char** argv) {int i3,j;j(i)(i)(i);…

有趣的for循环

#include <stdio.h> int main() {int i 0;for(i 0, printf(" First %d", i);i<10, printf(" Second %d", i);i, printf(" Third %d", i)){printf(" Fourth %d", i);}system("pause");return 0; } 这段代码会…

重载函数编译后的新名字

我们都知道很多语言都支持函数重载&#xff0c;那么编译器是怎么处理编译后它们的命名冲突的呢&#xff1f; 1、先给出几个正确的重载函数&#xff1a; #include <iostream> using namespace std;int Add(int a, int b) {return a b; }double Add(double a, double b)…

初识C++之函数重载

最近开始学习C&#xff0c;了解到它在C语言没有的一个特性 – 函数重载&#xff0c;这一特性使得c的函数数量得以减少&#xff0c;减小了对名字空间的污染&#xff0c;另外对程序的可读性也有很大帮助。 那么c的函数重载这一特性是怎么实现的&#xff0c;为什么不会发生命名冲…

const在C与C++中的区别

1、const用于C –> 运行时常量 a. 修饰变量 修饰变量为常变量&#xff08;只读变量&#xff09; const int i 10; 此时i还是一个变量&#xff0c;不信你可以把它用来当作定义一个数组时的数组长度&#xff0c;一定会报错。不过它具有一种常属性&#xff0c;它的值一直都…

乱入的'\0'

看这个题之前&#xff0c;先来回忆一下strlen函数的工作机制&#xff1a; strlen所作的仅仅是一个计数器的工作&#xff0c;它从内存的某个位置&#xff08;可以是字符串开头&#xff0c;中间某个位置&#xff0c;甚至是某个不确定的内存区域&#xff09;开始扫描&#xff0c;…

强迫症的自我恢复

怎样克服强迫症&#xff1f;这是很多的人比较关注的一个问题。强迫症给人们的工作和生活带来很严重的影响&#xff0c;如果不及时进行治疗&#xff0c;对人的健康损害是很大的。那么怎样克服强迫症呢&#xff1f;下面就介绍一些怎样克服强迫症的调试方法&#xff0c;希望能帮助…

如何克服拖延症

拖延几乎成为现代人的通病&#xff0c;“先放一下&#xff0c;待会再行动”成为拖延者最大的思想毒瘤&#xff0c;如何积极地克服拖延症&#xff0c;已经成为了一个大课题。 1、正视自己的拖延症。 要改掉拖延症坏毛病&#xff0c;首先就是要正确认识到拖延症的危害&#xff…

初识C++之指针与引用

1、What a. 指针&#xff1a; 指针可以看做是一个特殊的变量&#xff0c;它是用来存放变量的地址值的。 b. 引用 引用的话&#xff0c;可以看做是给变量起的一个别名&#xff0c;而不是定义一个新变量&#xff0c;它与那个变量的本质是相同的&#xff0c;内容与地址都是一样…

初识C++之剖析const与#define

1、 编译器处理方式不同   #define是一个宏定义命令&#xff0c;它是在预处理阶段就进行了替换&#xff1b;   const修饰的是一个编译时常量&#xff0c;它是在编译阶段处理的。 2、 类型和安全检查不同   #define定义的标识符仅仅是产生文本替换&#xff0c;不管内容…

初识C++之运算符重载

C里面有一个叫作运算符重载的特性&#xff0c;它其实是基于函数实现的&#xff0c;下面就来介绍一下运算符重载。 1、What  C中预定义的运算符的操作对象只能是基本数据类型。但实际上&#xff0c;对于许多用户自定义类型&#xff08;例如类&#xff09;&#xff0c;也需要类…

对堆栈的认识

什么是堆和栈&#xff0c;它们在哪儿&#xff1f; 问题描述 编程语言书籍中经常解释值类型被创建在栈上&#xff0c;引用类型被创建在堆上&#xff0c;但是并没有本质上解释这堆和栈是什么。我仅有高级语言编程经验&#xff0c;没有看过对此更清晰的解释。我的意思是我理解什…

Waiting For Debugger

最近楼主在使用手机上的邮政银行时&#xff0c;总是打不开&#xff0c;要强制关闭&#xff0c;给我一个“Waiting For Debugger”的提示&#xff0c;相信朋友们应该遇到过类似的问题&#xff0c;当然这里不一定就是邮政银行出问题&#xff0c;可能是你手机里的任意一款软件&…

防止头文件重复包含之pragma once与#ifndef

在我们自己编写 C/C的头文件时&#xff0c;可能会忽略一点&#xff1a;用一些处理机制来避免头文件的重复包含&#xff0c;因为头文件的内容在预编译时是把头文件的内容完全拷贝到引入的地方替换头文件的包含命令&#xff0c;而包含的头文件可能有包含很多内容&#xff0c;所以…

初识C++之继承

1、何为继承 C中所谓继承&#xff0c;就是在一个已存在类的基础上创建一个新的类&#xff0c;新类获得已存在类的部分特性&#xff08;为什么是部分特性&#xff0c;后面会讲到&#xff09;。已存在类被称为基类&#xff08;Base Class&#xff09;或父类&#xff08;Father Cl…

初识C++之函数重载、重写、重定义的区别

在C的学习中&#xff0c;慢慢接触了一些很容易混淆的名词&#xff0c;今天就来剖析几个容易混淆的名词。 1、函数重载   重载函数是函数的一种特殊情况&#xff0c;为方便使用&#xff0c;C允许在同一范围中声明几个功能类似的同名函数&#xff0c;但是这些同名函数的形式参…

初识C++之封装

学习C不得不说到C的三大特性&#xff1a;封装、继承、多态&#xff0c;今天就先来剖析一下他的封装性。 1、什么是封装   封装就是将抽象得到的数据和行为&#xff08;或功能&#xff09;相结合&#xff0c;形成一个有机的整体&#xff0c;也就是将数据与操作数据的源代码进…

初识C++之虚函数

1、什么是虚函数   在基类中用virtual关键字修饰&#xff0c;并在一个或多个派生类中被重新定义的成员函数&#xff0c;用法格式为&#xff1a;   virtual 函数返回类型 函数名&#xff08;参数表&#xff09;    {     函数体    }    虚函数是实现多态性…

初识C++之多态

多态性是将接口与实现进行分离&#xff1b;用形象的语言来解释就是实现以共同的方法&#xff0c;但因个体差异&#xff0c;而采用不同的策略。 1、什么是多态   多态&#xff08;Polymorphism&#xff09;按字面的意思就是“多种状态”。在面向对象语言中&#xff0c;接口的…