c实现单链表面试题

本文汇总了链表常见算法题目,包括打印、删除、插入、排序等操作,并提供了详细的代码实现,适合准备面试和技术进阶的学习者。

在面试中,链表题出现的频率也是相当的高,以下是对链表题的一个总结,归纳:

1 .从尾到头打印单链表

2.删除⼀个⽆头单链表的⾮尾节点
3.在⽆头单链表的⼀个⾮头节点前插⼊⼀个节点
4.单链表实现约瑟夫环
5.逆置/反转单链表
6.单链表排序(冒泡排序&快速排序)
7.合并两个有序链表,合并后依然有序
8.查找单链表的中间节点,要求只能遍历⼀次链表
9.查找单链表的倒数第k个节点,要求只能遍历⼀次链表
10.判断单链表是否带环?若带环,求环的长度?求环的⼊⼜点?并计算每个算法
的时间复杂度&空间复杂度。
11.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
12.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

具体代码如下


typedef int DataType;//数据类型的重定义
typedef struct Node
{
	DataType _data;
	struct Node* next;//指向下一个结点的指针
}Node,*pNode,*pList;
void InitLinkList(pList* pplist)  //链表初始化函数
{
    assert(pplist);
    *pplist = NULL;
}
pNode BuyNode(DataType x)//新建一个结点
{
    pNode  pnode=malloc(sizeof(Node));
	if(pnode==NULL)
	{
		perror("PushBack()::malloc");//内存开辟失败
		exit(1);
	}
	pnode->next=NULL;
	pnode->_data=x;
	return pnode;
}
void PushBack(pList *pplist,DataType x)//在单链表的尾部插入一个元素
{
	 pNode pnode=NULL;
	 assert(pplist);
	 pnode=BuyNode(x);
	if(*pplist==NULL)//链表为空
	{
      *pplist=pnode;
	}
	else//找到尾结点,在其后面插入
	{
		pNode cur=*pplist;
		while(cur->next)
        {
			cur=cur->next;
		}
        cur->next=pnode;
	}
}
void PopBack(pList *pplist)//删除尾部结点
{
	pNode cur=NULL;
	assert(pplist);
	
	if(*pplist==NULL)//空链表
	{
		return;
	}
	else if((*pplist)->next==NULL)//只有一个结点
	{
		free(*pplist);
		*pplist=NULL;
	}
	else //两个或两个以上结点
	{    cur=*pplist;//在单链表中删除一个结点需找到其前一个结点
         while((cur->next)->next)//查找尾部结点的前一个结点
	      {
		     cur=cur->next;
	      }
	     free(cur->next);//此时cur->next指向尾部结点
	     cur->next=NULL;
	}
}
void PushFront(pList *pplist,DataType x)//首部插入结点
{
	pNode pnode=NULL;
    assert(pplist);
	pnode=BuyNode(x);
	if(*pplist==NULL)
	{
		*pplist=pnode;
	}
	else
	{
		pnode->next=*pplist;//插在头部结点之前
		*pplist=pnode;
    }
}
void PopFront(pList *pplist)//首部结点的删除
{  
	pNode del=NULL;
    assert(pplist);
	del=*pplist;
	if(*pplist==NULL)
	{
		return;
	}
     *pplist=del->next;
	 free(del);
	 del=NULL;//将del置空,防止其成为野指针
}
void BubbleSort(pList* pplist)//冒泡排序
{
	pNode cur=NULL;
	pNode tail=NULL;
    assert(pplist);
	if(*pplist==NULL &&(*pplist)->next==NULL)//若单向链表为空或只有一个元素时,不进行排序
	{
		return;
	}
	while(tail != (*pplist)->next)//控制排序的趟数
	{
        cur=*pplist;//每次比较都从头部开始
	    while(cur!=tail && ( cur->next!=tail))//每趟中的比较次数
	    {
		   if(cur->_data > (cur->next)->_data)
		   {
			   DataType tmp=cur->_data;
			   cur->_data=(cur->next)->_data;
			   (cur->next)->_data=tmp;
		   }
		   cur=cur->next;
	     }
		 tail=cur;//尾部结点为比较的最后一个元素
	}//执行完一趟后,tail向前跳转
}
void ReversePrint(pList plist) //逆序打印单项链表
{
	pNode cur=plist;
	if(plist==NULL)
    {
		return;
	}
	if(cur->next)//寻找尾部结点
	{
		ReversePrint(cur->next);//递归
    }
	printf("%d",cur->_data);

}
pNode Find(pList plist,DataType x)//查找某个元素
{
	pNode cur=plist;
	if(cur==NULL)
	{
		return NULL;
	}
	else
	{
		while(cur&&(cur->_data!=x))//从前往后依次遍历整个链表
		{
          cur=cur->next;
		}
		if(cur!=NULL)
		{
			return cur;//返回结点位置
		}
		return NULL;//找不到返回空
	}
}
void EraseNotTail(pNode pos)//删除无头单链表的非尾结点 
{
	pNode del=NULL; //将指定位置处的结点,将其后一个结点的数据赋给指定位置处的数据,然后将其后一个结点删除
	if(pos==NULL)
	{
		return;
	}
	if(pos->next)
	{
		pos->_data=(pos->next)->_data;
		del=pos->next;
		pos->next=del->next;
		free(del);
		del=NULL;
	}
}
//在无头单链表的非头结点前插入一个元素 
void InsertFrontNode(pNode pos, DataType x)//思路:在指定位置的后面插入一个元素,它们的数据进行交换
{
	pNode pnode=BuyNode(x);
	DataType tmp;
	if(pos==NULL)
	{
		return;
	}
	pnode->next=pos->next;
	pos->next=pnode;
    tmp=pos->_data;
    pos->_data=pnode->_data;
	pnode->_data=tmp;
}
void JosephCycle(pList* pplist, int k)//约瑟夫环问题 
{
	pNode cur=NULL;
	pNode del=NULL;
	int i=0;
	assert(pplist);
	if(*pplist==NULL)
	{
		return;
	}
	cur=*pplist;
	while(cur!=cur->next)
	{
		for(i=0;inext;
	  }
	  printf("%d ",cur->_data);
	  cur->_data=(cur->next)->_data;
	  del=cur->next;
	  cur->next=del->next;
	  free(del);
	  del=NULL;
	}
	printf("%d",cur->_data);

}
void ReverseList(pList* pplist)//逆序单向链表  
{
	pNode cur=NULL;
	pNode next=NULL;
	pNode tmp=NULL;
	assert(pplist);
	if((*pplist==NULL) && ((*pplist)->next==NULL))
	{
		return;
	}
	cur=*pplist;
	next=cur->next;
	cur->next=NULL;
	while(next)
	{
		tmp=next->next;
		next->next=cur;
		cur=next;
		next=tmp;
	}
	*pplist=cur;
}
pList Merge(const pList* p1, const pList* p2)//合并两个有序列表 
{
	pList newlist=NULL;
	pList list1=NULL;
	pList list2=NULL;
	pList tail=NULL;
    assert(p1&&p2);
	list1=*p1;
	list2=*p2;
	if(list1==list2)//list1与list2为同一条链表,或都为空链表
	{
		return NULL;
    }
	if(list1==NULL)//一条为空,另一条不为空
	{
		return list2;
		
	}
	if(list2==NULL)
	{
		return list1;
	}
	 if(list1->_data_data)//新链表的首部
	  {
		  newlist=list1; 
		  list1=list1->next;
	  }
	 else
	 {
		  newlist=list1; 
		  list1=list1->next;
	 }
	 tail=newlist;//新链表的尾部
	while(list1 && list2)
    {
	   if(list1->_data_data)
	  {
		 tail->next=list1;
		 list1=list1->next;
		 tail=tail->next;
	   }
	  else
	  {
		tail->next=list2;
		list2=list2->next;
		 tail=tail->next;
	   }
	}
	if(list1==NULL)
	{
		tail->next=list2;
	}
	else
	{
		tail->next=list1;
	}
	return newlist;
	
}
//查找单链表的中间节点,要求只能遍历一次链表 
pNode FindMidNode(pList plist)
{//定义两个指针分别为fast和slow,fast每次跳两下,slow每次一下,
//当fast指向链表尾部时,slow所指的位置就是中间结点
    pNode fast=plist;
    pNode slow=plist;
    while((fast) && (fast->next))//链表的结点个数存在奇数和偶数
	{
		fast=(fast->next)->next;
		slow=slow->next;
	}
	return slow;
}

//查找单链表的倒数第k个节点,要求只能遍历一次链表 
pNode FindKNode(pList plist, int k)
{//定义两个指针分别为fast和slow,
//使fast先走k-1步,之后slow再走,当fast指向尾部时,slow所指的就是倒数第k个元素
	pNode fast=plist;
	pNode slow=plist;
    while(fast && fast->next)
	{
		if(--k<=0)
		{
		   slow=slow->next;
		  }
		fast=fast->next;
	}
	if(k<=0)
	{
		return slow;
	}
	return NULL;
}
//查找单链表的倒数第k个节点,要求只能遍历一次链表 
pNode FindKNode(pList plist, int k)
{  
	pNode fast=plist;
	pNode slow=plist;
	int i=0;
	for(i=0;inext)
		{
			fast=fast->next;
		}
		else
		{
			break;
		}
	}
	if(inext)
		{
			fast=fast->next;
			slow=slow->next;
		}
		return slow;
	}
}
pNode CheckCircle(pList plist) //判断链表是否带环 
{
    pNode fast=plist;
	pNode slow=plist;
	while(fast && fast->next)//当fast=slow时代表有环
	{
		fast=(fast->next)->next;
		slow=slow->next;
		if(fast==slow)
		{
			return fast;
		}
    }
	return NULL;
}
int GetCircleLength(pNode meet) //求环的长度
{  //从相遇点转一圈的距离
	pNode pnode=meet->next;//meet为fast和slow的相遇点
	int i=1;
	while(pnode!=meet)
	{
		pnode=pnode->next;
		i++;
	}
	return i;
}
pNode GetCycleEntryNode(pList plist, pNode meet) //求环的入口点 
{
	pNode pnode=plist;
	if((plist==NULL)&&(meet==NULL))
	{
		return NULL;
	}
	while(pnode!=meet)
	{
		meet=meet->next;
		pnode=pnode->next;
	}
	return meet;     
}
int CheckCross(pList list1, pList list2)//判断两条单链表是否相交  
{
	pNode p1=list1;
	pNode p2=list2;
	while(p1 && p1->next)
	{
		p1=p1->next;
	}
	while(p2 && p2->next)
	{
		p2=p2->next;
	}
	if((p1==p2)&&(p1!=NULL))
	{
		return 1;
	}
	return -1;
}

pNode GetCrossNoCycle(pList list1, pList list2)//转化成不带环的链表求解  
{   
	pNode meet=NULL;
	pNode cur=list1;
	pNode crossNode=NULL;
	while(cur&&cur->next)
	{
		cur=cur->next;
	}
	cur->next=list2;
    meet=CheckCircle(list1); 
    crossNode=GetCycleEntryNode(list1,meet);
	return crossNode;
}

pNode GetCycleFrontEntryNode(pList list,pNode meet)//求入口点的前一个结点
{
	pNode cur=list;
	pNode p=GetCycleEntryNode(list,meet);//环的入口点
	while(cur->next!=p)
	{
	   cur=cur->next;
	}
	return cur;
}
//在两个链表已经相交的前提下,转化成不带环的链表求解 
pNode GetCrossNode(pList list1, pList list2) //求交点
{
	pNode p1=CheckCircle(list1);//判断是否带环,相遇点
    pNode p2=CheckCircle(list2);//判断是否带环
	pNode crossNode=NULL;
	if(p1==NULL && p2==NULL)//list1不带环
	{
		crossNode=GetCrossNoCycle(list1,list2);
		return crossNode;
	}
   else if (p1&&p2)                    //如果两个链表都带环  
    {  
        pNode endNode= NULL;  
        pNode cur= NULL;
        endNode=GetCycleFrontEntryNode(list1,p1);//找到环入口的前一个结点  
        cur=endNode->next; //保存环的入口  
        endNode->next = NULL; //将环断开拆分成两个不带环的链表  
        crossNode=GetCrossNoCycle(list1,list2); //获得交点  
        endNode->next = cur;                         //复原链表  
        
	} 
	return crossNode;
}
void Display(pNode plist)
{

	pNode cur=plist;
	while(cur)
	{
		printf("%d->",cur->_data);
		cur=cur->next;
	}
	printf("NULL\n");
}
void Destory(pNode *pplist)
{
	pNode del=NULL;
	pNode cur=NULL;
    assert(pplist);
	 del=*pplist;
     cur=*pplist;
	 while(cur)
	 {
		 del=cur;
		 cur=cur->next;
		 free(del);
	 }
	 del=NULL;
	 *pplist=NULL;
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值