#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<Windows.h>
#include<math.h>
typedef int DataType;
typedef struct SListNode {
DataType data; // 值
struct SListNode *pNext; // 指向下一个结点
} SListNode;
// 初始化
void SListInit(SListNode **ppFirst)
{
assert(ppFirst != NULL);
*ppFirst = NULL;
}
SListNode *SListFind(SListNode *pFirst, DataType data)
{
SListNode *pNode;
for (pNode = pFirst; pNode; pNode = pNode->pNext){
if (pNode->data = data){
return pNode;
}
}
return NULL;
}
// 尾部插入
//尾插:申请空间(data=data,pNext=NULL),找到倒数第一个,倒数第一个的pNEXT=NULL
void SListPushBack(SListNode** ppFirst, DataType data)
{
assert(ppFirst != NULL);
SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));
assert(pNewNode != NULL);
pNewNode->data = data;
pNewNode->pNext = NULL;
if (*ppFirst == NULL)
{
*ppFirst = pNewNode;
}
SListNode *pNode;
pNode = *ppFirst;
while(pNode->pNext!= NULL)
{
pNode = pNode->pNext;
}
//倒数第一个
pNode->pNext = pNewNode;
pNewNode->pNext = NULL;
}
// 头部插入
void SListPushFront(SListNode **ppFirst, DataType data)
{
assert(ppFirst != NULL);
SListNode *pNewNode = (SListNode*)malloc(sizeof(SListNode));
assert(pNewNode != NULL);
pNewNode->data = data;
pNewNode->pNext = NULL;
if (*ppFirst == NULL){
SListPushBack(ppFirst, data);
}
pNewNode->pNext = *ppFirst;
*ppFirst = pNewNode;
}
//打印
void print(SListNode *pFirst)
{
SListNode *pNode;
for (pNode = pFirst; pNode; pNode = pNode->pNext)
{
printf("%d->", pNode->data);
}
printf("NULL");
printf("\n");
}
//单链表面试题
//1.打印单链表
//(1)逆置打印:先找到链表尾部,
//思路:先找到最后一个,打印最后一个,然后指向链表最后一个指针前移,知道遇到链表第一个节点。时间复杂度O(n^2),空间复杂度O(1)
void PrintTail(SListNode *pFirst)
{
SListNode *end = NULL;
while (end != pFirst){
SListNode *pNode = pFirst;
while (pNode->pNext != end){
pNode = pNode->pNext;
}
printf("%d ", pNode->data);
end = pNode;
}
printf("\n");
}
//(2)递归实现 时间复杂度:O(n),空间复杂度:O(n)
void PrintTailR(SListNode *pFirst)
{
if (pFirst == NULL)
return;
PrintTailR(pFirst->pNext);
printf("%d ", pFirst->data);
}
//2.删除一个无头单链表的非尾节点(不能遍历链表)
//思路:有头但是不告诉你,给一个节点位置,不一定要删除这个节点,但可以删除给定节点后面的那个节点
void DelNode(SListNode *pPos)
{
assert(pPos && pPos->pNext );
//替换法进行删除
SListNode *next = pPos->pNext;
pPos->data = next->data;
pPos->pNext = next->pNext;
free(next);
}
//3.在无头单链表的一个节点前插入一个节点(不能遍历链表)
//思路:将插入的数查到指定位置后面,因为我们不知道给定位置前面的值,所以只能插入节点的值和指定位置的值相等,然后将要要插入的值放到给定位置的值
void InsertNode(SListNode *pos,DataType data)
{
assert(pos);
SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));
pNewNode->data = pos->data;
pNewNode->pNext = pos->pNext;
pos->pNext = pNewNode;
pos->data=data;
}
//4.单链表实现约瑟夫环
/*约瑟夫环:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。
从编号为k的人开始报数,数到m的那个人出列;
他的下一个人又从1开始报数,数到m的那个人又出列;
依此规律重复下去,直到圆桌周围的人全部出列
*/
//思路:
SListNode *JosephCycle(SListNode *pFirst,int k) //数到k的那个删掉
{
SListNode *pNode =pFirst;
SListNode *p = pFirst;
if (pFirst == NULL)
return NULL;
while (pNode->pNext)
{
pNode=pNode->pNext;
}//找到结尾
pNode->pNext = pFirst; //构成环
//约瑟夫环结束
while (p->pNext!=p)
{
int count = k;
while (--count){
p = p->pNext;
//替换法删除
SListNode *next = p->pNext;
p->data = next->data;
p->pNext = next->pNext;
free(next);
}
}
return p;
}
//5.单链表的逆置/反转
//思路:
//思路一:定义三个指针:n1,n2,n3分别指向第一,二,三个元素
//可用返回值,也可用二级指针
SListNode *reserveNode(SListNode *pFirst)
{
SListNode *n1, *n2, *n3;
if (pFirst== NULL || pFirst->pNext == NULL)
return pFirst;
n1 = pFirst;
n2 = n1->pNext;
n3 = n2->pNext;
while (n2)
{
n2->pNext = n1;
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->pNext;
}
pFirst->pNext=NULL;
pFirst = n1;
return pFirst;
}
//思路二:头插法:定义两个指针一个pNewNode指向空,pNode指向链表头,pNode遍历链表,
//在遍历的时候再定义一个指针来保存pNode遍历的值,然后头插到节点pNewNode上,而不是创一个新链表
SListNode *reserveNode1(SListNode *pFirst)
{
SListNode *pNewNode = NULL;
SListNode *pNode=pFirst;
while (pNode)
{
SListNode *next = pNode;
pNode = pNode->pNext;
//头插
next->pNext = pNewNode;
pNewNode = next;
}
return pNewNode;
}
//6.单链表的排序(冒泡排序&&选择排序)
//冒泡排序
//思路:定义三个指针:pNode指向链表头,next指向第二个元素,tail指向的每轮结束的位置,开始为NULL。然后比较pNode和next的值,大就交换,当next为空时第一轮结束,记录这个位置,下一趟排序只要next遇到这个位置就结束
void BubbleNode(SListNode *pFirst)
{
SListNode *pNode;
SListNode *next;
SListNode *tail=NULL; //标记终止位置
if (pFirst == NULL || pFirst->pNext == NULL)
{
return;
}
while (tail!= pFirst->pNext)
{
DataType flag = 0;
pNode = pFirst;
next = pNode->pNext;
while (next!= tail)
{
if (pNode->data > next->data)
{
flag = 1;
DataType tmp = pNode->data;
pNode->data = next->data;
next->data = tmp;
}
pNode = pNode->pNext;
next = next->pNext;
}
if (flag == 0)
break;
tail = pNode;
}
}
//7.合并两个有序链表,合并后链表依旧有序
//思路:定义两个指针分别指向两个链表,每次在小的后面尾插.定义一个新链表,
//将两个链表中第一个元素小的地址拿下来作为新链表的头。
//在定义一个指针,指向新链表的尾部
SListNode *MergeList(SListNode *list1,SListNode *list2)
{
SListNode *Newlist;
SListNode *tail;
if (list1 == NULL)
return list2;
if (list2 == NULL)
return list2;
//将list1和list2中小的一个用来作为新链表的头
if(list1->data <list2->data)
{
Newlist = list1;
list1 = list1->pNext;
}
else
{
Newlist = list2;
list2=list2->pNext;
}
//刚开始尾指向新链表头
tail = Newlist;
while (list1&&list2)
{
if (list1->data < list2->data) //list1的值小就将list1尾插到tail后面
{
tail->pNext = list1;
list1 = list1->pNext;
}
else
{//list2的值小就将list2尾插到tail后面
tail->pNext= list2;
list2 = list2->pNext;
}
tail = tail->pNext;
}
//走到这里说明list1或list2为空
if (list1) //如果list1不为空,说明list2为空,就将list1插入到新链表尾部后面
tail->pNext = list1;
if (list2) //如果list2不为空,说明list1为空,就将list2插入到新链表尾部后面
tail->pNext = list2;
return Newlist;
}
//8.查找链表的中间节点,只能遍历一次链表
//思路:定义两个指针来遍历链表:指针pFast一次遍历两个元素,指针pSlow一次遍历一个元素
SListNode * FindMidNode(SListNode *pFirst)
{
SListNode *pFast=pFirst;
SListNode *pSlow = pFirst;
if (pFirst == NULL)
return NULL;
//快指针一次走两步,慢指针一次走一步
while (pFast&&pFast->pNext)//pFast第一步不为空,第二步也不为空。两个条件次序不能换
{
pFast = pFast->pNext->pNext;
pSlow = pSlow->pNext;
}
return pSlow;
}
//9.查找倒数第k个节点,只能遍历一次
//思路:给定两个指针:指针pFast先朝前走k步,
SListNode *FindKNode(SListNode *pFirst,DataType k)
{
SListNode *pFast = pFirst;
SListNode *pSlow = pFirst;
if (pFirst == NULL || k <= 0)
return NULL;
//(1)当pFast走k步时,即k--
#if 1
while (k--) //走到k的位置
{
if (pFast == NULL)
return NULL;
pFast = pFast->pNext;
}
while (pFast)
{
pFast = pFast->pNext;
pSlow = pSlow->pNext;
}
#endif
//当pFast走k-1步时,即--k
#if 0
while (--k) //走到k-1的位置
{
if (pFast == NULL)
return NULL;
pFast = pFast->pNext;
}
while (pFast->pNext )
{
pFast = pFast->pNext;
pSlow = pSlow->pNext;
}
#endif
return pSlow;
}
//10.判断链表是否带环,若带环求长度,求入口,并计算时间和空间复杂度
//思路:
//怎么判断带环:定义两个指针:指针pFast一次走两步,pSlow一次走一步,如果pFast不为空,则为环
//求环的长度:找到在环中两指针相遇点,然后定义两个指针,一个从头开始走,一个从相遇点开始走,两个指针相遇,则是链表尾节点,可求出长度
SListNode *IsCycle(SListNode *pFirst)
{
SListNode *pFast=pFirst;
SListNode *pSlow=pFirst;
//判断是否为环,并且返回入口点
while (pFast &&pFast->pNext)
{
pSlow = pSlow->pNext;
pFast = pFast->pNext->pNext;
if (pFast == pSlow)
return pSlow;
}
return NULL;
}
////入口点:
/*
假设环的长度为:C
入环前的长度为:L
入口点到相遇点长度:X
慢指针和快指针的关系:L+X=L+X+NC(N为pFast在和pSlow相遇前在环中走的环数,N大环小,N小环大)
即:L=NC-X
*/
SListNode *GetEntry(SListNode *pFirst,SListNode *meet)
{
while (pFirst != meet)
{
pFirst = pFirst->pNext;
meet = meet->pNext;
}
return meet;
}
//11.判断两个链表是否相交,若相交,求交点(假设链表不带环)
//思路:找尾,尾相同就相交,尾不相同就不想交
//求交点:相交前的长度不一样,让长的链表减去短的链表长度,得到差值,
//然后让长的链表先走差值的长度,最后在同时走,直到相遇
SListNode *GetNode(SListNode *list1,SListNode *list2)
{
SListNode *pList1 = list1;
SListNode *pList2 = list2;
SListNode *shortlist, *longlist;
DataType L1=0, L2=0,L;
if (pList1 == NULL || pList2 == NULL)
return NULL;
while (pList1){
pList1 = pList1->pNext;
++L1;
}
while (pList2){
pList2 = pList2->pNext;
++L2;
}
longlist = list1;
shortlist = list2;
if (L1 < L2){
longlist = list2;
shortlist = list1;
}
L = abs(L1 - L2); //取绝对值
while (L--){
longlist = longlist->pNext;
}
while (longlist != shortlist){
longlist = longlist->pNext;
shortlist = shortlist->pNext;
}
return longlist;
}
//12.判断两个链表是否相交,若相交,求交点(假设链表不带环)
//思路:
//先分类:
/*
(1)都不带环
(2)如果一个带环,一个不带换,肯定不相交
(3)都带环
a.判断相遇点是否在同一环
*/
void test()
{
SListNode *ppFirst;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
print(ppFirst);
//面试题函数
//1.逆置打印链表
PrintTail(ppFirst);
PrintTailR(ppFirst);
printf("\n");
}
void test1(){
SListNode *ppFirst;
SListNode *pPos;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
// 头部插入
SListPushFront(&ppFirst, 2);
print(ppFirst);
//2.删除一个无头单链表的非尾节点
pPos = SListFind(ppFirst,3);
DelNode(pPos);
print(ppFirst);
}void test2(){
SListNode *ppFirst;
SListNode *pPos;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
// 头部插入
SListPushFront(&ppFirst, 2);
print(ppFirst);
//3.在无头单链表的一个节点前插入一个节点(不能遍历链表)
pPos = SListFind(ppFirst, 3);
InsertNode(pPos, 1);
InsertNode(pPos, 4);
print(ppFirst);
}
void test3(){
SListNode *ppFirst;
SListNode *pPos;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 1);
SListPushBack(&ppFirst, 2);
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
print(ppFirst);
//4.约瑟夫环问题
pPos = JosephCycle(ppFirst, 2);
printf("%d\n", pPos->data );
}
void test4()
{
SListNode *ppFirst;
SListNode *pPos;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 1);
SListPushBack(&ppFirst, 2);
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
print(ppFirst);
//5单链表逆置
pPos= reserveNode1(ppFirst);
print(pPos);
}
void test5()
{
SListNode *ppFirst;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 8);
SListPushBack(&ppFirst,3);
SListPushBack(&ppFirst, 7);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 2);
SListPushBack(&ppFirst, 6);
print(ppFirst);
//6.冒泡排序
BubbleNode(ppFirst);
print(ppFirst);
}
void test6()
{
SListNode *p;
SListNode *list1 = NULL;
SListNode *list2 = NULL;
// 尾部插入
SListPushBack(&list1, 1);
SListPushBack(&list1, 3);
SListPushBack(&list1, 5);
SListPushBack(&list1, 7);
print(list1);
SListPushBack(&list2, 2);
SListPushBack(&list2, 4);
SListPushBack(&list2, 6);
SListPushBack(&list2, 8);
print(list2);
//7.合并两个有序链表,合并后依然有序
p = MergeList(list1, list2);
print(p);
}
void test7()
{
SListNode *ppFirst;
SListNode *p;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
SListPushBack(&ppFirst, 7);
print(ppFirst);
//查找链表的中间节点,只能遍历一次
p = FindMidNode(ppFirst);
printf("%d\n", p->data);
}
void test8()
{
SListNode *ppFirst;
SListNode *p;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
SListPushBack(&ppFirst, 7);
SListPushBack(&ppFirst, 8);
print(ppFirst);
//查找链表的倒数第k个节点,只能遍历一次
p = FindKNode(ppFirst,2);
if (p)
{
printf("倒数第k个节点是:%d\n", p->data);
}
else
printf("倒数第k个节点不存在\n");
}
void test9()
{
SListNode *ppFirst;
SListNode *p,*tail,*meet;
// 初始化
SListInit(&ppFirst);
// 尾部插入
SListPushBack(&ppFirst, 3);
SListPushBack(&ppFirst, 4);
SListPushBack(&ppFirst, 5);
SListPushBack(&ppFirst, 6);
SListPushBack(&ppFirst, 7);
//构成环
p = SListFind(ppFirst, 5);
tail = SListFind(ppFirst, 7);
tail->pNext = p;
//10.找环入口节点
meet = IsCycle(ppFirst);
printf("入口点为:%d\n", GetEntry(ppFirst, meet)->data);
}
void test10()
{
SListNode *p,*p1;
SListNode *list1 = NULL;
SListNode *list2 = NULL;
// 尾部插入
SListPushBack(&list1, 1);
SListPushBack(&list1, 3);
SListPushBack(&list1, 7);
SListPushBack(&list1,9);
print(list1);
SListPushBack(&list2, 2);
SListPushBack(&list2, 4);
SListPushBack(&list2, 6);
SListPushBack(&list2, 7);
SListPushBack(&list2, 8);
SListPushBack(&list2, 10);
print(list2);
p = SListFind(list1, 9);
p1 = SListFind(list1,8);
p->pNext = p1;
//11.判断是否相交,不带环
printf("%d\n", GetNode(list1, list2)->data);
}