赞
踩
线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列。
相同:各数据元素所占空间一样大。
有序序列:有先后次序
线性表和顺序表、链表关系:
线性表:是一种逻辑结构,表示元素之间一对一相邻的关系。
顺序表、链表:是指存储结构。
位序与数组下标的区别:
位序:从1开始
数据下标:从0开始
顺序表的顺序存储又称顺序表。(用顺序存储方式实现的线性表)
顺序存储:逻辑上相邻的两个元素在物理位置上也相邻(逻辑顺序与物理顺序相同)
顺序表的特点:
静态分配:
数组大小和空间事先固定。
#define MaxSize 50 //静态分配下的顺序表最大长度
typedef struct {
int data[MaxSize]; //静态数组存放元素
int lenght; //当前长度
}SqList; //顺序表定的类型定义
动态分配:
存储数组的空间是在程序执行过程中通过动态分配语句分配的,可扩展存储空间
#define InitSize 50 //动态分配下的顺序表初始长度
typedef struct {
int *data; //动态分配数组的指针
int MaxSize,lenght; //最大容量和当前长度
}SqList; //顺序表定的类型定义
初始化动态分配语句
L.data = (int*)malloc(sizeof(int) * InitSize);
malloc:申请存储空间
free: 释放存储空间
按位序插入
bool ListInsert(SqList &L,int i,int e){//i--位序,e--元素
//将e插入在第i个位置
if(i<1||i>l.length+1) return false;
if(L.length>=MaxSize) return false;
for(int j = L.length;j>=i;j--){
L.data[j] = L.data[j-1];
//最后一次循环:j==i 将data[i-1]放到data[i]
}
L.data[i-1] = e;
L.length++;
return true;
}
时间复杂度关注深层循环语句循环次数:移动元素
最好情况:
在表尾插入:(i=length+1)O(1)
最坏情况:
在表头插入: (i==1)O(n)
平均情况:
pi=1/(n+1)
移动的平均次数:n*p+(n-1)*p+(n-2)p+…+1p+0p=n/2
O(n)=2/n
按位序删除
bool ListDelete(SqList &L,int i,int &e){
//将第i个元素删除,并返回删除元素e
if(i<1||i>L.length) return false;//i范围是否有效
e = L.data[i-1];
for(int j = i;j<length;j++){//从下标为i的元素依次往前挪
data[j-1] = data[j];
}
L.length--;
return true;
}
最好情况:
在表尾删除:(i=length)O(1)
最坏情况:
在表头删除: (i==1)O(n-1)=O(n)
平均情况:
pi=1/n
移动的平均次数:(n-1)*p+(n-2)*p+(n-3)p+…+1p+0p=(n-1)/2
O(n)=(n-1)/2
查找第一个元素值等于e的元素,并返回位序
int LocateElem(SqList L,int e){//不用加&
for(int i = 0;i<L.length;i++){
if(L.data[i]==e) return i+1;
}
return 0;
}
复杂度:比较元素
最好情况:
在表头:比较一次 O(1)
最坏情况:
在表尾(不存在):比较length次 O(n)
平均情况:
pi=1/n
比较的平均次数:n*p+(n-1)*p+(n-2)*p+…+1p=(n+1)/2
O(n)=(n+1)/2
int GetElem(SqList L,int i){//不用加&
return L.data[i];
}
时间复杂度:O(1)
线性表的链式存储又称单链表 (不需要使用地址连续的存储单元)
链表节点内容:
①元素自身信息
②指向后续的指针
单链表表的特点:
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//*LinkList 指向节点的指针
扩展: typedef <数据类型><别名>
强调是单链表:LinkList
强调是节点:LNode *
(LNode * L 和 LinkList L等价,声明了指向单链表的第一个节点的指针)
判空条件:
①不带头节点:
L == Null
②带头结点:
L->next == Null
新节点插入到单链表表头
LinkList List_HeadInsert(LinkList &L){ LNode *s;int x; L = (LinkList)malloc(sizeof(LNode));//创建头节点 L->next = NULL;//避免指向脏数据 scanf("%d",&x); while(x!=9999){ s = (LinkList)malloc(sizeof(LNode)); //系统生成一个LNode的节点,并将节点起始位置赋值给s s->data = x; s->next = L->next; L->next = s; scanf("%d",&x); } return L; }
重要应用:链表的逆置
每插入一个新节点时间复杂度为:O(1)
总时间复杂度为:O(n)
新节点插入到单链表表尾
添加了一个链尾指针
LinkList List_HeadInsert(LinkList &L){ int x; LNode *s,*r=L; L = (LinkList)malloc(sizeof(LNode));//创建头节点 L->next = NULL;//避免指向脏数据 scanf("%d",&x); while(x!=9999){ s = (LinkList)malloc(sizeof(LNode)); //系统生成一个LNode的节点,并将节点起始位置赋值给s s->data = x; s->next = r->next; r->next = s; r=s;//跟随队尾移动 scanf("%d",&x); } r->next = NULL; return L; }
每插入一个新节点时间复杂度为:O(1)
总时间复杂度为:O(n)
位序查找
LNode *GetElem(LinkList L,int i){
int j = 1; // 计数,初值为1
LNode *p = L->next ;
if(i<1) return NULL;
while(p&&j<i){//从第1个节点开始找,找第i个
//结束循环是j=i
p = p->next;
j++;
}
return p;
}
按序号查找:时间平均复杂度 O(n)
LNode *LocateElem(LinkList L,int e){
//查找数据域等于e的节点并,返回该节点
LNode *p = L->next;
while(p!=NULL&&p->data!=e){//注意p判空在前
p = p->next;
}
return p;
//寻找失败返回NULL
}
按值查找:时间平均复杂度 O(n)
插入结点将值为x的新节点插入到单链表的第i个位置上。
关键key:找到第i-1个结点
p=GetElem(L,i-1);
s->next = p->next;
p->next = s;
时间开销在查找第i-1个元素,时间复杂度为O(n);
( 若在给定结点后插入元素,O(1) )
bool ListInsert(LinkList &L,int i,int e){ if(i<1) return false; LNode *p;//指向当前扫描到的结点 int j;//当前是第几个结点 p=L; while(p!=NULL&&j<i-1){//找到第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->next; return true; }
在某一结点前插入一个新的节点
s->next = p->next;
p->next = s->next;//修改指针域
temp = s->data;
s->data = p->data;
p->data = temp;//修改数据域
bool ListInsert(LinkLst &L ,int i,int e){ if(i<1) return false; if(i==1){//特殊处理 LNode *s = (LNode *)malloc(sizeof(LNode)); s->data = e; s->next = L->next; L=s; return true; } LNode p = L; int j=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; }
关键:找到第i-1个结点,既被删除结点的前驱结点
p = GetElem(L,i-1);
q = p->next;
p->next = q->next;
free(q);
时间耗费在查找上,时间复杂度:O(n)
q=p->next;
p->data =q-data;
p->next = q->next;
free(q);
时间复杂度:O(1)
时间复杂度:O(n)
int getSize(LinkList &L){
LinkList p=L;
int j=0;
while(p->next!=NULL){
j++;
p=p->next;
}
return j;
}
typedef struct DNode{
int data;
struct LNode *prior,*next;
}DNode,*DLinkList;
其按值查找和按位查找与单链表一致
只能遍历实现;O(n)
在p结点之后插入*s
s->next = p->next;
if(p->next!=NULL)p->next->piror = s;//若p为最后一个结点跳过
p->next = s;
s->piror = p;
在p结点之前插入*s
s->next = p;
s->piror = p->piror;
if(p->piror!=NULL)p->piror->next = s;
p->piror = s;
删除结点*p的后续结点 *q
p->next = q->next;
if(q->next!=NULL)q->next->prior = p;
free(q);
删除结点*p的前驱结点 *q
p->piror = q->piror;
if(q->prior!=NULL)q->piror->next = p;
free(q);
定义:
- 借组数组来描述线性表的链式存储结构。
- 结点有data数据域和next指针域(结点的相对地址-数组下标(游标))。
- 与顺序表一样,需要事先分配一块连续的内存空间
0号结点充当头指针,-1表示表尾,游标充当指针。
不支持随机存取,容量固定不变
扩展:
应用:放置在索引块的FAT(文件配置表)。
FAT支持随机访问(指不用依次访存),文件扩展也易实现。
#define MaxSize 50
typedef struct{
int data;
int next;
}SLinkList[MaxSise];//可以用SLinkList来定义一个长为maxSize的数组
//struct Node a[maxSize] ~SLinkList a;
都是线性结构
存取方式
- 顺序表可以顺序存取和随机存取~O(1)
- 链表只能从头顺序存取~O(n)
结构
- 顺序表:
逻辑上相邻,物理存储位置上也相邻。(大片连续空间,存储密度高)- 链表:
逻辑上相邻,物理存储位置不一定相邻。(离散小空间)操作
- 按值查找和,顺序表无序:O(1);有序时(折半):O(log2n)
- 顺序表插入和删除,平均移动半个表长的元素(移动元素):O(n)。
链表的插入删除、删除修改相关指针域即可(查找目标元素):O(n)空间分配
- 顺序表:
需预先分配足够大的存储空间。扩充效率低。- 链式节点空间只在需要时申请分配。
– | 顺序表 | 链表 |
---|---|---|
可扩容 | — | ✔ |
增删 | — | ✔ |
查 | ✔ | — |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。