合并两个有序表、判断链表的回文结构、相交链表、环的链表一和二

目录

一、OJ题目

1、

2、

3、

4、

5、

一、OJ题目

1、

思路1:将结点放在指定位置之前

思路2:创建空链表

时间复杂度:O(n)

cpp 复制代码
typedef struct ListNode ListNode;
struct ListNode * mergewoLists(struct ListNode * list1,struct ListNode * list2)
{
 if(list1 == NULL)
 {
  return list2;
 }
 if(list2 == NULL)
 {
  return list1;
 }
//创建空链表
 ListNOde * newHead,*newTail;
 newHead = newTail = NULL;
 
 ListNode * l1 = list1;
 ListNode * l2 = list2;
 
 while(l1 && l2)
 {
  if(l1->val < l2->val)
  {
//l1尾插到新链表中
   if(newHead == NULL)
   {
    newHead = newTail = l1;
   }
   else
   {
//链表不为空
    newTail->next = l1;
    newTail = newTail->next;
   }
   l1 = l1->next;
  }
  else
  {
//l2尾插到新链表中
   if(newHead == NULL)
   {
    newHead = newTail = l2;
   }
   else
   {
//链表不为空
    newTail->next = 12;
    newTail = newTail->next;
   }
   l2 = l2->next;
  }
 }
//要么l1为空,要么l2为空
 if(l2)
 {
  newTail->next = l2;
 }
 if(l1)
 {
  newTail->next = l1;
 }
 return newHead;
}

因为代码太过冗余,因此简化为

cpp 复制代码
typedef struct ListNode ListNode;
struct ListNode * mergewoLists(struct ListNode * list1,struct ListNode * list2)
{
 if(list1 == NULL)
 {
  return list2;
 }
 if(list2 == NULL)
 {
  return list1;
 }
//创建非空链表
 ListNode * newHead,*newTail;
 newHead = newTail = (ListNode *)malloc(sizeof(ListNode));
 
 ListNode * l1 = list1;
 ListNode * l2 = list2;
 
 while(l1 && l2)
 {
  if(l1->val < l2->val)
  {
//l1尾插到新链表中
   newTail->next = l1;
   newTail = newTail->next;
   l1 = l1->next;
  }
  else
  {
//l2尾插到新链表中
   newTail->next = l2;
   newTail = newTail->next;
   l2 = l2->next;
  }
 }
//要么l1为空,要么l2为空
 if(l2)
 {
  newTail->next = l2;
 }
 if(l1)
 {
  newTail->next = l1;
 }
 ListNode * retHead = newHead->next;
 free(newHead);
 newHead = NULL;
 return retHead;
}

2、

思路1:投机取巧创建一个数组存放从头遍历下的数据元素

cpp 复制代码
bool chkPailndrome(ListNode * A)
{
//创建数组------900
 int arr[900] = {0};
//遍历链表将链表中的值保存在数组中
 ListNode * pcur = A;
 int i = 0;
 while(pcur)
 {
  arr[i++] = pcur->val;
  pcur = pcur->next;
 }
//判断数组是否为回文结构
 int left = 0;
 int right = i - 1;
 while(lsft < right)
 {
  if(arr[left] != arr[right])
  {
   return false;
  }
  left++;
  right--;
 }
 return ture;
}

这种方法治标不治本

思路2:找链表的中间结点,将中间结点作为新的链表的头结点进行反转链表

cpp 复制代码
//找中间结点
ListNode * middleNode(ListNode * head)
{
 ListNode * slow = *fast;
 slow = fast = head;
 while(fast && fast->next)
 {
  slow = slow->next;
  fast = fast->next->next;
 }
 return slow;
}

//反转链表
ListNode * reverseList(ListNode * head)
{
//链表为空
 if(head == NULL)
 {
  return head;
 }
//创建三个指针
 ListNode * n1,n2,n3;
 n1 = NULL, n2 = head, n3 = n2->next;
 while(n2)
 {
  n2->next = n1;
  n1 = n2;
  n2 = n3;  
  if(n3) 
  {
   n3 = n3->next;
  }
 }
 return n1;
}

bool chkPailndrome(ListNode * A)
{
//中间结点
 ListNode * mod = middleNode(A);
//反转中间结点之后的链表
 ListNode * right = reverseList(mid);
//遍历原链表和反转后的链表
 ListNode * left = A;
 while(right)
 {
  if(left->val != right->val)
  {
   return false;
  }
  left = left->next;
  right = right->next;
 }
 return true;
}

3、

cpp 复制代码
sutrct ListNode 
{
 int val;
 struct ListNode * next;
};

typedef struct ListNode ListNode;

struct ListNode *getInsertsectionNode(struct ListNode * headA,struct ListNode * headB)
{
//求两个链表的长度
 ListNode * pa = headA;
 ListNode * pb = headB;
 int sizeA = 0;
 int sizeB = 0;
 while(pa)
 {
  sizeA++;
  pa = pa->next;
 }
 while(pb)]
 {
  sizeB++;
  pb = pb->next;
 }
//计算长度差
 int gap = abs(sizeA-sizeB);
//让长链表先走gap步
 ListNode * shortList = headA;
 ListNode * longList = headB;
 if(sizeA > sizeB)
 {
  longList = headA;
  shortList = headB;
 } 
 while(gap--)
 {
  longList = longList->next;
 }
//longList shortList在同一起跑线
 while(shortList)
 {
  if(longList == shortList)
  {
   return longList;
  }
  shortList = shortList->next;
  longList = longList->next;
 }
 return NULL;
}

4、

思路:快慢指针,在环里追逐,若链表带环,快慢指针一定会相遇

cpp 复制代码
typedef struct ListNode ListNode;

bool hasCycle(struct ListNode * head)
{
//快慢指针
 ListNode * slow = head;
 ListNode * fast = head;
 while(fast && fast->next)
 {
  slow = slow->next;
  fast = fast->next->next;
  if(slow == fast)
  {
//相遇
   return true;
  }
 }
 return false;
}

为什么在环形链表中快慢指针一定会在环里面一定会相遇

慢指针每次走一步,快指针每次走两步

我们假设fast和slow之间差距n个距离,slow和fast每次走的步数之间的距离就会缩减一步,当在第n次时就会重合

那如果在环形链表中,慢指针每次走一步,但是快指针每次走3,4,5,6,...步,快慢指针在环中是否还会相遇?
我们既设慢指针slow刚入环时,此时slow和fast之间的距离达到最大为N,每次的步数差为2

如果N为偶数,之间的步数会随着临近的次数而减少,N、N-2、N-4、...4、2、0,所以会相遇

如果N为奇数,同理,N、N-2、N-4、...3、1、C-1、出现了套圈。(假设一圈为N)

N为奇数:进入下一圈。若C-1为奇数,即C为偶数,一定不会相遇

若C-1为偶数,即C为奇数,一定会相遇

而在上面的假设中快指针走的总路程时慢指针的3倍(3 * 慢指针 = 快指针)设头部结点到入环结点的距离为L,则有头部节点到慢指针的方程为L,头部节点到快结点的方程为L+C-N+nc,因为快指针 = 3 * 慢指针,则有方程3L = L + C - N + nC,即2L = (n + 1)C - N,则C为偶数,N为偶数。若C为奇数,N为奇数,满足C-1为偶数,假设成立

结论:不管快指针不论走了多少步都可以满足再带环来年表中相遇。

但是在编写代码的时候会有额外的步骤引入,设计到快慢指针的算法体重通常习惯使用慢指针走一步快指针走两步的方式

5、

仔细观察的话,可以得出的结论是快慢指针相遇点和头结点到入环起始结点的距离是相等的

cpp 复制代码
struct ListNode
{
 int val;
 struct ListNode * next;
}

typedef struct ListNode ListNode;

struct ListNode *deteCycle(struct ListNode * head)
{
//快慢指针
 ListNode * slow = head;
 ListNode * fast = head;
 while(fast && fast->next)
 {
  slow = slow->next;
  fast = fast->next->next;
  if(slow == fast)
  {
   //找相遇点
   //相遇点和头结点到入环第一个结点距离相等
   ListNode * pcur = head;
   //pcur slow/fast
   while(pcur != slow)
   {
    pcur = pcur->next;
    slow = slow->next;
   }
   //pcur == slow
   return pcur;
  }
 }
 return NULL;
}

证明:

我们这假设快指针走的路程是慢指针的两倍,头部结点到入环第一个结点的距离为L,快慢指针到入环的第一个结点的距离为x,入环的一圈距离为R,则

2 * slow = fast

slow : L + x

fast : L + x + nR

代入公式得

2(L + x)= L + x + nR

L + x = nR

L = nR - x

L = (n-1)R + R - x

因为R套圈不影响方程两边,则L = R-x 。假设成功,即快慢指针相遇点和头结点到入环起始结点的距离是相等的

相关推荐
Rust语言中文社区2 小时前
【Rust日报】2026-04-24 Vizia 0.4 发布——纯 Rust 声明式响应式 GUI 框架
开发语言·后端·rust
木井巳2 小时前
【递归算法】解数独
java·算法·leetcode·决策树·深度优先·剪枝
普通网友2 小时前
JavaScript:ESLint+Prettier 规范代码格式
开发语言·javascript·ecmascript
t***5442 小时前
如何在 Dev-C++ 中切换编译器
java·开发语言·c++
jiayong232 小时前
第 38 课:任务列表里高亮当前正在查看详情的任务
开发语言·前端·javascript·vue.js·学习
大肥羊学校懒羊羊2 小时前
完数与盈数的计算题解
数据结构·c++·算法
澈2072 小时前
构造函数与析构函数完全指南
开发语言·c++
阿Y加油吧2 小时前
算法实战笔记:LeetCode 31 下一个排列 & 287 寻找重复数
笔记·算法·leetcode
穿条秋裤到处跑2 小时前
每日一道leetcode(2026.04.24):距离原点最远的点
算法·leetcode·职场和发展