目录
1.顺序表
顺序表的结构体定义
顺序表的一大特点:逻辑上上相邻的两个元素,在物理位置上也相邻。
//静态分配法
typedef struct SeqList //顺序表的结构体定义
{
ElemType data[MaxSize]; //定义的数组,用来存元素
int length; //顺序表当前的长度
}SeqList;
//动态分配法
插入:在第i个位置中插入元素e
//插入操作:函数为ListInsert
// 函数功能:在位置为i的地方,插入元素e。(注:i从1开始,而数组的下标从0开始)
//L代表的是顺序表,i代表要插入的位置,从1开始,e是要插入的元素
//假如在位置为2的地方插入60,也就是在下标为1的地方插入
bool ListInsert(seqList& L, int i, ElemType e)
{
if (i<1 || i>L.length + 1)
return false; //判断插入位置是否合法
if (L.length >= MaxSize) //元素存满了,不能在存了
return false; //超出空间了,只能访问下标为0到49
for (int j = L.length; j >= i; j--) //移动顺序表中的元素,依次往后移动
L.data[j] = L.data[j - 1];
L.data[i - 1] = e; //数组下标从0开始,插入第一个位置,访问下标为0
L.length++; //插入元素后,顺序表长度需要加1
return true;
}
删除:删除第i个位置的元素
//删除操作:函数为ListDelete
//函数功能:删除第i个位置的元素,用引用变量e返回
bool ListDelete(SeqList& L, int i, ElemType& e)
{
if (i < 1 || i>L.length)
return false; //判断i的范围是否合法
if (L.length == 0) //顺序表中没有元素,那么不需要删除
return false;
e = L.data[i - 1]; //将被删除元素的值赋给e
for (int j = i; j < L.length; j++)
L.data[j - 1] = L.data[j]; //将第一个位置之后的元素向前移
L.length--; //删除后,顺序表长度需要减1
return true;
}
查找:查找e在第几个位置(位置从1开始)
//查找操作:函数为LocateElem
//函数功能:在顺序表中,查找e,查找成功,返回位置(位置从1开始)。查找失败,返回0.
int LocateElem(SeqList L, ElemType e)
{
int i;
for (i = 0; i < L.length; i++)
if (L.data[i] == e)
return i + 1;
return 0;
}
完整的代码
#include <stdio.h> //头文件
#include <stdlib.h>
#define MaxSize 50 //定义线性表的最大长度
typedef int ElemType; //对int起别名,为了代码注释。顺序表元素的类型
typedef struct SeqList //顺序表的结构体定义
{
ElemType data[MaxSize]; //定义的数组,用来存元素
int length; //顺序表当前的长度
}SeqList;
//插入操作:函数为ListInsert
// 函数功能:在位置为i的地方,插入元素e。(注:i从1开始,而数组的下标从0开始)
//L代表的是顺序表,i代表要插入的位置,从1开始,e是要插入的元素
//假如在位置为2的地方插入60,也就是在下标为1的地方插入
bool ListInsert(SeqList &L, int i, ElemType e)
{
if (i<1 || i>L.length + 1)
return false; //判断插入位置是否合法
if (L.length >= MaxSize) //元素存满了,不能在存了
return false; //超出空间了,只能访问下标为0到49
for (int j = L.length; j >= i; j--) //移动顺序表中的元素,依次往后移动
L.data[j] = L.data[j - 1];
L.data[i - 1] = e; //数组下标从0开始,插入第一个位置,访问下标为0
L.length++; //插入元素后,长度需要加1
return true;
}
//删除操作:函数为ListDelete
//函数功能:删除第i个位置的元素,用引用变量e返回
bool ListDelete(SeqList& L, int i, ElemType& e)
{
if (i < 1 || i>L.length)
return false; //判断i的范围是否合法
if (L.length == 0) //顺序表中没有元素,那么不需要删除
return false;
e = L.data[i - 1]; //将被删除元素的值赋给e
for (int j = i; j < L.length; j++)
L.data[j - 1] = L.data[j]; //将第一个位置之后的元素向前移
L.length--; //删除后,顺序表长度需要减1
return true;
}
//查找操作:函数为LocateElem
//函数功能:在顺序表中,查找e,查找成功,返回位置(位置从1开始)。查找失败,返回0.
int LocateElem(SeqList L, ElemType e)
{
int i;
for (i = 0; i < L.length; i++) //遍历这个顺序表
if (L.data[i] == e)
return i + 1; //加1就是元素在顺序表中的位置
return 0;
}
//打印顺序表元素
void PrintList(SeqList &L)
{
for (int i = 0; i < L.length; i++)
{
printf("%3d", L.data[i]); //要求所有元素打印到一排,%3d是输出的长度
//注:%3d是右对齐
}
printf("\n");
}
int main()
{
SeqList L; //顺序表的名称
bool ret; //查看返回值,布尔型是True,或者False
ElemType del; //用来存要删除的元素
//首先手动在顺序表中前3个元素赋值
L.data[0] = 1;
L.data[1] = 2;
L.data[2] = 3;
L.length = 3; //总共3个元素,顺序表的长度为3
ret = ListInsert(L, 2, 60); //往位置为2的位置插入60这个元素
if (ret)
{
printf("插入成功\n");
PrintList(L); //打印成功后的顺序表
}
else
{
printf("插入失败\n");
}
ret = ListDelete(L, 1, del);//在顺序表中,删除第一个位置的元素,并把删除元素的值输出
if (ret)
{
printf("删除成功\n");
printf("删除元素值为%d\n", del);
PrintList(L);
}
else
{
printf("删除失败\n");
}
ret = LocateElem(L, 30);
if (ret)
{
printf("查找成功\n");
printf("元素位置为%d\n", ret);
PrintList(L);
}
else
{
printf("查找失败\n");
}
return 0;
}
2.单链表
单链表的结构体定义
示意图:
typedef struct LNode
{
ElemType data; //数据域
struct LNode* next; //指针域
}LNode,*LinkList;
头插法新建单链表L
理解的示意图:头插法和中间插入元素的代码是一样的。
最重要的三点:
1: s->next=L->next;
2: L->next=s;
//头插法新建链表
//函数为list_head_insert
LinkList list_head_insert(LinkList& L)
{
LNode* s; int x; //s是要插入的结点,x是插入元素的值
L = (LinkList)malloc(sizeof(LNode)); //带头结点的链表
L->next = NULL; //L->data里边没放东西,这个也就是初始化的操作
scanf("%d", &x); //从标准输入读取数据
//3 4 5 6 7 9999
while (x != 9999) {
s = (LNode*)malloc(sizeof(LNode)); //给s开辟一个新空间,强制类型转换
s->data = x; //把读取到的值,给新空间中的data成员(data指的是链表的数据域)
s->next = L->next; //让新结点(也就是待插入节点)的next指针指向链表的第一个元素
L->next = s; //L->next指向谁,谁就是第一个元素。让s作为第一个元素(不是头结点)
scanf("%d", &x); //读取标准输入
}
return L;
}
尾插法新建单链表L
注意:尾插法它是从空的链表开始插的。
最重要的两步:
1: r->next = s; //让尾部结点指向新结点
2: r = s; //r指向新的表尾结点
//尾插法新建链表
//函数为:list_tail_insert
LinkList list_tail_insert(LinkList& L)
{
int x;
L = (LinkList)malloc(sizeof(LNode)); //带头节点的链表,和头插法是一样的。
LNode* s, * r = L; //Linklist s,r=L;等价 r代表链表表尾结点,指向链表尾部
//3 4 5 6 7 9999
scanf("%d", &x);
while (x != 9999) {
s = (LNode*)malloc(sizeof(LNode)); //s是新插入的结点
s->data = x; //赋值
r->next = s; //让尾部结点指向新结点
r = s; //r指向新的表尾结点
scanf("%d", &x);
}
r->next = NULL; //尾结点的next指针赋值为NULL
return L;
}
按序号查找结点值
//按序号查找结点值
//函数为:GetElem
LNode* GetElem(LinkList L, int i) //i是位置 LNode*也可以写LinkList
{
int j = 1; //从位置1开始,遍历
LNode* p = L->next; //p移动了一次,让p指向第一个结点。 也可以写成LinkList p = L->next;
if (i == 0)
return L; //i是0返回头结点
if (i < 1)
return NULL; //i是负值就返回空
while (p && j < i)
{
p = p->next; //让p指向下一个结点
j++;
}
return p;
}
按值查找表结点:值第一次出现的位置
//按值查找
//函数为:LocateElem
LNode *LocateElem(LinkList L,ElemType e)
{
LNode *p=L->next;
while(p!=NULL&&p->data!=e)
p=p->next;
return p;
}
新结点插入第i个位置,值为e
插入结点的示意图:
//新结点插入第i个位置,值为e
//函数为:ListFrontInsert
bool ListFrontInsert(LinkList L, int i, ElemType e)
{
LinkList p = GetElem(L, i - 1); //拿到要插入位置的前一个位置的地址值
if (p == NULL)
{
return false;
}
LinkList s = (LNode*)malloc(sizeof(LNode));//为新插入的结点申请空间
s->data = e; //赋值
s->next = p->next; //插入结点的操作
p->next = s;
return true;
}
删除结点操作
示意图:
//删除第i个结点
//函数为:ListDelete
bool ListDelete(LinkList L, int i)
{
LinkList p = GetElem(L, i - 1); //查找删除位置的前驱结点
if (p == NULL) //删除的位置不存在
{
return false;
}
LinkList q;
q = p->next;
p->next = q->next;//断链
free(q);//释放对应结点的空间
q = NULL;
return true;
}
完整代码
#define _CRT_SECURE_NO_WARNINGS //解决scanf编译报错问题
#include <stdio.h> //头文件
#include <stdlib.h>
typedef int ElemType;
//结构体定义
typedef struct LNode
{
ElemType data; //数据域
struct LNode* next; //指针域----结构体指针
}LNode,*LinkList;
//头插法新建链表
//函数为list_head_insert
LinkList list_head_insert(LinkList& L)
{
LNode* s; int x; //s是要插入的结点,x是插入元素的值
L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
L->next = NULL;//L->data里边没放东西,这个也就是初始化的操作
scanf("%d", &x);//从标准输入读取数据
//3 4 5 6 7 9999
while (x != 9999) {
s = (LNode*)malloc(sizeof(LNode));//给s开辟一个新空间,强制类型转换
s->data = x; //把读取到的值,给新空间中的data成员(data指的是链表的数据域)
s->next = L->next; //让新结点(也就是待插入节点)的next指针指向链表的第一个元素
L->next = s; //L->next指向谁,谁就是第一个元素。让s作为第一个元素(不是头结点)
scanf("%d", &x);//读取标准输入
}
return L;
}
//尾插法新建链表
//函数为:list_tail_insert
LinkList list_tail_insert(LinkList& L)
{
int x;
L = (LinkList)malloc(sizeof(LNode));//带头节点的链表,和头插法是一样的。
LNode* s, * r = L; //Linklist s,r=L;等价 r代表链表表尾结点,指向链表尾部
//3 4 5 6 7 9999
scanf("%d", &x);
while (x != 9999) {
s = (LNode*)malloc(sizeof(LNode)); //s是新插入的结点
s->data = x; //赋值
r->next = s; //让尾部结点指向新结点
r = s;//r指向新的表尾结点
scanf("%d", &x);
}
r->next = NULL; //尾结点的next指针赋值为NULL
return L;
}
//打印链表中每个结点的值
//函数为:PrintList
void PrintList(LinkList L)
{
L = L->next; //相当于移动到第一个有元素的位置,也及时移动到头结点后一个元素
while (L != NULL)
{
printf("%3d", L->data); //打印当前结点数据,%3d是右对齐,长度为3。
L = L->next; //指向下一个结点
}
printf("\n");
}
//按序号查找结点值
//函数为:GetElem
LNode* GetElem(LinkList L, int i) //i是位置 LNode*也可以写LinkList
{
int j = 1; //从位置1开始,遍历
LNode* p = L->next; //p移动了一次,让p指向第一个结点。 也可以写成LinkList p = L->next;
if (i == 0)
return L; //i是0返回头结点
if (i < 1)
return NULL; //i是负值就返回空
while (p && j < i)
{
p = p->next; //让p指向下一个结点
j++;
}
return p;
}
//按值查找
//函数为:LocateElem
LNode* LocateElem(LinkList L, ElemType e) //e是要查的值, LNode*也可以写LinkList
{
LNode* p = L->next; //p移动了一次,让p指向第一个结点。 也可以写成LinkList p = L->next;
while (p != NULL && p->data != e) //p!=NULL和p是一个意思
p = p->next;
return p;
}
//新结点插入第i个位置,值为e
//函数为:ListFrontInsert
bool ListFrontInsert(LinkList L, int i, ElemType e)
{
LinkList p = GetElem(L, i - 1); //拿到要插入位置的前一个位置的地址值
if (p == NULL)
{
return false;
}
LinkList s = (LNode*)malloc(sizeof(LNode));//为新插入的结点申请空间
s->data = e; //赋值
s->next = p->next; //插入结点的操作
p->next = s;
return true;
}
//删除第i个结点
//函数为:ListDelete
bool ListDelete(LinkList L, int i)
{
LinkList p = GetElem(L, i - 1); //查找删除位置的前驱结点
if (p == NULL) //删除的位置不存在
{
return false;
}
LinkList q;
q = p->next;
p->next = q->next;//断链
free(q);//释放对应结点的空间
q = NULL;
return true;
}
int main()
{
LinkList L; //链表头,是结构体指针类型
LinkList search; //也可以写成 LNode *search;
//list_head_insert(L); //头插法:输入数据可以为3 4 5 6 7 9999
//printf("--------------------单链表的头插法--------------------------\n");
list_tail_insert(L); //尾插法
printf("--------------------单链表的尾插法-----------------------------\n");
PrintList(L); //打印链表
search = GetElem(L, 2); //查找链表第二个位置的元素值
if (search != NULL)
{
printf("---------------------按序号查找成功-------------------------\n");
printf("%d\n", search->data);
}
search = LocateElem(L, 6);//按值查询
if (search != NULL)
{
printf("---------------------按值查找成功--------------------------\n");
printf("%d\n", search->data);
}
ListFrontInsert(L, 2, 99);//新结点插入第2个位置,值为99
printf("--------------------在第二个位置插入99------------------\n");
PrintList(L);
ListDelete(L, 4);//删除第4个结点
printf("--------------------删除第四个结点------------------\n");
PrintList(L);
return 0;
}
3.双链表
双链表结构体定义
示意图:
typedef struct DNode
{
ElemType data; //数据域
struct DNode* prior, * next; //前驱指针, 后继指针
}DNode, * DLinkList;
头插法
示意图
//双链表头插法
//函数为:Dlist_head_insert
DLinkList Dlist_head_insert(DLinkList& DL)
{
DNode* s; //s为要插入的新结点
int x; //x为暂存的数据
DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表,申请空间,DL就是头结点
DL->next = NULL;
DL->prior = NULL; //初始化操作,把前驱和后继指针都写为NULL
scanf("%d", &x);//从标准输入读取数据
//3 4 5 6 7 9999
while (x != 9999) {
s = (DLinkList)malloc(sizeof(DNode));//申请一个空间空间,强制类型转换
s->data = x;
s->next = DL->next;
if (DL->next != NULL)//插入第一个结点时,不需要这一步操作
{
DL->next->prior = s;
}
s->prior = DL; //要插入的结点指向头结点
DL->next = s;
scanf("%d", &x);//读取标准输入
}
return DL;
}
尾插法
示意图
//双链表尾插法
//函数为:Dlist_tail_insert
DLinkList Dlist_tail_insert(DLinkList& DL)
{
int x; //要插入元素的值
DL = (DLinkList)malloc(sizeof(DNode));//带头节点的链表
DNode* s, * r = DL; //r代表尾指针, s为要插入的结点
DL->prior = NULL;
//3 4 5 6 7 9999
scanf("%d", &x);
while (x != 9999) {
s = (DNode*)malloc(sizeof(DNode));
s->data = x;
r->next = s;
s->prior = r;
r = s; //r指向新的表尾结点
scanf("%d", &x);
}
r->next = NULL;//尾结点的next指针赋值为NULL
return DL;
}
按序号查找结点值:与单链表一样
//按序号查找结点值
DNode* GetElem(DLinkList DL, int i)
{
int j = 1;
DNode* p = DL->next;
if (i == 0)
return DL;
if (i < 1)
return NULL;
while (p && j < i)
{
p = p->next;
j++;
}
return p;
}
新结点插入第i个位置,值为e
//新结点插入第i个位置
bool DListFrontInsert(DLinkList DL, int i, ElemType e)
{
DLinkList p = GetElem(DL, i - 1); //找前一个位置的地址
if (NULL == p)
{
return false;
}
DLinkList s = (DLinkList)malloc(sizeof(DNode));//为新插入的结点申请空间
s->data = e;
//下面这四步对应的是插入节点
s->next = p->next;
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
删除结点
示意图
//删除第i个结点
bool DListDelete(DLinkList DL, int i)
{
DLinkList p = GetElem(DL, i - 1);
if (NULL == p)
{
return false;
}
DLinkList q;
q = p->next;
if (q == NULL)//删除的元素不存在
return false;
p->next = q->next;//断链
if (q->next != NULL)
{
q->next->prior = p;
}
free(q);//释放对应结点的空间
return true;
}
完整代码
#define _CRT_SECURE_NO_WARNINGS //解决scanf编译报错问题
#include <stdio.h> //头文件
#include <stdlib.h>
typedef int ElemType;
//双链表结构体定义
//与单链表相比,增加了一个prior指针(也就是前驱指针)
typedef struct DNode
{
ElemType data; //数据域
struct DNode* prior, * next; //前驱指针, 后继指针
}DNode, * DLinkList;
//双链表头插法
//函数为:Dlist_head_insert
DLinkList Dlist_head_insert(DLinkList& DL)
{
DNode* s; //s为要插入的新结点
int x; //x为暂存的数据
DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表,申请空间,DL就是头结点
DL->next = NULL;
DL->prior = NULL; //初始化操作,把前驱和后继指针都写为NULL
scanf("%d", &x);//从标准输入读取数据
//3 4 5 6 7 9999
while (x != 9999) {
s = (DLinkList)malloc(sizeof(DNode));//申请一个空间空间,强制类型转换
s->data = x;
s->next = DL->next;
if (DL->next != NULL)//插入第一个结点时,不需要这一步操作
{
DL->next->prior = s;
}
s->prior = DL; //要插入的结点指向头结点
DL->next = s;
scanf("%d", &x);//读取标准输入
}
return DL;
}
//双链表尾插法
//函数为:Dlist_tail_insert
DLinkList Dlist_tail_insert(DLinkList& DL)
{
int x; //要插入元素的值
DL = (DLinkList)malloc(sizeof(DNode));//带头节点的链表
DNode* s, * r = DL; //r代表尾指针, s为要插入的结点
DL->prior = NULL;
//3 4 5 6 7 9999
scanf("%d", &x);
while (x != 9999) {
s = (DNode*)malloc(sizeof(DNode));
s->data = x;
r->next = s;
s->prior = r;
r = s; //r指向新的表尾结点
scanf("%d", &x);
}
r->next = NULL;//尾结点的next指针赋值为NULL
return DL;
}
//按序号查找结点值
DNode* GetElem(DLinkList DL, int i)
{
int j = 1;
DNode* p = DL->next;
if (i == 0)
return DL;
if (i < 1)
return NULL;
while (p && j < i)
{
p = p->next;
j++;
}
return p;
}
//新结点插入第i个位置
bool DListFrontInsert(DLinkList DL, int i, ElemType e)
{
DLinkList p = GetElem(DL, i - 1); //找前一个位置的地址
if (NULL == p)
{
return false;
}
DLinkList s = (DLinkList)malloc(sizeof(DNode));//为新插入的结点申请空间
s->data = e;
//下面这四步对应的是插入节点
s->next = p->next;
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
//删除第i个结点
bool DListDelete(DLinkList DL, int i)
{
DLinkList p = GetElem(DL, i - 1);
if (NULL == p)
{
return false;
}
DLinkList q;
q = p->next;
if (q == NULL)//删除的元素不存在
return false;
p->next = q->next;//断链
if (q->next != NULL)
{
q->next->prior = p;
}
free(q);//释放对应结点的空间
return true;
}
//双链表打印
void PrintDList(DLinkList DL)
{
DL = DL->next;
while (DL != NULL)
{
printf("%3d", DL->data);
DL = DL->next;
}
printf("\n");
}
int main()
{
DLinkList DL; //链表头
DLinkList search;
//Dlist_head_insert(DL); //头插法
//printf("------------双链表的头插法-------------\n");
//输入的数据可为 3 4 5 6 7 9999
Dlist_tail_insert(DL); //尾插法
printf("------------双链表的尾插法-------------\n");
PrintDList(DL); //打印链表
search = GetElem(DL, 2); //查找第二个位置的元素值
if (search != NULL)
{
printf("------------按序号查找成功----------\n");
printf("%d\n", search->data);
}
DListFrontInsert(DL, 3, 99);
printf("------------在第三个位置插入元素99后----------\n");
PrintDList(DL);
DListDelete(DL, 2);
printf("------------删除第2个位置----------\n");
PrintDList(DL);
return 0;
}