题单:
一,链表的分割
1.题目描述
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。你不需要 保留 每个分区中各节点的初始相对位置。
2.题目接口
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* partition(struct ListNode* head, int x){
}
3.题目分析
这道题的意思很简单:就是要将一个链表内的元素按照与某个数的对比结果来排个顺序,小于某个数的在左边,大于某个数的在右边。在解这道题时需要重新定义两个链表一个存大于指定值的数据(greaderHead),一个存小于指定值的数据(smallHead)。定义好两个链表以后还需要定义两个尾节点:greaderTail,smallTail。因为在接下来我们需要进行尾插。尾插之前还需要判断:
1.当原链表为NULL时直接返回即可。
2.当链表内只有一个节点时直接返回即可
3.当链表的节点大于等于两个时才执行尾插操作。
4.解题代码
第一种:不带虚拟头节点
struct ListNode* partition(struct ListNode* head, int x){
//当链表为NULL时直接返回返回head
if(head == NULL)
return head;
//当链表只有一个节点时直接返回head
if(head->next == NULL)
return head;
//当链表的节点个数大于等于2个时
struct ListNode* smallHead = NULL;
struct ListNode* smallTail = NULL;
struct ListNode* greaderHead = NULL;
struct ListNode* greaderTail = NULL;
struct ListNode* cur = head;
while(cur)
{ //每次尾插时都需要对尾节点的指向进行置空操作,这是为了避免造成环形链表的情况
if(cur->val>=x)
{
if(greaderHead == NULL)
{
greaderHead = greaderTail = cur;
cur = cur->next;
greaderTail->next = NULL;//尾节点置空
}
else
{
greaderTail->next = cur;
greaderTail = greaderTail->next;
cur = cur->next;
greaderTail->next = NULL;//尾节点置空
}
}
else
{
if(smallHead == NULL)
{
smallHead = smallTail = cur;
cur = cur->next;
smallTail->next = NULL;//尾节点置空
}
else
{
smallTail->next = cur;
smallTail = smallTail->next;
cur = cur->next;
smallTail->next = NULL;//尾节点置空
}
}
}
if(smallTail)//当小的链表不为NULL时要与greaderHead链接返回
{
smallTail->next = greaderHead;
return smallHead;
}
return greaderHead;//当smallTail等于NULL时直接返回greaderHead即可
}
二,回文链表
1.题目描述
给定一个链表的 头节点
head
,请判断其是否为回文链表。如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
2.题目接口
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool isPalindrome(struct ListNode* head){
}
3.题目分析
判断是否是回文链表就像判断是否是回文数一样看前后的数据是否对称,在这里解题的步骤有三个:
1.找到中间节点。
2.从后到中间节点依次反转。
3.前后比较数值是否相同判断是否是回文链表。
4.解题代码
struct ListNode* GetMid(struct ListNode* head)//计算中间节点
{ struct ListNode* dumy = ( struct ListNode*)malloc(sizeof( struct ListNode));
dumy->next = head;
struct ListNode* fast = dumy;
struct ListNode* slow = dumy;
while(fast&&fast->next){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
struct ListNode* reverse(struct ListNode* newhead){//将中间节点后面的节点反转
struct ListNode* pre = NULL;
struct ListNode* temp = NULL;
struct ListNode* cur = newhead;
while(cur){
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
bool isPalindrome(struct ListNode* head){
struct ListNode* mid = GetMid(head);
struct ListNode* cur = mid->next;
struct ListNode* newhead = NULL;
struct ListNode* tail = NULL;
while(cur){//将反转以后的链表尾插组成新的链表
if(tail == NULL){
newhead = tail = cur;
}
else{
tail->next = cur;
tail = cur;
}
cur = cur->next;
tail->next = NULL;
}
newhead = reverse(newhead);
while(newhead){//新的链表长度小于原链表,所以用新的链表来做结束的判断条件
if(newhead->val!=head->val){
return false;
}
newhead = newhead->next;
head = head->next;
}
return true;
}
三,单链表的第一个公共节点
1.题目描述
输入两个链表,找出它们的第一个公共节点。
2.题目接口
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
}
3.题目分析
这道题需要我们找到两个链表的相交节点,寻找相交节点的第一步便是要判断两个链表是否相交,若相交便找相交节点。
1.用两个节点指针cur1与cur2指向两个链表的头节点,遍历后让两个指针指向链表的尾节点并计算两个链表的长度。当两个链表的尾节点不是同一个时便可以判断两个链表不相交。
2.当两个链表相交时便可以让两个链表中长的那一个先走差距步再让两个链表一起走当两个指针指向同一个节点时便返回该节点。
4.解题代码
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
//当两个链表中有一个为NULL时便返回NULL
if(headA == NULL||headB==NULL)
{
return NULL;
}
//遍历两个链表计算两个链表各自的长度,并让遍历后的cur指向的是两个链表的最后一个节点。
struct ListNode * cur1 = headA;
struct ListNode * cur2 = headB;
int lenA = 0;
int lenB = 0;
while(cur1->next)
{
cur1 = cur1->next;
lenA++;
}
while(cur2->next)
{
cur2 = cur2->next;
lenB++;
}
//当两个链表的最后一个节点不为同一个时两链表一定不相交
if(cur1!=cur2)
{
return NULL;
}
//相交后的链表先让长的链表先走gap步,再让两个链表同时走直到指向同一个节点便返回该节点。
int gap = abs(lenA-lenB);
if(lenA>lenB)
{
while(gap)
{
headA = headA->next;
gap--;
}
}
else
{
while(gap)
{
headB = headB->next;
gap--;
}
}
while(headA)
{
if(headA==headB)
{
return headA;
}
headA = headA->next;
headB = headB->next;
}
return;
}
四,环形链表
1.题目描述
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
2.题目接口
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
}
3.题目分析
判断链表内是否有环这道题算是很简单的题了,可以想象这是两个运动员在跑步问题。当比赛的跑道是直道那慢的运动员一定是不会和快的运动员相遇的。但是如果跑道是环形的那快的运动员就可以追上慢的运动员。按照这个思路写成的代码如下:
4.解题代码
bool hasCycle(struct ListNode *head) {
//空链表直接返回NULL
if(head == NULL)
return false;
//定义快慢两个指针模拟追击问题
struct ListNode * slow = head;
struct ListNode * fast = head;
while(fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
{
return true;
}
}
return false;
}
五,寻找链表环的入口
1.题目描述
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
2.题目接口
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
}
3.题目分析
这道题的解题思路是这样子的:
1.先判断链表是否有环,若有环便找到快慢指针的相遇节点。
2.找到相遇节点以后便让一个指针从链表头走,一个指针从相遇点走。当两指针再次相遇时得到的节点就是环的起始节点。
数学问题:
其实这道题就是一个数学问题:
假设有下面的环形链表:
当我们假设环的长度为C时便有:
2*(L+X)=L+X+n*C
L+X= n*C
L=n*C-X = (n-1)*C+(C-X)
所以当两个指针,一个从相遇点一个从头节点处以相同的速度开始走时就会在环的入口处相遇。
4,解题代码
struct ListNode *detectCycle(struct ListNode *head) {
//判断是否为环并找到相遇节点。
if(head == NULL)
return NULL;
struct ListNode * fast = head;
struct ListNode * slow = head;
while(fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
break;
}
if(fast==NULL||fast->next == NULL)
{
return NULL;
}
slow = head;
while(1)
{
if(fast == slow)
{
return slow;
}
fast = fast->next;
slow = slow->next;
}
return NULL;
}