单向链表常见的笔试面试题

定义一个结构体,表示链表上每个节点的类型

typedef char LinkType;

typedef struct LinkNode {
	LinkType data;
	struct LinkNode* next;
} LinkNode;

1)逆序打印单链表.

 void LinkListReversePrint(LinkNode* head)
 {
     if(head == NULL)
     {
         return;
     }
     LinkListReversePrint(head->next);
     printf("[%c : %p] ",head->data,head);
 } 

这里使用递归的方法,递归出口 -> head == NULL

2 )不允许遍历链表, 在 pos之前插入

void LinkListInsertBefore(LinkNode** head, LinkNode* pos, LinkType value)
 {
     if(head == NULL)
     {
         return;
     }
     if((*head) == NULL)
     {//当前链表为空,直接return
         return;
     }  
     LinkNode* new_node = CreateLinkNode(value);
     if(pos == (*head))
     {//如果head就是要找的位置,直接进行头插
         new_node->next = (*head);
         (*head) = new_node;
         return;
     }  
     //以下使用移花接木的方法,将新插入的value赋值给pos的data,再在pos之后插入新的元素
     new_node->data = pos->data;
     pos->data = value;
     new_node->next = pos->next;                                                                                                                   
     pos->next = new_node;
 }

单向链表常见的笔试面试题_第1张图片

3)约瑟夫环

 LinkNode* JosephCycle(LinkNode* head, size_t food) {
     if(head == NULL)
     {
         return NULL;
     }
     LinkNode* cur = head;
     while(cur != cur->next)
     {
        size_t i = 1;
        for(;i < food; ++i)
        {
            cur = cur->next;
        }
        printf("删除的元素是 : %c\n",cur->data);
        LinkNode* to_delete = cur->next;
        cur->data = cur->next->data;
        cur->next = to_delete->next;
        DestoryLinkNode(to_delete);
     }
     printf("幸存者 : %c\n",cur->data);                                                                                                            
     return cur;
 }

每次找到要删除的元素是,同样使用第二题移花接木的方法删除,这样就不用再找当前元素的头结点

4)单链表逆置

void LinkListReverse(LinkNode** head)
 {
     if(head == NULL)                                                                                                                              
     {
         return;
     }
     if((*head) == NULL)
     {
         return;
     }
     if((*head)->next == NULL)
     {
         return;
     }
     LinkNode* pre = (*head);
     LinkNode* cur = pre->next;
     LinkNode* tail = cur->next;
     while(tail != NULL)
     {
         cur->next = *head;
         pre->next = tail;
         (*head) = cur;
         cur = tail;
         tail = cur->next;
     }
     cur->next = (*head);
     (*head) = cur;
     pre->next = NULL;
     return;
 }

5 )单链表的冒泡排序

void LinkListBubbleSort(LinkNode* head)
{
	if (head == NULL)
	{
		return;
	}
	if (head->next == NULL)
	{//只有一个节点
		return;
	}
	LinkNode *cur = head;
	while (cur->next != NULL)
	{
		cur = cur->next;
	}//cur指向最后一个元素
	LinkNode* out = head;
	LinkNode* in = head;
	for (; out ->next!= NULL; out = out->next)
	{
		for (in = head; in != cur; in = in->next)
		{
			if (in->data > in->next->data)
				Swap(&(in->data), &(in->next->data));
		}
		cur = in;
	}
}

6)将两个有序链表, 合并成一个有序链表

LinkNode* LinkListMerge(LinkNode* head1, LinkNode* head2)
{
	if (head1 == NULL && head2 == NULL)
	{//两个空链表
		return NULL;
	}
	else if (head1 == NULL && head2 != NULL)
	{
		return head2;
	}
	else if (head1 != NULL && head2 == NULL)
	{
		return head1;
	}

	LinkNode *cur1 = head1;
	LinkNode *cur2 = head2;
	LinkNode *new_head = NULL;
	LinkNode *tail = NULL;

	//确定新的链表的头结点,应指向两个链表中的较小的
	if (cur1->data > cur2->data)
	{
		new_head = tail = cur2;
		cur2 = cur2->next;
		if (cur2 == NULL)
		{
			tail->next = cur1;
			return new_head;
		}
	}
	else
	{
		new_head = tail = cur1;
		cur1 = cur1->next;
		if (cur1 == NULL)
		{
			tail->next = cur2;
			return new_head;
		}
	}
	while (cur1 != NULL && cur2 != NULL)
	{
		if (cur1->data > cur2->data)
		{//cur1的值较小,先将cur1装入新链表
			tail->next = cur2;
			tail = cur2;
			cur2 = cur2->next;
		}
		else
		{
			tail->next = cur1;
			tail = cur1;
			cur1 = cur1->next;
		}
	}

	if (cur1 == NULL && cur2 != NULL)//cur2还没有走完
	{
		tail->next = cur2;
	}
	else if (cur1 != NULL && cur2 == NULL)//cur1还没走完
	{
		tail->next = cur1;
	}
	return new_head;
}

7)找到链表中间的节点

LinkNode* FindMidNode(LinkNode* head)
 {
     if(head == NULL)
     {
         return NULL;
     }
     if(head->next == NULL)
     {
         return NULL;
     }
     LinkNode* quick = head;
     LinkNode* slow = head;
     while(quick->next != NULL)
     {
         quick = quick->next;
         if(quick ->next == NULL)
         {
             break;
         }
         quick = quick->next;
         slow = slow->next;
     }                                                                                                                                             
     return slow;
 }

这里使用快慢指针的方法,快指针每次走两步,慢指针每次走一步,当快指针走到链表的末尾时,慢指针刚好走到链表的一半

8)找到倒数第 K 个节点.

LinkNode* FindLastKNode(LinkNode* head, size_t K)
{
	if (head == NULL)
	{//空链表
		return NULL;
	}
	if (K > LinkListSize(head))
	{//非法输入
		return NULL;
	}
	LinkNode* quick = head;
	LinkNode* slow = head;
	while (K--)
	{
		quick = quick->next;
	}
	while (quick != NULL)
	{
		quick = quick->next;
		slow = slow->next;
	}
	return slow;
}

这里也定义两个指针,快指针先走K步,之后两个指针都从当前位置向后走,当快指针走到了链表末尾,满指针的位置就是倒数第K个节点

9)删除倒数第K个节点

void EraseLastKNode(LinkNode** head, size_t K)                                                                                 
 {
     if(head == NULL)
     {
         return;
     }
     if((*head) == NULL)
     {
         return;
     }
     LinkNode* cur = FindLastKNode((*head),K);
     if(cur == (*head))
     {
         (*head) = cur->next;
         DestoryLinkNode(cur);
         return;
     }
     if(cur->next == NULL)
     {
         LinkNode* pre = (*head);
         while(pre->next != cur)
         {
             pre = pre->next;
         }
         pre->next = NULL;
         DestoryLinkNode(cur); //这里的Destory是释放掉malloc申请的内存,防止内存泄漏
         return;
     }
     LinkNode* to_delete = cur->next;
     cur->data = to_delete->data;
     cur->next = to_delete->next;
     DestoryLinkNode(to_delete);
     return;
 }

这里同理,删除某一节点用的是移花接木的方法,不需要再去找当前节点的上一个节点

10)判定单链表是否带环. 如果带环返回当前的交点

LinkNode* HasCycle(LinkNode* head)
 {
     if(head == NULL)
     {
         return NULL;
     }
     LinkNode* quick = head;
     LinkNode* slow = head;
     while(quick->next != NULL && quick->next->next != NULL)
     {
         quick = quick->next->next;
         slow = slow->next;
         if(quick == slow)
         {
             return quick;
         }
     }
     return NULL;
 }

这里用的方法类似于快慢指针,快指针每次走两步,慢指针每次走一步,如果有环,快慢指针会相遇

注意 : 快指针走3步,满指针走1步这样的方法在有些情况下是不可以的

11) 如果链表带环, 求出环的长度

size_t GetCycleLen(LinkNode* head)
{
	LinkNode* cur = HasCycle(head);
	if (cur == NULL)
	{//没有环
		return 0;
	}
	size_t length = 1;
	LinkNode* cur1 = cur->next;
	while (cur1 != cur)
	{
		cur1 = cur1->next;
		++length;
	}
	return length;
}

利用(10)的方法求出交点,在求换的长度

12)如果链表带环, 求出环的入口

LinkNode* GetCycleEntry(LinkNode* head)  
{  
    LinkNode* cur = HasCycle(head);  
    if (cur == NULL)  
    {//没有环  
        return NULL;  
    }  
    int len = GetCycleLen(head);
    LinkNode* cur1 = head;
    LinkNode* cur2 = head;
    while(len--)
    {
        cur1 = cur1->next;
    }
    while(cur1 != cur2)
    {
        cur1 = cur1->next;
        cur2 = cur2->next;
    }
    return cur1;
}

单向链表常见的笔试面试题_第2张图片

13)判定两个链表是否相交, 并求出交点

LinkNode* HasCross(LinkNode* head1, LinkNode* head2)
{
	if (head1 == NULL || head2 == NULL)
	{//空链表
		return NULL;
	}
	size_t len1 = LinkListSize(head1);
	size_t len2 = LinkListSize(head2);
	LinkNode* cur1 = head1;
	LinkNode* cur2 = head2;
	if (len1 > len2)
	{//如果head1比head2长 K ,就让head1先走 K 步.
		size_t K = len1 - len2;
		while (K--)
		{
			cur1 = cur1->next;
		}
	}
	else if (len2 > len1)
	{//如果head2比head1长 K ,就让head2先走 K 步.
		size_t K = len2 - len1;
		while (K--)
		{
			cur2 = cur2->next;
		}
	}

	//当两个链表剩余长度相同时,继续往下走,如果有 cur1==cur2 的情况,证明有交点.
	//如果是因为某一个链表为空跳出循环,证明没有交点.
	while (cur1 != NULL && cur2 != NULL && cur1 != cur2)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	if (cur1 == cur2 && cur1 != NULL)
	{
		return cur1;
	}
	return NULL;
}

你可能感兴趣的:(单向链表常见的笔试面试题)