线性表的基本概念
线性表的定义与基本操作
- 相同有限数据元素构成的有限序列,线性表是一种逻辑结构,顺序表(数组)和链表是存储结构
//静态数组
# define MaxSize 50
struct SqList{
ElemType data[MaxSize];
int length;
};
//动态数组
# define InitSize 50
struct SeqList{
ElemType* data;
int length,Maxsize;
//因为这里是动态创建的,只知道初始长度
//需要确定其最大的长度因为是有限的
};
//需要动态创建
SeqList L;
L.data = new int[InitSize];
- 基本操作
需要真正对表进行修改的则需要传入&L,否则直接按值传递
bool InitList(&L);//初始化
int Length(L);//返回长度
int LocateElem(L,e);//返回值为e的元素的位置
ElemType GetElem(L,i);//返回i位置的元素值
bool ListInsert(&L,i,e);//在i位置插入元素
bool ListDelete(&L,i,&e);//删除i位置的元素,返回到e
void PrintList(L);//打印L中的元素
bool Empty(L);//判断线性表是否为空
bool DestoryList(&L);//销毁线性表
//简单实现
bool ListInsert(SqList &L,int i ,ElemType e){
if(i<1&&i>length+1)//这里是length+1但是不能是其他,因为可以加在当前list的尾部,这里指的是顺序表的位序
return false;
if(L.length>=MaxSize)//当前存储空间已满,不能插入
return false;
for(int j=L.length;j>=i;j--)//这里是线性表的下标
L.data[j]=L.data[j-1];
L.data[i-1]=e;
L.length++;
return true;
注意:凡是涉及到位置的都要先判断一下位置是否合理,然后涉及到实际位置(索引)和线性表本身的顺序不一致,需要注意一下
线性表的链式表示
单链表
- 节点类型
typedef struct LNode{
ElemType data;
struct LNode* next;
}LNode ,*LinkList;
- 头指针
始终设置一个指针指向第一个节点,通常用来标识一个单链表。 - 头节点
头节点通常里面不存任何信息,也可以存储一些表长等信息。
头节点有两个优点:
a、因为头节点的指针域指向第一个节点,所以正常的第一个节点和其他所有节点的所有操作都一致了,因为不需要对头指针进行改变了
b、无论链表是否为空,头指针都指向头节点,这样非空链表和空链表的处理方式就一致了
单链表的基本操作
1. 插入方法
头插法
首先创立头节点,然后使用不断生成新节点挂在头节点的后面,生成链表的元素顺序和输入数据的顺序相反。
LinkList CreateList1(LinkList &L){//已经传入L,后面直接分配空间即可
LNode * s;
int x;
L=(LinkList)malloc(sizeof(LNode));
L->next = NULL;
cin>>x;
while(x!=9999){
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
cin>>x;
}
return L;
尾插法
尾插法必须增加一个指针始终指向当前链表的尾部,这样才能知道下一个元素插入到哪里
LinkList CreateList2(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L -> next = NULL;
LNode *r=L,*s;
int x;
cin>>x;
while(x!=9999){
s = (ListNode)malloc(sizeof(LNode));
s->data = x;
// s->next = NULL;如果最后直接在外面进行一次置NULL,这里就不用置NULL了
r->next = s;
r = s;
cin>>x;
}
r->next = NULL;
return L;
}
2. 按序号查找节点
LNode *GetElem(LinkList L,int i){
int j = 1;
LNode *p = L->next;
if (i == 0)
return L;
if (i<1)
return NULL;
while(j!=i){
p=p->next;
j++;
}
return p;
}
3. 按值查找节点
LNode* LocateElem(LinkList L,ElemType e){
LNode* p = L->next;
while( p!=NULL&&p->data!=e){
p=p->next;
}
return p;//没找到就是NULL不可能是其他的值,因为一定会到末尾
}
4. 插入节点操作
分为前插和后插,一般采用的是后插操作
p = GetElem(L,i-1);
s->next = p->next;
p->next = s;
前插操作:(两种方法)
- 使用GetElem方法找到第i-1个节点,然后使用后插操作
- 仍然使用后插操作,但是将后插后的节点数值与插入节点之前的节点数值进行一次交换
s->next = p->next;
p->next = s;
temp = p->data;
p->data = s->data;
s->data = temp;
5. 删除节点操作
删除操作(2种方法)
- 查找到被删节点的前驱节点,然后执行删除操作
p = GetElem(L,i-1);
q = p->next;
p->next = q->next;
free(q);
- 删除当前节点可以用删除当前节点的后继节点来操作,将后继节点的值赋值给前面节点,然后直接删除后继
q = p->next;
p->data= p->next->data;
p->next = q->next;
free(q);
双链表
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
循环链表
- 循环单链表
最后一个节点的next域不指向NULL,指向头指针,判空条件:头节点的指针域是否为头节点 - 循环双链表
循环双链表的处理注意prior和next节点都要保证循环指向,判空条件:prior和next都指向L
静态链表
借助数组来描述线性表的链式存储结构,节点也有data和next域,但是这里的指针是节点的相对地址(数组下标),同静态数组一样也要预先分配一个空间
# define MaxSize 50
typedef struct{
ElemType data;
int next;
}SLinkList[MaxSize];
静态链表以-1为终点结束