不要求逻辑上相邻的元素物理上也相邻
通过链来建立起数据元素之间的逻辑关系
**插入和删除不需要移动数据元素,**只需要修改链
但是访问序号为i的元素?
求线性表的长度?
//顺序表访问序号为i的元素和求线性表的长度
//访问下标为i的元素:L.Data[i]或PtrL->Data[i]
//线性表的长度:L.Last+1或者PtrL->Last+1//last是最后一个元素的下标
//如何存储
struct Node{
ElementType Data;
struct Node *Next;
}List;
List L,*PtrL;
//求表长
int Length(List *PtrL){
List *p=PtrL;//p指向表的第一个节点
int j=0;
while(p){
p=p->Next;//当前p指向第j个节点
j++;
}
return j;
}
//查找
//按序号查找--------第k个
List *FindKth(int K,List *PtrL){
List *p=PtrL;
int i=1;
while(p&&i<k){
p=p->Next;
i++;
}
if(i==k)
return p;
else
return NULL;
}
//按值查找Find
List *Find(ElementType X,List *PtrL){
List*p=PtrL;
while(p!=NULL&&p->Data!=x)
p=p->next;
return p;
}
//插入
//先构造一个新节点,用s指向
//再找到链表的第i-1个节点,用p指向
//然后修改指针 插入节点(p之后插入新节点是s)
List *Insert(ElementType X,int i,List *PtrL){
List *p,*s;
if(i==1){//新节点插入在表头
s=(List*)malloc(sizeof(List));
s->Data=X;
s->Next=PtrL;
return s;
}
p=FindKth(i-1,PtrL);
//按序号查找--------第k个
//List *FindKth(int K,List *PtrL){
if(p==NULL){//没这个序号
printf("参数i错误");
return NULL;//第i-1个元素不存在,不能插入
}else{
s=(List*)malloc(sizeof(List));
s->Data=X;
s->Next=p->next;//新节点插入在第i-1个节点的后面
p->Next=s;
return PtrL;
}
}
//malloc 向系统申请分配指定size个字节的内存空间。
//返回类型是 void* 类型。
//void* 表示未确定类型的指针。
//C,C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。--摘自百度百科 malloc函数
//删除(删除链表的第i(1<=i<=n)个位置上的结点)
List *Delete(int i,List *PtrL){
List *p,*s;
if(i==1){//删除的如果是第一个节点
s=PtrL;//s指向第一个节点
if(PtrL!=NULL)
PtrL=PtrL->next;//从链表中删除
else
return NULL;
free(s);
return PtrL;
}
p=FindKth(i-1,PtrL);//查找第i-1个节点???????????
if(p==NULL){
printf("第%d个节点不存在",i-1);
return NULL;
}else if(p->Next==NULL){/??????????
printf("第%d个节点不存在",i);
return NULL;
}else{
s=p->Next;
p->Next=s->Next;
free(s);
return PtrL;
}
}
数据结构中,在单链表的第一个结点之前附设一个结点,它没有直接前驱,称之为头结点。
头结点的数据域可以不存储任何信息,头结点的指针域存储指向第一个结点的指针(即第一个元素结点的存储位置)。头结点的作用是使所有链表(包括空表)的头指针非空,并使对单链表的插入、删除操作不需要区分是否为空表或是否在第一个位置进行,从而与其他位置的插入、删除操作一致。
(这个更好的利用了头节点的性质)
这道题我自己刚开始写是没有头指针和头节点的区分,导致自己能运行对,但就是过不了(然后去找度娘),知道了头节点和头指针的区别。
头节点(在存数前有个只含指针的head)
头节点
有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了,这是头节点的优势。
总的来说,考察的是单链表的插入,基础题
typedef的用法
简单来讲,typedef的作用就是给某些类型定义别名,和#define类似,但是比#define更加灵活。
这里我暂时只遇到了两个使用场景:
第一处,定义普通数据类型
typedef int ElementType; // 所以包含这个语句的C文件中ElementType就代表了int
第二处,定义结构体和指针
struct Node;
typedef struct Node * PtrToNode; // 表示可以用PtrToNode类型代替指向结构体Node的指针类型
typedef PtrToNode List; // 表示List是PtrToNode的别名,其作用相同
typedef PtrToNode Position; // 同上
List head = L;
L=L->Next;
List p=(List)malloc(sizeof(List));
p->Data=X;
p->Next=NULL;
if(L==NULL){
head->Next=p;
return head;
}
List q=head;
while(L->Data<X){
q=L;
L=L->Next;
if(L->Next==NULL){//L->Next->Next
L->Next=p;
return head;
}
}
p->Next=L;
q->Next=p;
return head;
浙大的数据结构题的单链表都带了一个头结点,以后遇到同类型的需要注意。
**都从各自的头结点开始,先指向下一位,**然后开始比较大小,把小的放前面,如果有一条比较完了,那么后面判断哪一条还未比较完,最后返回L。
List Merge( List L1, List L2 ){
List a,b;
a=L1->Next,b=L2->Next;
List c=(List)malloc(sizeof(List));
List l=c;
while(a&&b){
if(a->Data>=b->Data){
c->Next=b;
c=b;
b=b->Next;
}else{
c->Next=a;
c=a;
a=a->Next;
}
}
if(a)c->Next=a;
if(b)c->Next=b;
L1->Next=NULL;
L2->Next=NULL;
return l;
}
为什么是 L1->Next = NULL ; 而不是直接 L1= NULL;
因为pta很多的链表都会自带一个头结点,
他给你的是自带头结点的链表,
如果直接L1=NULL,就相当于把头结点赋值空了,
所以就会答案错误。