特点:
- 链式存储的线性表,便于插入和删除,但不能随机存储(即根据一定的公式快速定位某个结点,只能从前往后遍历)
- 节点包括:任意元素类型的数据域、结点类型的指针。
- 最好设置一个头结点,这样对第一个结点操作与对其他结点操作一致,对空表的操作和对非空表的操作一致。
单链表结点的描述
typedef struct{
int num;
char data; //这两个都是数据域,也可以单独定义一个结构体将这两个数据域打包
struct Lnode *next; //指针域,一个类型为Lnode的指针
}Lnode ,*LinkList; //定义一个类型为Lnode的指针--LinkList;
一个小小的突发状况:. 和 -> 的区别
先说结论:结构体用点,结构体指针用箭头。 现在编译器优化后,两者可以通用
- ".“直接读做"的”。
->读作"指向的结构体的”- C需要显式区分直接访问与指针间接访问。
应试需要,简单记为,顺序表用 . ,单链表用 ->
头插法插入单链表
-思路:
- 定义一个头结点,包括使用malloc函数给其分配空间
- 头结点初始指向空
- while语句接收数据,插入单链表
- 为待插入的结点分配空间
- 待插入结点获取数据
- 待插入的结点指针域指向下一个结点的数据域,上一个结点的指针域指向待插入结点的数据域。顺序很重要!不能颠倒
- 返回头结点
//头插法
LinkList LHead(LinkList &L){
// 返回值类型为LinkList ,传入的参数:类型为LinkList的单链表,记得带 & 符号,因为会对原有数据修改
Lnode *s; //一个Lnode类型的指针.这个是待插入指针
int x; //接收传入的数据
L=(LinkList)malloc(sizeof(Lnode)); //malloc函数给 头结点 分配初始空间,强制转换类型为Lnode;
//将L理解为头结点即可,其实并没有什么变量是专门指这整张单链表.表示一整张单链表只需要一个头指针就够了
L->next=NULL; //初始化头结点指向空
scanf("%d",&x); //接收第一个传入数据
while(x!=99){ //当输入99时才跳出循环,这样可以保证连续输入多个数据
s=(Lnode*)malloc(sizeof(Lnode)); //给待插入结点分配空间,这样才能存数据
s->data=x; //没有搞清哪个是待插入结点,哪个是待插入链表,晕了,这里就不会写了.解决:Line 19
s->next=L->next;
L->next=s; //这两行顺序不能颠倒
scanf("%d",x);
}
return L; //返回头结点
}
尾插法插入单链表
- 分配空间
- while接收输入
- 尾结点指向新结点
//尾插法
LinkList LTail(LinkList &L){
L=(LinkList)malloc(sizeof(Lnode));
Lnode *s,*r;
int m;
s=(Lnode *)malloc(sizeof(Lnode));
printf("请输入尾插法要插入的数据\n");
scanf("%d",&m);
while(m!=99){
s->data=m;
r->next=s;
r=s;
scanf("%d",&m);
}
r->next=NULL;
return r;
}
按序号查找
-思路:
- 设置j用来记录结点位置
- 头结点赋值给p
- 三种情况:一、i为0,返回头结点。二、i小于0,报错。三、i>0
- 第三种情况,while循环让p指针不断后移,直至j==i
LinkList LSearch(LinkList L,int i){
int j=0;
//Lnode p = (Lnode)malloc(sizeof(Lnode));
Lnode *p=L->next;
if(i==0)
return L;
if(i<0)
return NULL;
while(p&&j<i){
p=p->next;
j++;
}
return p;
}
按值查找
-思路:
- 头结点赋值给p
- while,直至p=null 或 找到
//按值查找
LinkList LSearch_(LinkList L,int x){
Lnode *p=L->next;
while(p!=NULL&&p->data!=x)
p=p->next;
return p;
}
按位置插入
-思路:
- 根据按位置查找获取待插入位置的前驱结点
- 类比头插法插入
//根据位置插入
LinkList LInsert(LinkList L,int i){
Lnode *s=(Lnode)malloc(sizeof(Lnode));
p=LSearch(LinkList L,int i-1);
s->next=p->next;
p->next=s;
}
删除
思路:
- 找到前驱结点
- 前驱结点的指针域跳过待删除的结点,这样逻辑上就删除了
- free释放被删除结点的空间,物理删除
//删除
LinkList LDelete(LinkList L){
p=LSearch(L,i-1);
q=p->next;
p->next=q->next;
free(q);
}