1.单链表的实现
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SlistNode* next;
}SLTNode;
SLTNode* BuyListNode(SLTDateType x); //开辟新节点
void SListPrint(SLTNode* phead); //打印
void SListPushBack(SLTNode** pphead, SLTDateType x); //尾插
void SListPushFront(SLTNode** pphead, SLTDateType x); //头插
void SListPopBack(SLTNode** pphead); //尾删
void SListPopfront(SLTNode** pphead); //头删
SLTNode* SListFind(SLTNode* phead, SLTDateType x); //查询&修改
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);//在pos位置之前去插入一个节点
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDateType x);//在pos位置后面去插入一个节点
void SListErase(SLTNode** pphead, SLTNode* pos); // 在pos指定位置删除数据
void SListEraseAfter(SLTNode* pos); // 在pos指定位置后面删除数据
void SListDestroy(SLTNode** pphead);//销毁
#include "SList.h"
//开辟新节点
SLTNode* BuyListNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//打印
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//尾插
void SListPushBack(SLTNode** pphead, SLTDateType x)
{
//动态开辟新节点的内存
SLTNode* newnode = BuyListNode(x);
//如果头节点为空指针,直接将新节点赋值
//如果phead为一级指针,对phead的赋值是形参赋值,对实参plist不影响,plist仍为空,导致后面代码无法实现
if (*pphead == NULL)
{
*pphead = newnode;
}
//找尾
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
//头插
void SListPushFront(SLTNode** pphead, SLTDateType x)
{
SLTNode* newnode = BuyListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
//尾删
void SListPopBack(SLTNode** pphead)
{
////温柔方式
//if (*pphead == NULL)
//{
// return;
//}
//粗暴方式
assert(*pphead != NULL);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
// 方法一
SLTNode* tail = *pphead;
SLTNode* prev = NULL;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
//方法二
/*SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail);
tail->next = NULL;*/
}
}
//头删
void SListPopfront(SLTNode** pphead)
{
assert(*pphead != NULL);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
//查找
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
//在pos位置之前去位置插入一个节点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
SLTNode* newnode = BuyListNode(x);
SLTNode* posPrev = *pphead;
if (*pphead == pos)//头插
{
newnode->next = *pphead;
*pphead = newnode;
}
else
{
//找到pos的前一个位置
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}
posPrev->next = newnode;
newnode->next = pos;
}
}
//在pos位置后面去插入一个节点,更适合单链表,也更简单
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
SLTNode* newnode = BuyListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
//在pos指定位置删除数据
void SListErase(SLTNode** pphead, SLTNode* pos)
{
SLTNode* cur = *pphead;
if (*pphead == pos)//头删
{
*pphead = pos->next;
free(pos);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
// 在pos指定位置后面删除数据
void SListEraseAfter(SLTNode* pos)
{
assert(pos->next);
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
next = NULL;
}
//销毁
void SListDestroy(SLTNode** pphead)
{
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
#include"SList.h"
void TestSList1()
{
SLTNode* plist = NULL; //链表为空
SListPushBack(&plist, 1);//尾插
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPrint(plist);
SListPushFront(&plist, 1);//头插
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPrint(plist);
}
void TestSList2()
{
SLTNode* plist = NULL; //链表为空
SListPushFront(&plist, 1);//头插
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPrint(plist);
SListPopBack(&plist);//尾删
SListPrint(plist);
}
void TestSList3()
{
SLTNode* plist = NULL; //链表为空
SListPushFront(&plist, 1);//头插
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPrint(plist);
SListPopfront(&plist);//头删
SListPrint(plist);
SListPopfront(&plist);
SListPrint(plist);
SListPopfront(&plist);
SListPrint(plist);
SListPopfront(&plist);
SListPrint(plist);
}
void TestSList4()
{
SLTNode* plist = NULL; //链表为空
SListPushFront(&plist, 1);//头插
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 2);
SListPushFront(&plist, 4);
SListPushFront(&plist, 2);
SListPushFront(&plist, 2);
SListPushFront(&plist, 4);
SListPrint(plist);
//找
SLTNode* pos = SListFind(plist, 2);
int i = 0;
while (pos)
{
printf("第%d个pos节点:%p->%d\n", ++i, pos, pos->data);
pos = SListFind(pos->next, 2);
}
//修改 3->30
pos = SListFind(plist, 3);
if (pos)
{
pos->data = 30;
}
SListPrint(plist);
}
void TestSList5()
{
SLTNode* plist = NULL; //链表为空
SListPushFront(&plist, 1);//头插
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 2);
SListPushFront(&plist, 4);
SListPushFront(&plist, 2);
SListPushFront(&plist, 2);
SListPushFront(&plist, 4);
SListPrint(plist);
SLTNode* pos = SListFind(plist, 3);
if (pos)
{
SListInsert(&plist, pos, 30);//在3的前面插入30
}
SListPrint(plist);
}
int main()
{
//TestSList1();
//TestSList2();
//TestSList3();
//TestSList4();
TestSList5();
return 0;
}
2.移除链表中某些元素,并返回新的头节点
#include <stdio.h>
#include <stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
struct ListNode* removeElements(struct ListNode* head,int val)
{
struct ListNode* prev=NULL;
struct ListNode* cur=head;
while(cur)
{
if(cur->data==val)
{
if(cur==head)
{
head=cur->next;
free(cur);
cur=head;
}
else
{
//删除
prev->next=cur->next;
free(cur);
cur=prev->next;
}
}
else
{
//往后走
prev=cur;
cur=cur->next;
}
}
return head;
}
int main()
{
//快速创建链表,方便调试
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=7;
n2->data=7;
n3->data=7;
n4->data=7;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
struct ListNode* head=removeElements(n1,7);
printf("%p",head);
return 0;
}
3.翻转链表
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
//方法一
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)
{
return NULL;
}
else
{
struct ListNode* n1=NULL;
struct ListNode* n2=head;
struct ListNode* n3=head->next;
while(n2!=NULL)
{
//翻转
n2->next=n1;
//迭代往后走
n1=n2;
n2=n3;
if(n3!=NULL)
{
n3=n3->next;
}
}
return n1;
}
}
//
////方法二
//struct ListNode* reverseList(struct ListNode* head)
//{
// struct ListNode* cur=head;
// struct ListNode* newHead=NULL;
//
// while(cur)
// {
// struct ListNode* next=cur->next;
// //头插
// cur->next=newHead;
// newHead=cur;
//
// //迭代
// cur=next;
// }
// return newHead;
//}
int main()
{
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=1;
n2->data=2;
n3->data=3;
n4->data=4;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
//翻转前
struct ListNode* cur=n1;
while(cur)
{
printf("%d->",cur->data);
cur=cur->next;
}
printf("NULL\n");
//翻转后
cur=reverseList(n1);
while(cur)
{
printf("%d->",cur->data);
cur=cur->next;
}
printf("NULL\n");
return 0;
}
4.找到非空链表的中间节点,若有两个返回第二个
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* slow=head; //fast速度是slow的两倍,fast走到末尾,slow刚好走到中间
struct ListNode* fast=head;
while(fast&&fast->next) // 1 2 3 4 5(fast)
// 1(fast1) 2 3(fast2) 4 5(fast3) 6 NULL(fast4)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
int main()
{
//快速创建链表,方便调试
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=1;
n2->data=2;
n3->data=3;
n4->data=4;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
printf("%d\n",middleNode(n1)->data);
return 0;
}
5.找到链表中倒数第K个节点
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
//1.fast先走K步
//2.slow和fast再一起走,fast==NULL时,slow就是倒数第K个
struct ListNode* FindKthToTail(struct ListNode* head,int k)
{
struct ListNode* fast=head;
struct ListNode* slow=head;
int i=k;
while(i--) //k步
{
//K大于链表长度
if(fast==NULL)
{
return NULL;
}
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
int main()
{
//快速创建链表,方便调试
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=1;
n2->data=2;
n3->data=3;
n4->data=4;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
printf("%d\n",FindKthToTail(n1,3)->data);
return 0;
}
6.合并两个升序链表得到新的升序链表
#include <stdio.h>
#include <stdlib.h>
//思路:依此比较链表中的节点,每次取小的节点,尾插到新的链表
struct ListNode
{
int data;
struct ListNode* next;
};
struct ListNode* mergeTwoLists(struct ListNode* l1,struct ListNode* l2)
{
//如果其中一个为空,返回另一个
if(l1==NULL)
{
return l2;
}
if(l2==NULL)
{
return l1;
}
struct ListNode* head=NULL;
struct ListNode* tail=NULL;
//设置哨兵位
//多一个哨兵位的头节点,这个节点不存储有效数据
head=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
while(l1&&l2)
{
if(l1->data<l2->data)
{
// if(head==NULL)
// {
// head=tail=l1;
// }
//else
{
tail->next=l1;
tail=tail->next;
}
l1=l1->next;
}
else
{
// if(head==NULL)
// {
// head=tail=l2;
// }
//else
{
tail->next=l2;
tail=tail->next;
}
l2=l2->next;
}
}
if(l1)//若L1没结束
{
tail->next=l1;
}
if(l2)
{
tail->next=l2;
}
//return head;
struct ListNode* list=head->next;
free(head);
return list;
}
int main()
{
//快速创建链表,方便调试
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=1;
n2->data=2;
n4->data=4;
n1->next=n2;
n2->next=n4;
n4->next=NULL;
struct ListNode* m1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* m2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* m3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* m4=(struct ListNode*)malloc(sizeof(struct ListNode));
m1->data=1;
m2->data=3;
m4->data=4;
m1->next=m2;
m2->next=m4;
m4->next=NULL;
struct ListNode* cur=NULL;
cur=mergeTwoLists(n1,m1);
while(cur)
{
printf("%d->",cur->data);
cur=cur->next;
}
printf("NULL\n");
return 0;
}
7.分割链表
#include<stdio.h>
#include<stdlib.h>
//给定数值X,将小于X的节点排在其他节点之前,且不能改变原来的数据顺序
struct ListNode
{
int data;
struct ListNode* next;
};
ListNode* Partition(ListNode* pHead,int x)
{
struct ListNode* lessHead=NULL;
struct ListNode* lessTail=NULL;
//开一个哨兵位头节点,方便尾插
lessHead=lessTail=(struct ListNode*)malloc(sizeof(struct ListNode));
lessTail->next=NULL;
struct ListNode* greaterHead=NULL;
struct ListNode* greaterTail=NULL;
//开一个哨兵位头节点,方便尾插
greaterHead=greaterTail=(struct ListNode*)malloc(sizeof(struct ListNode));
greaterTail->next=NULL;
struct ListNode* cur=pHead;
while(cur)
{
if(cur->data<x)
{
lessTail->next=cur;
lessTail=cur;
}
else
{
greaterTail->next=cur;
greaterTail=cur;
}
cur=cur->next;
}
lessTail->next=greaterHead->next;
greaterTail->next=NULL;//否则可能是环形
struct ListNode* list=lessHead->next;
//free(lessHead);
//free(greaterHead);
return list;
}
int main()
{
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=2;
n2->data=3;
n3->data=1;
n4->data=4;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
struct ListNode* cur=NULL;
cur=Partition(n1,3);
while(cur)
{
printf("%d->",cur->data);
cur=cur->next;
}
printf("NULL\n");
return 0;
}
8.判断链表是否是回文结构
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
//翻转
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* cur=head;
struct ListNode* newHead=NULL;
while(cur)
{
struct ListNode* next=cur->next;
//头插
cur->next=newHead;
newHead=cur;
//迭代
cur=next;
}
return newHead;
}
//找到中间节点
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* slow=head; //fast速度是slow的两倍,fast走到末尾,slow刚好走到中间
struct ListNode* fast=head;
while(fast&&fast->next) // 1 2 3 4 5(fast)
// 1(fast1) 2 3(fast2) 4 5(fast3) 6 NULL(fast4)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
bool PalindromeList(ListNode* A)
{
struct ListNode* mid=middleNode(A);
struct ListNode* rHead=reverseList(mid);
struct ListNode* curA=A;
struct ListNode* curR=rHead;
while(curA&&curR)
{
if(curA->data!=curR->data)
{
return false;
}
else
{
curA=curA->next;
curR=curR->next;
}
}
return true;
}
int main()
{
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=2;
n2->data=3;
n3->data=3;
n4->data=2;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
bool PalindromeList(n1);
return 0;
}
9.找到两个链表相交节点
#include <stdio.h>
#include <stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
//尾节点相同就是相交,否则就是不相交
//求交点: 长的链表先走(长度差)步,再同时走,第一个相同就是交点
// 1 2 3
// 7 8
// 4 5
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;
}
//求差
//abs求绝对值
int gap=abs(lenA-lenB);
//长的链表先走(长度差)步,再同时走找交点
struct ListNode* longList=headA;
struct ListNode* shortList=headB;
if(lenA<lenB)
{
shortList=headA;
longList=headB;
}
while(gap--)
{
longList=longList->next;
}
while(longList!=shortList)
{
longList=longList->next;
shortList=shortList->next;
}
return longList;
}
int main()
{
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=1;
n2->data=2;
n3->data=3;
n4->data=4;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
struct ListNode* m1=(struct ListNode*)malloc(sizeof(struct ListNode));
m1->data=5;
m1->next=n3;
printf("%d\n",getIntersectionNode(n1,m1)->data);
return 0;
}
10.判断链表中是否有环
#include <stdio.h>
#include <stdlib.h>
struct ListNode
{
int data;
struct ListNode* next;
};
//快慢指针
//slow和fast指向链表的开始
//slow一次走一步
//fast一次走两步
//不带环,fast会指向空
//带环,fast会追上slow,再次相遇
bool hasCycle(struct ListNode* head)
{
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
int main()
{
struct ListNode* n1=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4=(struct ListNode*)malloc(sizeof(struct ListNode));
n1->data=1;
n2->data=2;
n3->data=3;
n4->data=4;
n1->next=n2;
n2->next=n3;
n3->next=n4;
n4->next=NULL;
hasCycle(n1);
return 0;
}
11.单链表带环问题,延申问题
单链表带环问题
解法思路:快慢指针,slow和fast同时指向链表头部
Slow每次走一步
Fast每次走两步
有环,Slow和fast会在环中相遇
无环,则fast或fast->next指向空
延伸问题:
- 为什么Slow每次走一步,Fast每次走两步的情况,它们一定会在环中相遇?会不会在环中错过,永远遇不上?请证明。
结论:带环一定会相遇
分析证明:
第一步:slow和fast,fast一定先进环,此时slow走了入环前距离的一半。
第二步:随着slow进环,fast已经在环中走了一段路程,路程大小与环的大小有关
假设slow进环的时候,slow与fast的距离是N
(fast追slow的距离)
Slow每次向前走一步,fast向前走两步,每追一次,fast和slow的距离变化为N-1,N-2,……,1,0
每追一次,fast和slow的距离-1,它们之间的距离减到0的时候就是相遇的点。
- 为什么slow走一步,fast一定要走两步,可不可以走n步(n>=2)?请证明。
结论:fast一次走n步(n>=2),不一定相遇
分析证明:
假设:slow走一步,fast走三步,slow进环的时候,slow与fast的距离是N(fast追slow的距离)
它们之间的距离变化:N,N-2,N-4,N-6,……
如果N是偶数就能追上
如果N是奇数,这次追不上,此时它们之间的距离变为C-1(C为环长,fast追slow的距离)
如果C-1是奇数,就永远追不上,追到后面还是相距C-1
如果C-1是偶数,就能追上
slow走一步,fast走N步,证明类似。