基本操作
创建链表
typedef struct Node{
int val;
struct Node *next;
}*NodePtr;
构造链表并返回表头的指针
NodePtr createList(int a[],int n) //a[]是数组,n是数组的长度
{
NodePtr L=NULL,p=NULL,r=NULL; //p和r是临时的指针
for(int i=0;i<n;i++)
{
p=(NodePtr)malloc(sizeof(struct Node));
p->val=a[i];
p->next=NULL;
if(L==NULL)
L=p;
else
r->next=p;
r=p; //将新结点放在链表尾部
}
return L;
}
求长度
单向链表
int getLength(NodePtr list)
{
NodePtr p; //p为遍历指针
int n=0;
for(p=list;p!=NULL;p=p->next) //移动p的位置
{
n++;
}
return n;
}
循环链表
int getCircleLength(NodePtr list)
{
if(list==NULL)
return 0;
int n=0;
NodePtr p=list;
do {
p=p->next;
n++;
}while(p!=list);
return n;
}
插入
在非空线性链表的第一个结点前插入item
NodePtr insertFirst(NodePtr list,ElemType item)
{
NodePtr p=NULL;
p=(NodePtr)malloc(sizeof(struct Node));
p->val=item;
p->next=list;
return p;
}
调用时:list=insertFirst(list,item);
在p指向的链结点后插入item
void insertNode(NodePtr p,ElemType item)
{
NodePtr q=NULL;
q=(NodePtr)malloc(sizeof(struct Node));
q->val=item;
q->next=NULL;
q->next=p->next;
p->next=q;
}
线性链表中第n(n>0)个结点后面插入一个数据项为item的新结点
int insertNodeN(NodePtr list,int n,ElemType item)
{
//寻找第n个
int i;
NodePtr p=list,q;
for(i=1;i<n && p!=NULL;i++) //ppt上p->next!=NULL一样,因为下面有p==NULL的判断???怎么设计呢
{
p=p->next;
}
if(p==NULL||i!=n)
{
printf("Error.\n");
return -1;
}
//生成结点
q=(NodePtr)malloc(sizeof(struct Node));
q->val=item;
q->next=NULL;
//插入
q->next=p->next;
p->next=q;
return 1;
}
在有序(从小到大)线性链表中相应位置上插入一个数据项为item的新结点
NodePtr insertNodeSequently(NodePtr list,ElemType item)
{
NodePtr p=NULL,q=NULL,r=NULL;
p=(NodePtr)malloc(sizeof(struct Node));
p->val=item;
p->next=NULL;
//应该插在首位
if(list==NULL||item < list->val)
{
p->next=list;
list=p;
}
else
{
r=list; //保存直接前驱点位置
q=list->next;
while(q!=NULL && item > q->val)
{
r=r->next;
q=q->next;
}
p->next=q;
r->next=p;
}
return list;
}
删除
从非空线性链表中删除p指向的链结点
NodePtr deleteNode(NodePtr p,NodePtr list)
{
//删头结点
if(p==list)
{
list=p->next;
}
else
{
NodePtr q=list;
while(q->next!=p && q->next!=NULL)
{
q=q->next;
}
if(q->next!=NULL)
q->next=p->next; //容错机制,毕竟p可能不存在
}
free(p);
return list;
}
删除数据值为item的所有链结点
NodePtr deleteNodeAll(NodePtr list,ElemType item)
{
NodePtr q=list,p=list->next; //q是前驱结点,保证相差1,最后再处理头结点
while(p!=NULL)
{
if(p->val==item)
{
q->next=p->next;
free(p); //把p指的那个结点free
p=q->next;
}
else
{
q=q->next;
p=p->next;
}
}
if(list->val==item)
{
q=list;
list=list->next;
free(q);
}
return list;
}
销毁
void deleteWhole(NodePtr list)
{
NodePtr p=list;
while(p!=NULL)
{
list=p->next; //主要设计free
free(p);
p=list;
}
}
逆转
NodePtr invertList(NodePtr list)
{
NodePtr p,q,r;
p=list;
q=NULL;
while(p!=NULL)
{
r=q;
q=p;
p=p->next;
q->next=r;
}
list=q;
return list;
}
合并
将两个按值有序(从小到大)连接的非空链表合并为一个有序链表
NodePtr mergeList(NodePtr lista,NodePtr listb)
{
NodePtr listc,p=lista,q=listb,r; //p,q分别指向lista和listb当前待插入的链结点,r指向listc最后的结点
if(lista->val <= listb->val)
{
listc=lista;
p=p->next;
}
else
{
listc=listb;
q=q->next;
}
r=listc; //listc指向a,b中较小的
while(p!=NULL && q!=NULL)
{
if(p->val<=q->val)
{
r->next=p;
r=p;
p=p->next;
}
else
{
r->next=q;
r=q;
q=q->next;
}
}
r->next=p?p:q; //插入剩余链结点
return listc;
}
复制
NodePtr copy(NodePtr lista) //神奇!!!
{
NodePtr listb;
if(lista==NULL)
return NULL;
else
{
listb=(NodePtr)malloc(sizeof(struct Node));
listb->val=lista->val; //复制lista所指的链结点,并将该链结点指针赋予listb
listb->next=copy(lista->next); //再复制lista的直接后继点
}
return listb; //复制后的由listb指出
//因为返回是倒着返回,所以可以返回到首地址
}
双向链表
双向链表定义
typedef struct DNode{
ElemType data;
struct DNode *rlink,*llink;
}DNode,*DNodePtr;
构造
DNodePtr initDLink(int n)
{
int i;
DNodePtr list,p;
list=(DNodePtr)malloc(sizeof(struct DNode));
scanf("%d",&list->data);
list->rlink=list;
list->llink=list;
for(i=1;i<n;i++)
{
p=(DNodePtr)malloc(sizeof(struct DNode));
scanf("%d",&p->data);
list->llink->rlink=p; //新来的都插到list左边
p->llink=list->llink;
list->llink=p;
p->rlink=list;
}
return list;
}
插入
在非空双向循环链表中某个数据域的内容为x的链结点右边插入一个数据信息为item的新结点
void insertD(DNodePtr list,ElemType x,ElemType item)
{
DNodePtr p=list->rlink;
while(p!=list && p->data!=x)
{
p=p->rlink;
}
if(p==list)
{
printf("error\n");
return ;
}
else
{
DNodePtr new=(DNodePtr)malloc(sizeof(struct DNode));
new->data=item;
new->llink=p;
new->rlink=p->rlink;
p->rlink->llink=new;
p->rlink=new;
}
}
删除
删除非空双向循环链表中数据域的内容为x的链结点,带有头结点
void deleteD(DNodePtr list, ElemType x)
{
DNodePtr p=list->rlink;
while(p!=list && p->data!=x)
{
p=p->rlink;
}
if(p==list)
{
printf("error\n");
return ;
}
p->llink->rlink=p->rlink;
p->rlink->llink=p->llink;
free(p);
return ;
}
Tips
- 定义指针时一定
p=NULL
,这样我就可以检查后续是否乱用野指针 - 返回值可以是:
- 1和0表状态
- NodePtr list,返回起点
- 返回某个结点NodePtr
- 插入操作的特殊情况(要改头结点):
- 链表为空
- 在头结点前插入
- 参数一般包含
- list头
- 操作位置指针p
- 删除
- 一定释放
free(p)
,要事先保存结点指针 - 参数一般都有list,因为可能会把list给删除
- 看看链表是否非空
- 调用:
list=deleteNode(NodePtr list,...)
- 一定释放
- header(又称哑结点dummy node)
- 对链表结点的插入及删除操作统一了(不用考虑是否是头结点)
PS. 写的注释中,在表示第一个结点时使用了“头结点”这一称呼,希望大家悉心辨别