头文件内容:
#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;
}
};
若有任何问题,请读者在评论区积极留言,谢谢!!!