线性表
抽象数据类型定义形式:
ADT List
{
数据对象 ;
数据关系;
基本操作;{初始化操作;销毁操作;引用类型操作;加工型操作;}
}
链式存储
一组数据类型相同的节点串接成一个单向链表,每一个节点是一个结构体变量,由数据域和指针域组成。
带头节点的单向链表
头指针指向第一个结点的地址,当头指针为空时,链表为空。
建立
typedef struct
{
char name[20];
float score;
} STD;
typedef STD ElemType;
typedef struct Londe
{
ElemType data; //data是一个STD类型的变量
struct Lnode *next;
}LNode,*LinkList; //LNode是结点数据类型,LinkList为指向节点的指针类型
初始化
带头结点的链表更加常用
int initList(LinkList *L)
{
*L = (LinkList)malloc(sizeof(Lnode)); //L是指向头指针的指针变量,(*L)是头指针,头指针指向头结点位置
if(*L == NULL)
return 0;
(*L)->next = NULL; //头结点为空
return 1;
}
调用:
int main()
{
LinkList L;
if(LinkList(&L))
printf("初始化成功!\n");
else
printf("初始化失败!\n");
}
插入
- 任意位置插入
第一个结点和其他结点操作相同
int insertList(LinkList* L, int i, ElemType e)
{
LinkList p = *L, s; //p指向头结点
int pos = 0; //记住p
while (p != NULL && pos < i - 1)
{
p = p->next;
pos++;
}
if (p == NULL || pos > i - 1)
return 0;
s = (LinkList)malloc(sizeof(Lnode));
s->data = e;
s->next = p->next;
p->next = s;
return 1;
}
调用:
ElemType e;
printf("请输入需要插入的数据\n:");
scanf("%s%f", &e.name, &e.score);
int pos;
printf("请输入需要插入的位置:\n");
scanf("%d", &pos);
if (insertList(&L, pos, e) != 0)
printf("插入成功!\n");
else
printf("插入位置不合理,失败!\n");
printList(L);
- 头插
头指针地址不变,内容指向新节点,新节点成为新的头结点,没有遍历
int frontinsertList(LinkList L)
{
ElemType e;
printf("请输入数据:");
scanf("%s%d", &e.name, &e.time);
LinkList p = L, q;
q = (LinkList)malloc(sizeof(Lnode));
q->data = e[i];
q->next = p->next;
p->next = p;
return 1;
}
- 尾插
直接在链表结尾加上新结点,需要遍历,要将下一个结点置空
int rearinsertList(LinkList L)
{
ElemType e;
printf("请输入数据:");
scanf("%s%d", &e.name, &e.time);
LinkList p = L->next,q;
while (p)
p = p->next;
q = (LinkList)malloc(sizeof(Lnode));
q->data = e[i];
q->next = NULL; //一定要做,不然会报错
p->next = p;
return 1;
}
删除
删除指定位置:
int deleteList(LinkList *L, int i)
{
LinkList q,p = *L;
int pos = 0;
while (p->next!= NULL && pos < i - 1)
{
p = p->next;
pos++;
}
if (p->next == NULL && pos < i - 1)
return 0;
q = p->next;
p->next = q->next;
free(q);
return 1;
}
调用:
ElemType e;
int pos;
printf("请输入需要删除的数据的位置:\n");
scanf("%d", &pos);
if (deleteList(&L, pos))
printf("删除成功!\n");
else
printf("删除失败!\n");
printf("最新的数据表为:\n");
printList(L);
修改
int reviseList(LinkList L, int i, ElemType e)
{
LinkList p = L->next;
int n = 1;
while (p && n < i)
{
p = p->next;
n++;
}
if (p == NULL || n > i)
return 0;
p->data = e;
return i;
}
调用:
ElemType e;
printf("请输入需要修改的数据的位置:");
int pos;
scanf("%d", &pos);
printf("请输入需要修改的数据:");
scanf("%s%d", & e.name, &e.time);
if (reviseList(L, pos, e))
printf("修改成功!");
else
printf("修改失败!");
遍历(打印)
int printList(LinkList L)
{
LinkList p = L->next;
if (p == NULL)
{
printf("链表为空!\n");
return 0;
}
while (p)
{
printf("%s发生在%d年\n", p->data.name, p->data.time);
p = p->next;
}
return 1;
}
调用:
printList(L);
查找
根据姓名查找:
int locationList(LinkList L, char* name)
{
LinkList p = L->next;
int j = 1;
while (p)
if (strcmp(p->data.name, name))
{
p = p->next;
j++;
}
else return j;
if (p == NULL)
return 0;
}
调用:
ElemType e;
char name[20];
printf("请输入事件的名称:\n");
scanf("%s", &name);
if(pos = locationList(L, name))
printf("这是第%d个事件");
else
printf("未查询到该事件!");
定位
根据位置得到数据:
int getList(LinkList L, int i, ElemType* e)
{
LinkList p = L->next;
int n = 1;
while (p && n < i)
{
p = p->next;
n++;
}
if (p == NULL || n > i)
return 0;
*e = p->data;
return 1;
}
调用:
ElemType e;
int pos;
printf("请输入需要的数据的位置:\n");
scanf("%d", &pos);
getList(L, pos, &e);
printf("%s发生的时间为:%d年", e.name, e.time);
求长
单向链表和顺序表不一样,需要遍历求长
int LinkLength(LinkList *L)
{
LinkList p = (*L)->next;
int n = 0;
while (p) {
n++;
p = p->next;
}
return n;
}
调用:
LinkLength(&L);
可能遇到的报错
- 引发了异常: 读取访问权限冲突。 p 是 0xCCCCCCCC
这是因为p是个野指针!
只要将它赋空p = NULL 就好。 - 引发了异常: 读取访问权限冲突。 p 是 nullptr
这里的p是个空指针,如果报了这样的错,说明使用了p->data 或p->next之类的语句,但空指针的数据和指针是不能读取或者设置的。
检查一下是不是遍历的条件设置有问题。