链表及其实现–Mooc浙大数据结构听课笔记
1.顺序存储
一般来说顺序存储使用并不多,主要是链表存储。
//2.1 第一节
//顺序存储的链表结构
typedef struct PolyNode* Polynominal;
struct PolyNode{
int coef;
int expon;
Polynominal link;
};
//2.1 第二节
//操作集
List MakeEmpty();//初始化一个空线性表
ElementType FindKth(int K,List L);//根据位序K,返回相应元素
int Find(ElementType X,List L);//在线性表中查找X的 第一次出现位置
void Insert(ElementType X,int i,List L);//在位序i前插入一个新元素X
void Delete(int i,List L);//删除指定位序i的元素
int Length(List L);//返回线性表L的长度n
//线性表的顺序存储实现
typedef struct LNode* List;
struct LNode{
ElementType Data[MAXSIZE];
int Last;
};
struct LNode L;
List PtrL;
//访问下标为i的元素:L.Data[i] PtrL->Data[i]
//线性表的长度: L.Last+1 Ptr->Last+1
//主要操作的实现
List MakeEmpty()
{
List PtrL;
PtrL=(List)malloc(sizeof(struct LNode));
PtrL->Last=-1;
return PtrL;
}
int Find(ElementType X,List PtrL)
{
int i=0;
while(i<=PtrL->Last&&PtrL->Data[i]!=X)
i++;
if(i>PtrL->Last) return -1;
else return i;
}
//2.1 第三节 链式存储的插入和删除
//比较简单,不再做笔记
2.链式存储
1.链式存储及查找
链式存储的链表结构定义
typedef struct LNode *List;
struct LNode{
ElementType Data;
List Next;
};
struct Lnode L;
List PtrL;
此处只是定义了链表的结构,并没有实际应用链表,因此使用时还需要使用malloc()函数分配内存空间。另外需要注意第一个指针以及后边涉及插入删除等操作时对最后一个指针指向NULL的考虑。可以对刚声明的指针直接赋值NULL然后再赋予其他值。
1.求链表长度
int Length(List PtrL)
{
List p=PtrL;//定义一个新的指针,尽量不要对原指针进行操作
int j=0;
while(p!=NULL){
p=p->Next;
j++;
}
return j;
}
2.按序号查找
这个是我自己一开始没有看视频写的
List FindKth(int K,List PtrL)
{
List p=PtrL;
for(;k>1;k--)
p=p->Next;
return p;
}
下面这个是正确答案
List FindKth(int K,List PtrL)
{
List p=PtrL;
int i=1;
while(p!=NULL&&i<K){
p=p->Next;
i++;
}
if(i==K)
return p;
else
return NULL;
}
可以看到缺少了对于查找是否到达结尾的讨论,只注重了函数的功能却忽略了链表本身的越界问题。另外这种讨论方式要用&&而不是||,很容易分不清。
3.按值查找
List Find(ElementType X,List PtrL)
{
List p=PtrL;
while(p!=NULL&&p->Data!=X){
p=p->Next;
}
if(p==NULL)
return NULL;
else
return p;
}
显然在这里,代码可以做进一步简化,最后的选择语句可以直接变为return p;原因是我们在这里的while的两个条件其实都和p有关,因此无论何时终止while都只与p有关。
2.链式存储的插入和删除
1.插入
List Insert(ElementType X,int i,List PtrL)
{
List p,s;
s=(List)malloc(sizeof(struct LNode));
//插入时要考虑插入的特殊位置,此处特殊位置为头部位置
if(i==1){//如果是头部位置
s->Data=X;
s->Next=PtrL;
return s;//返回新的头指针
}
else{
p=FindKth(i-1,PtrL);//其实这里只用while循环找到也可以,省去了调用函数的混淆问题。但在较大程序中推荐这种函数调用方式。
if(p==NULL){
printf("error");
return NULL;
}
else{
s->Data=X;
s->Next=p->Next;
p->Next=s;
return PtrL;
}
}
}
在这里也有一种化简方法,由于头指针的特殊性在于前面没有指针,因此放置一个空指针即可,这一步在初始化链表的函数中即可完成。
List Start()
{
List p,q;
p=(List)malloc(sizeof(LNode));
p->Data=NULL;
p->Next=q;//此处p为空指针
q=(List)malloc(sizeof(LNode));
q->Next=NULL;//注意在初始化每一个链表结构时一定要将其中元素尽可能也初始化
return p;//返回头指针
//此后再使用时就可以不用讨论头指针的问题了,但是计数的数值一定要把握准确
}
2.删除
List Delete(int i,List PtrL)
{
List p,s;
if(i==1){
s=PtrL;
if(PtrL!=NULL)
PtrL=PtrL->Next;
else
return NULL;
free(s);
return PtrL;
}
else{
p=FindKth(i-1,PtrL);
if(p==NULL||p->Next==NULL){
printf("error")
return NULL;
}
else{
s=p->Next;
p->Next=s->Next;
free(s);
return PtrL;
}
}
}
同理,在这里如果头指针前再放置一个空指针就不用讨论了,但是完成其他函数的功能时一定要注意是否需要跳过空指针。