一、线性表的基本概念
1.1 线性表定义(可能考的概念)
- 具有相同数据类型的n个数据元素的有限序列,其中n为表长,n等于0表示为空表。
- 除了第一个和最后一个元素,每个元素有且仅有一个直接前驱和直接后继
1.2 两种存储形式
绪论部分已经提过每种数据结构都有顺序存储结构和链式存储结构。
二、线性表的顺序存储结构(顺序表/数组)
2.1 概念
- 线性表的顺序存储结构又称顺序表。
- 顺序的意思是所有的数据元素都是存在一组地址连续的存储单元中的。即逻辑结构和物理结构都是连续的。
理解:这部分经常和线性表的链式存储放在一起考。可以理解为每个班的学生,依次从教室的第一个位置开始做,每个位置都不能空。也就是逻辑和物理上都是连续的,没有空隙。而链式存储结构是可以乱坐的。学生可以几个人挨在一起可以,也可以中间空的做。就是一个字乱。也可以不乱。
2.2 顺序存储结构体代码(理解)
#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int length;
}SqList;
理解:从结构体定义可以看出,顺序表长短是固定好的。学JAVA和C的的也知道,数组都是不能增加。想要增加,必须动态申请。由此引出,考试常考的c语言动态申请空间的代码
L.data = (ElemType *)malloc(sizeof(Elemtype)*InitSize);
理解:这段代码可能考天空。ElemType表示数据类型,如int,char等;其次InitSize 表示申请的长度;
例如:L=(LNode *)malloc(sizeof(LNode));写多了,多做题就会了。
2.3 顺序表基本操作(要背的代码)
绪论中也说过,数据结构就是对数据增删查代码。
- 增加一个元素
bool ListInsert(SqList &L,int i,ElemType e){
if(i<1 || L.length+1) //判断i的范围是否有效
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++; //表长+1
return true;
}
- 删除一个元素
bool ListDelete(SqList &L,int i,ElemType &e){
if(i<1 || i>L.length) //判断i是否有效
return false;
e =L.data[i-1]; //将被删除的元素赋值给e
for(int j = i;j<L.length;j++) //将第i个元素后的元素前移
L.data[j-1] =L.data[j];
L.length--; //线性表长度 -1
return true;
}
- 按值查找
int LocateElem(SqlList L,ElemType e){
int i;
for(i = 0;i<L.length;i++)
if(L.data[i] == e)
return i+1;
return 0;
}
- 按照下标查找
return a[i]
理解:这里一定要注意,这个是找元素最快的方法,数组中,一般知道下标,就能找到这个数,i 对应 a[i]。选择题经常考。
三、线性表的链式存储结构
3.1 概念
链式,绪论也说过,它在内存中可以连续,也可以杂乱无章。适用于大量删除和插入
线性表的链式存储结构可以有好多花样 ,下面分几个考点
四、 单链表
4.1结构体
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
注意区分头指针和头结点
4.2 单链表的基本操作(要背的代码)
4.2.1 声明单链表(带头和不带头)
//带头结点
bool InitList(LinkList &L){
L =(LNode *)malloc(sizeof(LNode));
if(L == null)
return false; //申请头结点失败
L->next = null;
return true;
}
//不带头结点
bool InitList(LinkList &L){
L=null;
return true;
}
4.2.2 判断单链表是否是空(带头和不带头)
//带头结点
bool Empty(LinkList L){
if(L->next == null)
return true;
else
return false;
}
//不带头
bool Empty(LinkList L)
return L==null;
4.2.3插入元素(带头、不带头)
1、在给的在序号处插入
下面这段代码有点长,其实不难,大部分都是一样的,因为大部分都是遍历的代码找到p结点。重点在插入部分
//==不带头==
bool ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
//不带头结点,所以是专属部分
if(i==1)
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L=s; //头指针指向新的结点
return true;
LNode *p; //指针p指向当前扫描到的结点
int j = 1; //p指向的是第几个结点
P=L; //指向第1个结点
//这部分开始查找
while(p!=null &&j<i-1){
p=p->next;
j++;
}
//查找结束
if(p==>NULL)
return false;
//这里开始是重点,进行插入操作
LNode *s =(LNode *)malloc(sizeof(LNode))); //先申请一个结点
s->data = e; //申请的结点需要进行放值
s->next =p->next;
p->next =s;
return true;
//重点结束
}
//==带头结点==
bool ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的结点
int j = 0; //p指向的是第几个结点
P=L; //L指向头结点,头结点是第0个结点(不存数据)
//这部分循环查找
while(p!=null &&j<i-1){
p=p->next;
j++;
}
//查找结束
if(p==>NULL)
return false;
//与上文一样
//这里开始是重点,进行插入操作
LNode *s =(LNode *)malloc(sizeof(LNode))); //先申请一个结点
s->data = e; //申请的结点需要进行放值
s->next =p->next;
p->next =s;
return true;
//重点结束
2、 在指定元素P后面插入
这段代码是基本,且考的很多
p = GetElem(L,i-1); //获得P
s-next = p->next; //对应图中的序号1
p->next = s; //对应途中的序号2
3、在指定元素P前面插入
s->next = p->next;
p->next = s; ///新结点s连到p之后
s->data = p->data; //将品种元素复制到s中
p->data = e; //p中元素覆盖为e
理解:为什么分这么多插入,需要理解单链表的逻辑结构,因为单链表他是一条特殊的’‘铁链’‘,只能从一个根据他指的箭头找到下一个元素,只能遍历一个个找,不能回头。一条路走到黑,所以需要转变思维
4.2.4 删除元素
1、给位序删除
这部分大部分代码也是和上文一样,找到P结点,重点在删除部分
bool ListDelete(LinkList &L,int i,ElemType &e){//注意这里的e有&符号
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的结点
int j = 0; //p指向的是第几个结点
P=L; //L指向头结点,头结点是第0个结点(不存数据)
//这部分循环查找
while(p!=null &&j<i-1){
p=p->next;
j++;
}
//查找结束
if(p==>NULL)
return false;
//与上文一样
//删除的重点来了!!!
LNode *q = p->next;//令q指向被删除的结点
e = q->data; //用e返回被删除元素的值
p->next =q->next;//将*q结点从链中断开
free(q); //释放q
return true;
}
2、删除指定P之后的元素
p = GetElem(L,i-1);
q=p->next;
p->next = q->next;
free(q);
3、删除指定P之前的元素
// 方式二的代码a
LNode *q = p->next;//令q指向*p的后继结点
p->data = p->next->data; //和后继结点交换数据域
p->next =q->next;//将*q结点从链中断开
free(q); //释放q
-
按位查找
-
按值查找
-
求表长
-
头插法
-
尾插法
五、 双链表
5.1概念
上面的单链表缺点非常明显,只能往后找,不能往前找,所以就发明了双链表。就是数据前面,后面都有指针,不仅能找前,也能找后。
5.2 结构体
typedef struct DNode(
ElemType data;
struct DNode *prior,*next;
)DNode,*DLinkList;
5.3 双链表的初始化
bool InitDLinkList(DLinkList &L){
L=(DNode *)malloc(sizeof(DNode));//分配一个头结点
if(L==NULL)
return false;//内存不足分配失败
L->prior=NULL; ///主要记住这两句
L->next =NULL;
return true;
}
5.3 双链表插入(常考)
①s->next = p->next; //上图数字一部分
②p->next->prior =s;//上图数字二部分
③s->prior = p;//上图数字三部分
④p->next =s;//上图数字四部分
//数字4必须在数字1 2 之后。
5.4 双链表删除(常考)
p->next= q->next;//图中步骤1
q->next->prior = p;//图中步骤2
free(q);
六、循环单链表与循环双链表
这部分大部分需要理解一下它长什么样,知道结构体中指针往头结点指就问题不大。
6.1 循环单链表
6.2 循环双链表
常常在选择题中出现。它们的增删和前面对应单链表和循环双链表步骤完全不一样。考这个增删代码其实就是考的上面的单链表和双链表。
七、静态链表
静态链表,虽说是链表,但其实是用数组也就是顺序存储来实现的。严格上来讲属于顺序存储,但是实现了链表的功能,同样理解它的结构就好。不太会考增删。
7.1结构体
#define MaxSize 50 //静态链表最大长度
typedef struct{
ElemType data;
int next;
}SLinkList[MaxSize];
7.2 静态链表考点
这部分主要理解他的结构,然后做选择题。需要看课理解。
八、选择题考点
8.1 选择题考点
-
1.顺序存储的按照下标查找。这部分要和单链表区别。顺序存储中,我们可以通过下标,直接得到数据也就是return a[i]。其时间复杂度是O(1)
- 数组只要给下标,就能直接
return a[i]
- 单链表,只能一个个遍历查找,所以是
O(n)
- 考试会考,访问第i个元素,或者第i-1,i+1,或者访问第i个元素的前驱怎么样最快。当然是顺序存储最快,也就是数组最快,因为已经给i下标,所以只要return a[i]即可。
- 数组只要给下标,就能直接
-
2.顺序存储结构是随机存取的。
-
3.顺序表中
- 删除第i个元素需要移动n-i个元素。
- 在i位置插入一个元素,需要移动n-i+1
-
4.顺序存储最大的优势就是存储密度大。
- 多次插入删除,用单链表。因为插入删除需要移动大量元素,非常浪费时间。
- 只在最后插入删除,用顺序表。因为不需要移动元素。
8.2 顺序表和链表区别
- 存储方式:顺序表可以顺序存取,也可以随机存取,链表只能从表头开始存取元素
- 逻辑结构与物理结构:顺序结构逻辑物理位置连续。链表逻辑上相邻,物理上不一定相邻。
- 按值查找:顺序表无序,链表与顺序表复杂度均为
O(n)
,因为都要遍历。若顺序表有序,那顺序表可以进行折半查找,复杂度为O(log2n)
- 按序号查找:顺序表直接return 下标就行。为
O(1)
。链表仍要一个个找为O(n)
- 空间分配:顺序表不能扩充,链表能扩充
- 查找多用顺序表,插入删除多用链表
8.3 经常考上面的代码,插入删除,填空代码都有可能。
九、大题考点
经常在单链表插入和删除,顺序表基础上进行修改
这部分结合最后一章排序和排序考一个算法