在面试中,关于链表的考察,大多都是结合题目来对链表进行综合考察。链表的一些应用一般为以下几种。
- 单链表反转
- 链表中环的检测
- 两个有序的链表合并
- 删除链表倒数第n个节点
- 求删除的中间节点
- 两两交换链表中的节点
单链表反转
//解法一
struct ListNode* reverseList(struct ListNode* head) {
//定义三个指针
struct ListNode *nodePre,*nodeCur,*nodeNext;
//为三个指针开辟空间
nodePre = (struct ListNode*)malloc(sizeof(struct ListNode) * 1);
nodeCur = (struct ListNode*)malloc(sizeof(struct ListNode) * 1);
nodeNext = (struct ListNode*)malloc(sizeof(struct ListNode) * 1);
//如果链表为空或者只有一个节点,直接返回head
if(head == NULL || head->next == NULL)
return head;
//将pre,cur,next分别指向链表的第一、第二和第三个节点
nodePre = head;
nodeCur = head->next;
nodeNext = nodeCur->next;
//当前节点不为空
while(nodeCur)
{
//当前节点的下一个节点变为前一个节点
nodeCur->next = nodePre;
//将第一、第二个节点往后移
nodePre = nodeCur;
nodeCur = nodeNext;
//如果第三个节点不为空,就往后移
if(nodeNext)
nodeNext = nodeNext->next;
}
//此时反转完成,head节点为最后一个节点
head->next = NULL;
//将头节点指向最后一个节点
head = nodePre;
return head;
}
//解法一
struct ListNode* reverseList(struct ListNode* head) {
//将前一个节点置为空,当前节点指向头节点
struct ListNode *prev = NULL,*cur = head;
//当当前节点不为空时
while(cur)
{
//nextNode指向当前节点的下一个节点
ListNode *nextNode = cur->next;
//当前节点的下一个节点指向上一个节点
cur->next = prev;
//将当前节点和前一个节点都往后移
prev = cur;
cur = nextNode;
}
return prev;
}
链表中环的检测
//给定一个链表,判断链表中是否有环
bool hasCycle(struct ListNode *head) {
//定义快慢指针
struct ListNode *slow,*fast;
//初始将快慢指针都指向头节点
slow = head;
fast = head;
//当慢节点不为空且指针的下一个不为空时
//(因为里面要执行快指针走两步,所以要判断快指针的下一个不为空)
while(slow != NULL && fast->next != NULL){
//快指针走两步,慢指针走一步
slow = slow->next;
fast = fast->next->next;
//如果快指针等于慢指针,说明存在换
if(slow == fast)
return true;
//如果快指针等于空,说明不存在环
if(fast == NULL)
return false;
}
return false;
}
两个有序的链表合并
//合并两个链表,并将合并后的链表返回
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
//如果l1链表为空,就返回l2链表
if(!l1)
return l2;
//如果l2为空,返回l1
if(!l2)
return l1;
//创建一个新节点,并指向空
struct ListNode *newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode = NULL;
//如果l1节点的值小于等于l2节点的值
if(l1->val <= l2->val){
//新节点指向l1
newNode = l1;
//新节点的下一个节点指向递归返回的值(因为此函数一直在做的就是找到两个对应的值,哪个最小)
newNode->next = mergeTwoLists(l1->next,l2);
}else{
//否则新节点指向l2
newNode = l2;
newNode->next = mergeTwoLists(l1,l2->next);
}
return newNode;
}
删除链表倒数第n个节点
//给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//创建哨兵(防止对头节点进行处理时出错)
struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
//哨兵节点的下一个节点定义为头节点
dummy->next = head;
//定义慢节点和快节点,并指向哨兵
struct ListNode *slowNode = dummy;
struct ListNode *fastNode = dummy;
//当快节点的下一个节点不为空时
while(fastNode->next != NULL)
{
//当n<=0时,往后移动慢节点
if(n<=0)
slowNode = slowNode->next;
//移动快节点
fastNode = fastNode->next;
//每次n--
n--;
}
//当慢节点的下一个节点不为空时
if(slowNode->next != NULL)
//慢节点的下一个节点指向下下个节点,下个节点即为要删除的节点
slowNode->next = slowNode->next->next;
//返回哨兵的下一个节点,即头节点
return dummy->next;
}
求删除的中间节点
//给定一个带有头结点 head 的非空单链表,返回链表的中间结点
struct ListNode* middleNode(struct ListNode* head) {
//定义防止哨兵节点(防止对头节点进行处理时出错)
struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
//将哨兵节点的下一个节点指向头节点
dummy->next = head;
//统计链表的长度
int length = 0;
//利用哨兵节点统计链表的长度
while(dummy->next != NULL)
{
dummy->next = dummy->next->next;
length++;
}
//求出链表的中间位置
length /= 2;
//将哨兵节点重新指向头节点
dummy->next = head;
//利用链表的中间位置索引,求出链表的中间的指针
while(length--)
dummy->next = dummy->next->next;
return dummy->next;
}
//解法二
struct ListNode* middleNode(struct ListNode* head) {
//设置快慢节点
struct ListNode *slow,*fast;
//将快慢节点都指向头节点
slow = head;
fast = head;
//当慢节点不为空并且快节点的下一个节点不为空
//(因为要使用要快节点的下下个节点,所以要确保快节点的下一个节点不为空)
while(fast != NULL && fast->next != NULL){
//慢节点移动一次,快节点移动两次
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
两两交换链表中的节点
//给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
struct ListNode* swapPairs(struct ListNode* head) {
//如果链表为空表或者只有一个节点,就直接返回
if(!head || !head->next)
return head;
//定义临时节点tmp,并且指向头节点的下一个节点
struct ListNode *temp = head->next;
//头节点的下一个节点指向后面一组两两交换后的地址
head->next = swapPairs(temp->next);
//第二个节点的下一个节点指向头节点
temp->next = head;
return temp;
}
代码(题目链接):
https://github.com/Han-YLun/DataStructureAndAlgorithm
总结:
很多人也许一看到链表为头疼,但是链表是我们学习和掌握数据结构跨不过去的坎。我们只有多加练习,了解链表真正的含义,才能真正的掌握。