顺序表
特点:用一段地址连续的存储空间来依次顺序存放线性表中的各元素。是一种随机存储结构。
定义
typedef struct{
datatype data[MAXSIZE];
int len;
} SeqList;
- SeqList L:定义一个顺序表L
- SeqList *L:定义一个指向SeqList 类型的指针
基本运算
1. 初始化 Init_SeqList
SeqList *Init_SeqList(){
SeqList *L;
L = malloc(sizeof(SeqList));
L->len = -1;
return;
}
2. 插入运算Insert_SeqList
实现步骤
(1)将an~ai == 顺序向后==移动一个位置,即an移动到an+1,……,ai移动到ai+1,为待插入的新元素让出位置i;
(2) 将 e 放到空出的第i个位置;
(3) 修改 len 指针,使之恒指向当前表中最后一个元素。
int Insert_SeqList(SeqList *L,int i,datatype e){
int j;
if (L->len ==MAXSIZE-1){
printf(“表满溢出”);
return -1;
}
if (i<1 || i>L->len +2){ /*检查插入位置i是否有效*/
printf("位置错");
return 0;
}
for( j=L->len ; j>=i-1 ; j--)
L->data[j+1]=L->data[j]; /* 结点往后移动一个位置 */
L->data[i-1]=e; /*新元素e插入*/
L->len ++; /*len仍指向最后元素*/
return 1; /*插入成功,返回*/
}
时间复杂度:O(n)
- 平均移动数据元素的次数(加权平均数):
Ein = ∑ i = 1 n + 1 \sum_{i=1}^{n+1} ∑i=1n+1 pi (n - i + 1) - 设为等概率情况,即pi = 1/(n+1),则:
Ein = ∑ i = 1 n + 1 \sum_{i=1}^{n+1} ∑i=1n+1 pi (n - i + 1) = 1 n + 1 \frac{1}{n+1} n+11 ∑ i = 1 n + 1 \sum_{i=1}^{n+1} ∑i=1n+1(n - i + 1) = n 2 \frac{n}{2} 2n - 平均需要挪动一半的元素。
3. 删除运算Delete_SeqList
4. 按值查找Locate_SeqList
单链表
特点:通过一组任意的存储单元来存储线性表中的数据元素,“链”保存的是其直接后继所在存储单元的起始地址。是一种顺序存储结构。
定义
typedef struct node{
datatype data;
struct node *next;
} LNode, *LinkList;
- LNode:结点类型
- LinkList:指向LNode类型结点的指针类型
基本运算
1. 建立
(1)逆序建立单链表(头插法)
- 设一个指针 L 始终指向单链表中的当前首结点(每插入一个结点,指针L向前移动一位,使其一直在首部)。
LinkList Creath_LinkList (){
LinkList L = NULL; /*空表,不指向任何地址*/
LNode *s;
int e; /*设数据元素的类型为int*/
scanf("%d", &e);
while (e!=flag){ /*flag为设置的线性表数据元素结束标志*/
s = malloc(sizeof(LNode)); /*malloc()返回被分配内存的指针*/
s->data = e;
s->next = L;
L = s;
scanf ("%d",&e);
}
return L;
}
(2)顺序建立单链表(尾插法)
- 设一个指针 r 始终指向单链表中的当前尾结点。
LinkList Creatr_LinkList (){
LinkList L = NULL;
LNode *s, *r = NULL;
int e; /*设数据元素的类型为int*/
scanf("%d",&e);
while (e!=flag){ /*flag为设置的线性表数据元素结束标志*/
s = malloc(sizeof(LNode));
s->data = e;
if(L==NULL) L = s; /*插入的结点是第一个结点*/
else r->next = s; /*插入的结点是其它结点*/
r=s; /*r 恒指向新的尾结点*/
scanf("%d",&e);
}
if( r!=NULL) r->next = NULL; /*对于非空表,表尾结点的指针域置为空指针*/
return L;
}
2. 求表长
- 方法:设一个指针变量和计数器,初始化后,如果该指针变量所指结点后面还有结点,该指针变量向后移动,计数器同时加1,直至该指针变量指向表尾。
///带头结点的单链表
int Length_LinkList1 (LinkList L)
{ LNode * p=L; /* p指向头结点*/
int i=0; /* i是计数器*/
while (p->next)
{p=p->next; i++ } /* p所指的正是第 i 个结点*/
return i;
}
///不带头结点的单链表
int Length_LinkList2 (LinkList L)
{ LNode * p=L;
int i; /* i是计数器*/
if (p==NULL) return 0; /*空表的情况*/
i=1; /*在非空表的情况下,p所指的是第一个结点*/
while (p->next )
{ p=p->next; i++ }
return i;
}
3. 查找
4. 插入
(1)在某结点之后插入 O(1)
s->next = p->next;
p->next = s;
(2)在某节点之前插入 O(n)
//设单链表头指针为L
q = L;
while(q->next!=p) q = q->next; /*找*p的直接前驱*/
s->next = q->next;
q->next = s;
总结
- 若想在单链表上插入、删除一个结点,必须知道其前驱结点。
- 不能按序号随机访问,只能从头结点开始顺着指针域顺序访问,该存取结构称为顺序存取结构。
- 如何选择存储结构? 顺序表(频繁查找);链表(频繁插入和删除)。
循环链表
只需要将原来寻找表尾时判断指针是否为NULL改为判断指针是否指向头结点即可。
双向链表
单链表中寻找后继结点的时间复杂度是O(1),寻找前驱结点的时间复杂度是O(n)。
若为每个结点再增加一个指向前驱的指针域prior,寻找前驱结点的时间性能也能达到O(1) 。
定义
typedef struct dunode{
datatype data;
struct dunode *prior,*next;
}DulNode, *DulLinkList;
//特征:q->prior->next = q = q->next->prior
插入
///关键:要保证链表不能断
///已知的地址:s、q、q->prior
s->prior=q->prior;
q->prior->next=s;
s->next=q;
q->prior=s;
删除
q->prior->next=q->next;
q->next->prior=q->prior;
free(q);