@链表面试题

头文件内容:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int DataType;

typedef struct ListNode
{
	int data;
	struct ListNode* next;
}ListNode;

一、从尾到头打印单链表。
(1)递归方法实现:递归出口为List->next == NULL;

void PrintFromEnd2Head(ListNode* List)
{
	if(NULL == List)
	{
		return;
	}
	else
	{
		PrintFromEnd2Head(List->next);
		printf("%d ",List->data);
	}
}

二、删除无头单链表的非尾节点。
(1)交换需要删除的节点和下一个节点位置的数据。(相当于交换了两个节点,即把问题转换为删除下一个节点的问题)
(2)当前节点的next指针域指向next的next节点即完成操作。在这里插入图片描述

void DelNotTailNode(ListNode* pos)
{
	assert(pos);
	if(NULL == pos->next)
	{
		return;
	}
	else
	{
		ListNode* pDel = NULL;
		int temp = 0;
		temp = pos->next->data;
		pos->data = pos->next->data;
		pos->next->data = temp;
		pDel = pos->next;
		pos->next = pos->next->next;
		free(pDel);
		pDel = NULL;
	}
}

三、在无头单链表的一个节点前插入一个节点。
(1)创建一个值与pos节点值相同的节点。
(2)将该节点插入到pos节点之后。
(3)修改pos节点的数据。
在这里插入图片描述

void InsertNoHead(ListNode* pos,DataType data)
{
	assert(pos);
	ListNode* tmp = CreatNode(pos->data);
	tmp->next = post->next;
	pos->next = tmp;
	pos->data = data;
}

四、实现约瑟夫环。
(1)构建环。
(2)循环内部记录要删除节点的前一个节点,之后需要将该节点的next指针指向删除节点的下一个节点。
在这里插入图片描述

ListNode* JosephCircle(ListNode* List,DataType K)
{
	assert(List);
	ListNode* tail = List;
	while(tail->next != NULL)
	{
		tail = tail->next;
	}
	tail->next = List;
	ListNode* cur = List;
	ListNode* pre = NULL;
	while(cur->next != cur)
	{
		int k = 0;
		k = K;
		while(--k)
		{
			pre = cur;
			cur = cur->next;
		}
		pre->next = cur->next;
		free(cur);
		cur = pre->next;
	}
	return cur;
}

五、逆置单链表。
(1)类似头删尾插操作,每次取一个节点连接在后面。
在这里插入图片描述

void Reverse(ListNode** List)
{
	if((NULL == *List)||(NULL == (*List)))
	{
		return;
	}
	ListNode* cur = *List;
	ListNode* tmp = NULL;
	ListNode* newList = NULL;
	while(cur)
	{
		tmp = cur;
		cur = cur->next;
		tmp->next = newList;
		newList = tmp;
	}
	*List = newList;
}

六、单链表排序。
(1)这里就先只写冒泡排序,其他排序后续在排序专题。
在这里插入图片描述

ListNode *ListSort(ListNode* List)
{
	if(NULL == List)
	{
		return NULL;
	}
	if(NULL == List->next)
	{
		return List;
	}	
	ListNode* tail = NULL;
	while(tail != List)
	{
		ListNode* cur = List;
		ListNode* next = cur->next;
		while(next != tail)
		{
			if(cur->data > next->data)
			{
				int tmp = cur->data;
				cur->data = next->data;
				next->data = tmp;
			}
			cur = cur->next;
			next = next->next;
		}
		tail = cur;
	}
	return List;
}

七、合并两个有序的链表,合并后依然有序。
(1)排除特殊情况。
(2)确定新链表头节点。
(3)比较大小,一次插入。
(4)拼接剩余链表。

ListNode* CombineList(ListNode* List1,ListNode* List2)
{
	if((List1 == NULL) && (List2 == NULL))
	{
		return NULL;
	}
	else if((List1 == NULL) && (List2 != NULL))
	{
		return List2;
	}
	else if((List1 != NULL) && (List2 == NULL))
	{
		return List1;
	}
	else
	{
		ListNode* newList = NULL;
		if(List1->data <= List2->data)
		{
			newList = List1;
			List1 = List1->next;
		}
		else
		{
			newList = List2;
			List2 = List2->next;
		}
		while(List1 && List2)
		{
			if(List1->data <= List2->data)
			{
				newList->next = List1;
				newList = newList->next;
				List1 = List1->next;
			}
			else
			{
				newList->next = List2;
				newList = newList->next;
				List2 = List2->next;
			}
		}
		if(List1)
		{
			newList->next = List1;
		}
		if(List2)
		{
			newList->next = List2;
		}
	}
}

八、查找单链表的中间节点,要求只能遍历一次单链表。
(1)从头开始,设置快慢指针,快指针走两步,慢指针走一步。
(2)当快指针直线终点时,慢指针指向中点。
在这里插入图片描述

ListNode* FindMidNode(ListNode* List)
{
	if((NULL == List)||(NULL == List->next))
	{
		return NULL;
	}
	if(NULL == List->next->next)
	{
		return List->next;
	}
	ListNode* fast = List;
	ListNode* slow = List;
	while(fast->next != NULL)
	{
		fast = fast->next->next;
		slow = slow->next;
	}
	return slow;
}

九、查找单链表的倒数第k个节点,要求只能遍历一次单链表。
(1)设置快慢指针,快指针先走k步。
(2)快慢指针同时走,快指针到终点时,慢指针指向倒数第k个节点。
在这里插入图片描述

ListNode* FindLastKNode(ListNode* List,DataType k)
{
	if((NULL == List)||)
	{
		return NULL;
	}
	if(List->next->next == NULL)
	{
		return List->next;
	}
	ListNode* fast = List;
	ListNode* slow = List;
	int K = k;
	while(--k)
	{
		fast = fast->next;
	}
	while(fast->next != NULL)
	{
		fast = fast->next
		slow = slow->next;
	}
	return slow;
}

十、判断单链表是否带环?若带环,求环的长度?求环的入口?并计算每个算法的时间复杂度&空间复杂度。
(1)定义快慢指针判断是否有环。
(2)快慢指针走之前,判断fast与fast->next是否为空,两个必须判断。
(3)判断指针是否相遇,相遇则有环。并记录相遇节点。
(4)求环的长度。(利用相遇节点在环内的性质)
(5)求环的入口点 。
思路:当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:
2s = s + nr
s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。

在这里插入图片描述

ListNode* EntryNodeLoop(ListNode* List)
{
	if((List == NULL)||(List->next == NULL))
	{
		return NULL;
	}
	ListNode* fast = List;
	ListNode* slow = List;
	while(fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
		if(slow == fast)
			break;
	}
	ListNode* cur = fast->next;
	int count = 1;
	while(cur != fast)
	{
		cur = cur->next;
		count++;
	}
	while(fast != List)
	{
		fast = fast->next;
		List = List->next;
	}
	return List;
}

十一、删除链表的倒数第K个节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(n--)
        {
            fast = fast->next;
        }
        if(fast == NULL)//如果走了k步变为空,那么说明它要删除的是头节点
        {
            return head->next;
        }
        
        while(fast->next)
        {
            slow = slow->next;
            fast = fast->next;
        }
        slow->next = slow->next->next;
        return head;
    }
};

若有任何问题,请读者在评论区积极留言,谢谢!!!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值