序:
调用函数时,其实就是把变量的地址传给函数,但是函数只能修改指针指向变量的值,不能修改指针的指向
如果需要修改指针的指向就必须在传参时传指针的指针!
比如:单链表在遍历,查找时就不需要改变指针指向,插入删除就必须改变指针指向
为了保证代码质量,我将所有的函数都使用指针的指针形式
先附上运行结果:
ListNode.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
typedef struct ListNode
{
struct ListNode* _pnext;
DataType _pdata;
}Node,*pNode;
void SListInit(pNode* pHead);
void PrintList(pNode* pHead);
pNode BuySListNode(DataType data);
void SListPushBack(pNode* pHead,DataType data);
void SListPopBack(pNode* pHead);
void SListPushFront(pNode* pHeaf,DataType data);
void SListPopFront(pNode* pHead);
void SListInsert(pNode* pHead,pNode pos,DataType data);
int SListEmpty(pNode* pHead);
int SListSize(pNode* pHead,DataType data);
void SListErase(pNode* pHead,pNode pos);
pNode SListFind(pNode* pHead,DataType data);
void SListDestroy(pNode* pHead);
ListNode.c
#include"ListNode.h"
void SListInit(pNode* pHead)
{
assert(pHead);
*pHead = NULL;
}
void PrintList(pNode* pHead)
{
pNode pCur = *pHead;
if (pCur == NULL)
{
printf("链表为空\n");
return;
}
while (pCur)
{
printf("%d->",pCur->_pdata);
pCur = pCur->_pnext;
}
printf("NULL\n");
}
pNode BuySListNode(DataType data)
{
pNode pNewNode = (pNode)malloc(sizeof(Node));
if (NULL == pNewNode)
{
perror("pNewNode");//perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 错误 (stderr)
//在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了error的值。perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出。
exit(EXIT_FAILURE);//退出,stdlib.h中定义了#define EXIT_FAILURE 1
//可以用做exit()的参数来使用,表示没有成功的执行一个程序
//assert(0); 0为假,打印一条错误信息,然后停止程序运行
//return NULL; 返回为空。
}
pNewNode->_pdata = data;
pNewNode->_pnext = NULL;
return pNewNode;
}
void SListPushBack(pNode* pHead, DataType data)
{
pNode pCur = *pHead;
assert(pHead);
if (NULL == pCur)
{
*pHead = BuySListNode(data); //如果链表为空,直接插
}
else //链表不为空时,找到最后一个再插入
{
while (pCur->_pnext)
{
pCur = pCur->_pnext; //循环直到pCur->_pnext为空,找到最后一个
}
pCur->_pnext = BuySListNode(data);
}
}
void SListPopBack(pNode* pHead)
{
pNode pCur = *pHead;
pNode pTail = NULL;
assert(pHead);
if (NULL == *pHead)//头结点不存在
{
return;
}
else if (NULL == (*pHead)->_pnext)//只有一个节点
{
free(*pHead);
*pHead = NULL;
}
else//节点超过一个
{
while (pCur->_pnext)//pCur->_pnext循环直到指向NULL,pCur同时找到最后一个节点,pTail找到倒数第二个接点
{
pTail = pCur;
pCur = pCur->_pnext;
}
pTail->_pnext = NULL;
free(pCur);
pCur = NULL;
}
}
void SListPushFront(pNode* pHead, DataType data)
{
pNode pNewNode = BuySListNode(data);
assert(pHead);
pNewNode->_pnext = *pHead;
*pHead = pNewNode;
}
void SListPopFront(pNode* pHead)
{
pNode pDelNode = *pHead;
if (NULL == *pHead)
{
assert(pHead);
return;
}
else
{
*pHead = pDelNode->_pnext;
free(pDelNode);
}
}
void SListInsert(pNode* pHead, pNode pos, DataType data)//将值为data的节点插入pos的后面
{
pNode pNewNode = NULL;
if (NULL == pHead || NULL == pos)
{
printf("链表不存在\n");
return;
}
else
{
pNewNode = BuySListNode(data);
pNewNode->_pnext = pos->_pnext;
pos->_pnext = pNewNode;
}
}
int SListEmpty(pNode* pHead)
{
if (NULL == pHead)
{
return 1;
}
else
{
return 0;
}
}
int SListSize(pNode* pHead, DataType data)//找到值为data的节点个数
{
int count = 0;
pNode pCur = *pHead;
if (NULL == pHead)
{
printf("链表为空\n");
return 0;
}
while (pCur)
{
if (pCur->_pdata == data)
{
count++;
}
pCur = pCur->_pnext;
}
return count;
}
void SListErase(pNode* pHead, pNode pos)
{
pNode pDel = NULL;
assert(pHead);
if (pHead == NULL || NULL == pos)
{
printf("输入错误\n");
return;
}
else
{
pDel = pos->_pnext;
pos->_pdata = pDel->_pdata;//交换pos和pDel的data
pos->_pnext = pDel->_pnext;//删除pDel
free(pDel);
pDel = NULL;
}
}
pNode SListFind(pNode* pHead, DataType data)
{
pNode pCur = *pHead;
if (NULL == pHead)
{
printf("链表为空\n");
return NULL;
}
while (pCur)
{
pCur = pCur->_pnext;
if (pCur->_pdata == data)
{
return pCur;
}
}
return NULL;
}
void SListDestroy(pNode* pHead)
{
pNode pCur = *pHead;
pNode pnext = NULL;
assert(pHead);
if (NULL == pHead)
{
return;
}
else
{
*pHead = NULL;
while (pCur)
{
pnext = pCur->_pnext;
free(pCur);
pCur = pnext;
}
}
}
test.c
#include"ListNode.h"
int main()
{
pNode Slist,pos;
SListInit(&Slist);
printf("初始化之后的链表为: ");
PrintList(&Slist);
SListPushBack(&Slist, 1);
SListPushBack(&Slist, 2);
SListPushBack(&Slist, 3);
SListPushBack(&Slist, 4);
SListPushBack(&Slist, 5);
SListPushBack(&Slist, 6);
printf("尾插之后的链表为:");
PrintList(&Slist);
SListPopBack(&Slist);
printf("尾删后的链表为: ");
PrintList(&Slist);
SListPushFront(&Slist, 0);
printf("头插后的链表为: ");
PrintList(&Slist);
SListPopFront(&Slist);
printf("头删后的链表为: ");
PrintList(&Slist);
printf("寻找节点为3的个数:%d\n", SListSize(&Slist, 3));
pos = SListFind(&Slist,3);//pos为节点,通过Find的=函数返回一个节点进行传参
SListInsert(&Slist,pos,9);
printf("在节点为3的位置后插入9:\n");
PrintList(&Slist);
SListErase(&Slist,pos);
printf("删除值为3的节点:\n");
PrintList(&Slist);
system("pause");
return 0;
}
单链表面试题思路
- 0、删除单链表p指向的那个元素,(时间和空间复杂度尽量小)
为了时间和空间复杂度尽量小,尽可能不改变链表本身结构。
将q的值赋给节点p在将p节点删除 - 1、找出单链表的倒数第K个元素,(仅允许遍历一遍链表)
快慢指针,倒数第K个,所以快指针比慢指针先走K步,当快指针到尾部是,慢指针距尾部K个元素 - 2、找出单链表的中间元素,(仅允许遍历一遍链表)
快慢指针,fast走两步,slow走一步,fast走到尾部是slow正好的中间 - 3、判断单链表是否有环(6形状)?
快慢指针,快指针走两步,慢指针走一步,若有环,快指针总会追上慢指针,如果不会环,快指针遇见NULL接受 - 4、如何找到环的入口?
设起始位置到入口位置长度为L,相遇点距入口点为S,
相遇时,慢指针:L+S 快指针领先n圈:L+S+nR,快指针是慢指针两倍速度:2(L+S)=L+S+nR
化简:L=nR-S
L仍然表示从链表头到达环入口的距离,而nR−S可以看成从起始点出发移动nR步后再倒退S步
即两个指针一个从起始位置开始,一个从相遇点开始,每次走一步,再次相遇时为如入口点! - 5、如何知道环的长度?
记录某一定点,相遇点或者入口点,一个指针不动,另一个一直走,再次相遇是为一圈,记录所走过的长度 - 6、带环链表的长度是多少?
带环链表的长度为L+R(起始位到入口点的长度和环长度) - 7、如何知道两个单链表(无环)是否相交
1:因为是单链表,所以如果有相交结果必定为Y型,使用两个指针向后走,如果尾节点相同则为相交
2:人为构环,将尾部连接到b首,指针在a中移动,如果出现在b环中则为相交 - 8、如果两个单链表(无环)相交,如何知道它们相交的第一个节点是什么
由于单链表相交肯定是Y型,所以如果有长度差别肯定在相交之前,遍历ab两链表,求出长度差
快指针先走长度差步,慢指针在走,两指针速度相同,相遇即为第一个节点 - 9、如何知道两个单链表(有环)是否相交
两个单链表有环相交时,环为共用环,求出a的入口点,判断入口点是否在b环上
或判断出a的快慢指针交点,看其是否在b上 - 10、单链表实现约瑟夫环
pNode JosephCycle(pNode* pHead, DataType m) { assert(pHead); pNode pcur = *pHead; if (pcur) return 0; while (1) { if (pcur->next == pcur) return pcur; int c = m; while (--c) { pcur = pcur->next; } pNode del = pcur->next; pcur->date = del->date; pcur->next = del->next; free(del); del = NULL; } }