目录
1.链表分割
我们需要将比x小的链接lessTail保存在一起,将比x大的链接greaterTail保存在一起。
最后lessTail的链接指向greaterTail的第一个元素 ,且我们要返回新连接的头指针
故此还需要定义一个lessHead和greaterHeat
开一个哨兵位头结点,方便尾插
struct ListNode* lessHead, * lessTail, * greaterHead, * greaterTail;
//开一个哨兵位头结点,方便尾插
lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
lessTail->next = NULL;
greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
greaterTail->next = NULL;
如何进行比较?
定义一个cur ,将cur->val 与 x 进行比较。
如果比x小,那便让lesstail 链接到 cur上,并将lesstail走到cur处。 结束后 cur = cur->next
如果比x大,那便让greatertail 链接到 cur上,并将greatertail走到cur处。 结束后 cur = cur->next
struct ListNode* cur = pHead;
while (cur)
{
if (cur->val < x) {
lessTail->next = cur;
lessTail = cur;
}
else {
greaterTail->next = cur;
greaterTail = cur;
}
cur = cur->next;
}
比较完后,便需要将两者链接起来,由于我们开头都定义了一个哨兵位,故此是将
lessTail->next = greaterHead->next;
返回的lessHead的地址,但结束后我们需要 free(lessHead)和free(greaterHead)的空间,所以我还需要以将lessHead的地址保存下来
lessTail->next = greaterHead->next;
greaterTail->next = NULL;
struct ListNode* newHead = lessHead->next;
free(lessHead);
free(greaterHead);
return newHead;
完整代码如下:
ListNode* partition(ListNode* pHead, int x) {
struct ListNode* lessHead, * lessTail, * greaterHead, * greaterTail;
lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
lessTail->next = NULL;
greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
greaterTail->next = NULL;
struct ListNode* cur = pHead;
while (cur)
{
if (cur->val < x) {
lessTail->next = cur;
lessTail = cur;
}
else {
greaterTail->next = cur;
greaterTail = cur;
}
cur = cur->next;
}
lessTail->next = greaterHead->next;
greaterTail->next = NULL;
struct ListNode* newHead = lessHead->next;
free(lessHead);
free(greaterHead);
return newHead;
}
2. 链表的回文结构
此题思路我只需要将原链表逆置保存到一个新的链表中,在于原链表一一对比,若相一致便是回文结构,不一致便返回false
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* newhead = NULL;
struct ListNode* cur = head;
while(cur)
{
struct ListNode* next = cur->next;
//头插
cur->next = newhead;//将newhead看作首地址
newhead = cur;
//迭代往后走
cur = next;
}
return newhead;
}
bool chkPalindrome(ListNode* A)
{
// write code here
struct ListNode* curA = A;
struct ListNode* curR = reverseList(A);
while(curA && curR)
{
if(curA->val != curR->val){
return false;
}
else{
curA = curA->next;
curR = curR->next;
}
}
return true;
}
3.相交链表
力扣https://leetcode.cn/problems/intersection-of-two-linked-lists/description/
此处,我们判断A和B链表相交,只需要判断他们是否出现地址相同即可。
两个链表都先遍历第一遍,如果尾部的数据地址不相同,那必然没有相交,返回NULL即可,倘若尾部地址相同,
那么我们需要让他们同时走到地址相同处,但我们又不知道他们的长度如何,所以两者都是一步一步的迭代,但是长的链表要先迭代他们的长度之差步。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* tailA = headA;
struct ListNode* tailB = headB;
int lenA = 1;
int lenB = 1;
while(tailA->next){
++lenA;
tailA = tailA->next;
}
while(tailB->next){
++lenB;
tailB = tailB->next;
}
if(tailA != tailB){
return NULL;
}
int gap = abs(lenA-lenB);
//让长的先走gap步
//假设 A 长B短,再判断,如果A短B长则相反赋值
struct ListNode* longList = headA;
struct ListNode* shorList = headB;
if(lenA < lenB){
longList = headB;
shorList = headA;
}
while(gap--){
longList = longList->next;
}
while(longList != shorList){
longList = longList->next;
shorList = shorList->next;
}
return longList;
}
4.判断环形链表
力扣https://leetcode.cn/problems/linked-list-cycle/description/设定一快一慢两个链表, slow每次迭代一步,fast每次迭代两步。
如果存在环形链表,那么slow和fast必定会在环形中相遇。
bool hasCycle(struct ListNode *head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
return true;
}
}
return false;
}
5.环形链表II
此处主要是需要证明一个数学问题,证明在下面
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
//相遇
struct ListNode* meet = slow;
while(meet != head){
meet = meet->next;
head = head->next;
}
return meet;
}
}
return NULL;
}
延展问题
1.为什么slow 和 fast一定会在环内相遇?会不会在环内错过,永远遇不上? 证明一下
结论:一定会相遇
分析证明:
第一步: slow和fast,fast一定先进环,这是 slow走了fast进环前的一半路程
第二步: 随着slow进环,fast已经在环里面走了一段,走了多少未知
假设fast进环的时候,slow与fast的距离为N,fast开始追slow
slow往前走一步,fast往前走2步,每追一次,判断一次相距
每追一次,fast和slow的距离变化为n-1、n-2、n-3 直至变成0时相遇
如何求相遇点?
第一种方法:
一个指针从相遇点开始走,一个指针从链表头开始走,他们会在环的入口点相遇
追上的过程中:(L是环前的链表前度,C是环的长度,X是慢指针在和快指针的相遇点)
慢指针走的距离:L+X
快指针走的距离:L+N*C+X
快指针走的距离是慢指针的两倍:
2(L+X) = L+N*C+X(N>=1)
L = N*C-X
L = (N-1)*C + C-X;
整数圈
第二种方法:
转换为链表相交问题
将相遇点指向NULL
从相遇点的下一个节点 当作新的链表,走到相遇点
再从原链表的表头开始走,走到fast和slow相遇点,
中间两者地址相同的地方,就是环的第一个节点
延展问题
2.为什么slow走一步,fast走两步?能不能fast一次走3、4、5、n步?请证明一下
结论:fast一次走n步,n>2 不一定会相遇
假设 fast一次走三步,
slow进环后,fast开始追slow,距离为N
他们间的距离变化:
N、N-2、N-4……
如果N是 偶数 …… 2、0 可以追上
如果N是 奇数 …… 1、-1一次追不上。距离变成-1,意味着他们之间的距离变成C-1(C是环的长度)
如果C-1 是奇数,那么永远追不上,如果C-1是偶数,那么就可以追上
6.复制带随机指针的链表
1.先复制每一个节点,插入原节点的后面,
2.将copy的random指向原链表的random的下一个
3.最后将原链表与复制的链表分别链接
struct Node* copyRandomList(struct Node* head) {
//1.复制节点,插入到每一个原节点的后面
struct Node* Cur = head;
while(Cur)
{
struct Node* Copy = (struct Node*)malloc(sizeof(struct Node));
Copy->val = Cur->val;
//插入copy点
Copy->next = Cur->next;
Cur->next = Copy;
Cur = Copy->next;
}
//2.根据原节点,处理copy节点的random
Cur = head;
while(Cur)
{
struct Node* Copy = Cur->next;
if(Cur->random == NULL){
Copy->random = NULL;
}
else{
Copy->random = Cur->random->next;
}
Cur = Copy->next;
}
//把拷贝节点解下来,连接成新链表,同时恢复原链表
struct Node* CopyHead = NULL, *CopyTail = NULL;
Cur = head;
while(Cur)
{
struct Node* Copy = Cur->next;
struct Node* next = Copy->next;
if(CopyTail == NULL){
CopyHead = CopyTail = Copy;
}
else{
CopyTail->next = Copy;
CopyTail = Copy;
}
Cur->next = next;
Cur = next;
}
return CopyHead;
}