一、顺序表
顺序存储,通过静态或动态开辟[数组]存储数据,可分为静态顺序表和动态顺序表
静态顺序表 SqList
1. 类型定义
#define MaxSize 10
typedef struct
{
ElemType data[MaxSize]; //静态数组 存放元素类型为ElemType,使用完自动回收
int length; //当前静态数组中元素个数,即表长
}SqList;
2. 初始化
bool InitList(SqList &L)
{
int i = 0;
for(i = 0; i<MaxSize; i++) //数组元素设置为0
L.data[i] = 0;
L.length = 0; //表长设置为0
return true;
}
3. 判空
bool Empty(SqList L)
{
return (L.length == 0);
}
4. 插入元素
bool ListInsert(SqList &L, int i, ElemType e) //插入到表的第i位,即data[i-1]
{
if(i<1|| i>L.length+1|| L.length==MaxSize) //非法输入或表满
return false;
int j = 0;
for(j = L.length; j>=i; j--)
L.data[j] = L.data[j-1]; //从后至前, 将data[i-1]及之后元素后移
L.data[i-1] = e; //插入
L.length++; //表长+1
return true;
}
5. 删除元素
bool ListDelete(SqList &L, int i, ElemType &e) //找到表的第i位,即data[i-1],返回至e并从表中删除
{
if(i<1|| i>L.length) //非法输入
return false;
e = L.data[i-1]; //导出data[i-1]至e
int j = 0;
for(j = i; j<=L.length - 1; j++) //从前至后,将data[i]及之后元素前移
L.data[j-1] = L.data[j]
L.length--; //表长-1
return true;
}
6. 定位元素位序
int LocateElem(SqList L,ElemType e)
{
int i = 0;
for(i = 0; i<L.length ;i++)
if(L.data[i] == e)
return i + 1; //找到data[i],位置为第i+1个元素
return 0; //查找失败
}
注:顺序表的LocateElem返回的是第一次出现元素e的位序,链表的LocateElem返回的是第一次出现元素e的结点的指针
7. 获取位序元素
ElemType GetElem(SqList L,int i)
{
assert(i<1|| i>L.length) ; //非法输入
return L.data[i-1];
}
注:顺序表的GetElem返回的是位序i的元素值,链表的GetElem返回的是第i结点的指针
动态顺序表 SeqList
1. 类型定义
#define InitSize 10 //第一次动态开辟的Elemtype数量
typedef struct
{
ElemType* data; //动态数组 存放元素类型为ElemType
int length; //当前动态数组中元素个数,即表长
int MaxSize; //当前动态数组中元素的最多个数,即最大表长
}SeqList;
2. 初始化
bool InitList(SeqList &L)
{
L.data = (ElemType*)malloc(InitSize*sizeof(Elemtype)) //在L.data动态开辟InitSize个Elemtype的大小
if(L.data == NULL)
return false;
L.length = 0; //表长设置为0
L.MaxSize = InitSize; //最大表长设置为InitSize
return true;
}
3. 扩大空间
bool IncreaseSize(SeqList &L, int len) //扩充len个空间
{
ElemType* p = (ElemType*)realloc(L.data, (L.MaxSize + len)*sizeof(ElemType));
//在临时指针p动态开辟(当前最大表长+len)个Elemtype的大小,并将L.data数据导入
if(p == NULL)
return false;
L.data = p; //p的地址给L.data
L.MaxSize += len; //最大表长扩大len
return true;
}
4. 插入元素
bool ListInsert(SeqList &L, int i, ElemType e) //插入到表的第i位,即data[i-1]
{
if(i<1|| i>L.length+1|| L.length==L.MaxSize) //非法输入或表满
return false;
int j = 0;
for(j = L.length, j>=i, j--)
L.data[j] = L.data[j-1]; //从后至前, 将data[i-1]及之后元素后移
L.data[i-1] = e; //插入
L.length++; //表长+1
return true;
}
二、链表
建立结点,每个结点除了存放数据之外,还存放指向其他结点的指针.
单链表 LinkList
1. 类型定义
typedef struct LNode
{
ElemType data; //数据域
struct LNode* next; //指针域,指向下一个节点
}LNode,*LinkList; //LinkList用来强调单链表,LNode强调结点
LinkList L; L为未被赋值的LNode型的指针,类似于初始化函数,L指针保存的地址初始化为指向开辟的LNode空间的地址,即指针本身发生了变化,所以要&,类似于修改链表节点值的函数,只修改了L指向的LNode里的data和next值,不改变L本身,所以不用&.
2. 初始化
//不带头结点
bool InitList(LinkList &L)
{
L = NULL; //防止脏数据
return true;
}
//带头结点
bool InitList(LinkList &L)
{
L = (LNode*)malloc(sizeof(LNode));
if(L == NULL)
return false;
L->next = NULL;
return true;
}
3. 判空
//不带头结点
bool Empty(LinkList L)
{
return (L == NULL);
}
//带头结点
bool Empty(LinkList L)
{
return (L->next == NULL);
}
4. 求表长
//无头结点
int Length(LinkList L)
{
if(Empty(L))
return 0; //空表为0
int length = 1;
Node* p = L; //从第一个结点出发
while(p->next != NULL)
{
p = p->next;
length++;
}
return length;
}
//有头结点
int Length(LinkList L)
{
if(Empty(L))
return 0;
int length = 1;
Node* p = L->next;
while(p->next != NULL)
{
p = p->next;
length++;
}
return length;
}
5. 给定元素获取指针
//不带头结点
LNode* LocateElem(LinkList L,ElemType e)
{
LNode* p = L; //从第一个结点开始找
while(p!= NULL && p->data!=e) //直到找到e元素
p = p->next;
return p; //找到后返回该结点指针 找不到返回NULL
}
//带头结点
LNode* LocateElem(LinkList L,ElemType e)
{
LNode* p = L->next;
while(p!= NULL && p->data!=e)
p = p->next;
return p;
}
注:都要从第一个存储数值的结点开始匹配e的值
6. 给定位序获取指针
//不带头结点
LNode* GetElem(LinkList L,int i)
{
if(i<1)
return NULL; //非法输入
LNode* p = L; //从第一个结点开始
int j = 1;
while(p != NULL && j<i)
{
p = p->next;
j++;
}
return p ; //返回i结点指针,若i大于表长则返回NULL
}
//带头结点
LNode* GetElem(LinkList L,int i)
{
if(i == 0)
return L; //返回头结点
if(i<1)
return NULL; //非法输入
LNode* p = L->next; //从第一个结点开始
int j = 1;
while(p != NULL && j<i)
{
p = p->next;
j++;
}
return p ; //返回i结点指针,若i大于表长则返回NULL
}
7. 指定结点后插
bool InsertNextNode(LNode* p,ElemType e)
{
if(p == NULL)
return false;
LNode* s=(LNode*)malloc(sizeof(LNode)); //s指向新插入的结点
if(s == NULL)
return false; //内存分配失败
s->data = e; //新结点赋值
s->next = p->next; //新结点指向原p之后的结点
p->next = s; //p指向新结点
return true;
}
8. 指定结点前插
原理:指定结点后插之后,交换两结点的data数据
bool InsertPriorNode(LNode* p,ElemType e)
{
if(InsertNextNode(p,e))
{
ElemType temp = p->data;
p->data = p->next->data;
p->next->data = temp;
return true;
}
else
return false;
}
9. 指定位序插入
原理:利用GetElem找到i-1结点的指针,对i-1结点进行后插操作
//不带头结点
bool ListInsert(LinkList &L, int i ,ElemType e)
{
if(i<1)
return false; //非法输入,i过小
if(i == 1) //第一个结点
{
LNode* s=(LNode*)malloc(sizeof(LNode));//s指向新插入的结点
s->data = e; //新结点赋值
s->next = L; //新结点的next指针指向原来的首结点
L = s; //改变指向第一个结点的指针L
}
LNode* p = GetElem(L,i-1); //p指向i-1结点
return InsertNextNode(p,e)
//如果return false,可能由于非法输入,i大于表长+1导致GetElem返回NULL 或者内存开辟失败
}
//带头结点
bool ListInsert(LinkList &L, int i ,ElemType e)
{
if(i<1)
return false; //非法输入,i过小
LNode* p = GetElem(L,i-1); //p指向i-1结点
return InsertNextNode(p,e)
}
注:无头结点需要对i=1进行特殊处理,因此大多数情况都建立带头结点链表
10. 指定结点删除
原理:将后续结点的数据域和指针域转移给其本身,然后释放后续结点
bool DeleteNode(LNode* p, ElemType &e)
{
if(p == NULL || p->next == NULL)
//p指向尾结点时方法不可用,需遍历至next指向p的结点,另其next指针指向NULL,并释放原后续结点p
return false;
e = p->data;
LNode* q = p->next; //q指向p后续结点
p->data = q->data; //转移数据域
p->next = q->next; //转移指针域
free(q);
return true;
}
11. 指定位序删除
原理:利用GetElem找到i-1结点的指针,进行后续操作
//不带头结点
bool ListDelete(LinkList &L, int i, Elemtype &e)
{
if(i == 1)
{
Node* p = L;
e = p->data;
L = L->next;
free(p);
}
Node* p = GetElem(L,i-1); //p指向i-1结点
if(p == NULL || p->next == NULL)//i-1 结点为空或i-1结点无后续结点
return false;
LNode* q = p->next; //q指向i结点,即待删除结点
e = q->data;
p->next = q->next;
free(q);
return true;
}
//带头结点
bool ListDelete(LinkList &L, int i, Elemtype &e)
{
Node* p = GetElem(L,i-1);
if(p == NULL || p->next == NULL)
return false;
LNode* q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
12. 头插法建立单链表
对传入的已被初始化的链表头指针L进行逆向建表
LinkList List_HeadInsert(LinkList &L)
{
Elemtype x;
LNode* s;
scanf("",&x);
while(x!=9999)
{
s = (LNode*)malloc(sizeof(Node));
s->data = x;
s->next = L->next;
L->next = s;
scanf("",&x);
}
return L;
}
13 .尾插法建立单链表
对传入的已被初始化的链表头指针L进行正向建表
LinkList List_TailInsert(LinkList &L)
{
ElemType x;
LNode* r = L;//尾指针
LNode* s ; //s指向新结点
scanf("",&x);
while(x!=9999)
{
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = NULL;
r->next = s; //当前尾结点指向新结点
r = s; //尾指针指向新结点
scanf("",&x);
}
return L;
}
双链表 DLinkList
1.类型声明
typedef struct DNode
{
ElemType data;
struct DNode* prior;
struct DNode* next;
}DNode,*DLinkList;
2.初始化
bool InitList(DLinkList &L)
{
L = (DNode*)malloc(sizeof(DNode));
if(L == NULL)
return false;
L->prior = NULL;
L->next = NULL;
return true;
}
3.指定结点后插
bool InsertNextNode(DNode* p,ElemType e)
{
if(p == NULL)
return false;
DNode* s=(DNode*)malloc(sizeof(DNode)); //s指向新插入的结点
if(s == NULL)
return false;
s->data = e;
s->next = p->next;
if(p->next != NULL) // p如果没有后续结点
p->next->prior = s; //p的下一个结点的前指针指向新结点
s->prior = p;
p->next = s;
return true;
}
4.指定结点删除
bool DeleteNode(DNode* p, ElemType &e)
{
if(p == NULL || p->next == NULL)
return false;
e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return true;
}
循环链表 CLinkList
1.单循环链表
类型定义与单链表相同
初始化时需要 L->next = L;
判空条件 L->next == L
2.双循环链表
类型定义与双链表相同
初始化时需要 L->prior = L; L->next = L;
判空条件 L->next == L
静态链表 SLinkList
1. 类型定义
#define MaxSize 10
typedef struct
{
ElemType data;
int next;
}SlinkList[MaxSize];