DataStructure
by MarkfuGod
Contiguous List(linear Construct)
线性关系:结构中数据元素之间存在着一对一的关系 用指针来实现链式的存储结构(既有数值的内容,也有下一个数据的地址) 线性关系:结构中数据元素之间存在着一对一的关系\\ 用指针来实现链式的存储结构(既有数值的内容,也有下一个数据的地址)\\ 线性关系:结构中数据元素之间存在着一对一的关系用指针来实现链式的存储结构(既有数值的内容,也有下一个数据的地址)
抽象数据类型 ( A D T ) : A b s t r a c t _ D a t a _ T y p e 抽象数据类型(ADT):Abstract\_Data\_Type 抽象数据类型(ADT):Abstract_Data_Type
ADT List{
数据对象:D = {a_i|a_i \in Elemset, i = 1,2,3...,n};
数据关系:R = {<a_i-1,a_i>| a_i-1,a_i \in D(i = 2,3,...,n)}
基本操作:
InitList(&L);
DestroyList(&L);
ListInsert(&L,i,e);//i在1到i+1的位置,在i之前插入新的数据元素e,L长度加一
ListDelete(&L,i,&e);//i在1到n的位置,删除第i个数据元素,并e返回其值,L长度-1
ListTraverse(&L,visited());//对线性表中每个元素调用visited();
ListEmpty(L);
ListLength(L);
GetElem(L,i,&e);//用e返回L中第i个数据元素的值
LocateElem(L,e,compare());//返回L中第一个与e满足compare()的数据元素的位置序号
PriorElem(L,cur_e,&pre_e);//cur就是current,pre_e返回的是他的前驱(注意cur不是一第一个,否则失败)
NextElem(L,cur_e, &next_e);
... etc//FIXME: 等待补充;
}ADT List
Initialization
L o c ( a i + 1 ) = L o c ( a i ) + l 常用: L o c ( a i ) = L o c ( a 1 ) + ( i − 1 ) × l 优点:找地址十分方便,只需进行一次运算,时间复杂度为 O ( 1 ) ; 任意元素可以随机存取 , 地址连续,依次存放,类型相同 : : 数组 线性表表长可变,而数组长度不可以动态定 Loc(a_{i+1}) = Loc(a_i) + l\\ 常用:Loc(a_i)=Loc(a_1)+(i-1)\times l \\优点:找地址十分方便,只需进行一次运算,时间复杂度为O(1); \\任意元素可以随机存取,地址连续,依次存放,类型相同::数组 \\线性表表长可变,而数组长度不可以动态定 Loc(ai+1)=Loc(ai)+l常用:Loc(ai)=Loc(a1)+(i−1)×l优点:找地址十分方便,只需进行一次运算,时间复杂度为O(1);任意元素可以随机存取,地址连续,依次存放,类型相同::数组线性表表长可变,而数组长度不可以动态定
#define List_Init_Size 100
typedef struct{
ElemType elem[List_Init_Size];
int length;
}SqList;
类c语言操作的补充
typedef struct{
ELemType data[];//ELemType:可以是char,float
//typedef chae Elemtype
int length;
}
typedef struct {
ElemType *data;//想想上学期讲指针的那时候,可以用data存储数据的首地址,之后开辟空间
int length;
}SqList;//不要忘了加载 stdlib.h!
/*---------------------------------------------------------*/
#c++的动态存储分配
new 类型名T(初值列表) int *p1 = new int; or int *p1 = new int(10);
成功:T类型的指针,指向新分配的内存;
失败:NULL;
delete 指针P delete p1;
释放P所指向的内存,P必须是new操作的返回值;
#c++参数传递
&:取地址, *:看地址
引用类型作为参数:给一个对象提供替代的名字
/*--------如下---------------*/
int i = 5;
int &j = i;\\相当于直接对同个地址所在的值进行操作,j相当于也指向i的地址
i = 7;
cout << i << j ; \\ 会输出i=7,j=7;
/***********************************/
int main(){
int a, b;
swap_t(a,b);
cout << a << b;
}
int swap_t(int& a, int& b) {
int t;
t = a;
a = b;
b = t;
}
说明
形参变化,实参也发生变化
直接对实参进行了操作,不会产生副本
指针相比之下就比较复杂了
形参变化,实参也发生变化 \\直接对实参进行了操作,不会产生副本 \\指针相比之下就比较复杂了
形参变化,实参也发生变化直接对实参进行了操作,不会产生副本指针相比之下就比较复杂了
Initialization_2
Initlist
Status InitList_Sq(SqList &L){
L.elem = new ElemType[MAXSIZE]; //分配空间
if(!L.elem) exit(OVERFLOW);//分配失败,L.elem是0,OVERFLOW是-2,返回时就可以判别失败类型;
L.length = 0;
return OK;
}
DestroyList
void DestroyList(SqList &L){
if(L.elem) delete L.elem; //释放内存
}
ClearList
void ClearList(SqList &L){
L.length = 0; //内存还在,但是长度是0了,即元素个数是0
}
GetLength
int GetLength(SqList L){
return(L.length);
}
IsEmpty
int IsEmpty(SqList L){
if(L.length == 0) return 1;
else return 0;
}
Initialization_2
GetElem
int GetElem(SqList L, int i, ElemType &e)
{
if(i<1 || i > L.length) return ERROR;
e = L.elem[i-1];//NOTION:第i-1的位置存储第i个数据
return OK;
}
C o m p l e x i t y : O ( 1 ) Complexity:O(1) Complexity:O(1)
LocateElem
int LocateElem_1(SqList L, ElemType e){
for(int i = 0; i < L.length; i ++)
if(L.elem[i] == e) return(i + 1);//查找成功,返回元素序号位置
return 0; // 查找失败,返回 0
}// O(n)
// ASL : 平均查找长度
// 期望值
平均的查找长度和查找的时间复杂度
A
S
L
=
Σ
i
=
1
n
P
i
C
i
设
P
i
=
1
n
即每次查找的概率相同
A
S
L
S
S
=
Σ
i
=
1
n
P
i
C
i
=
1
n
Σ
i
=
1
n
i
=
1
+
n
2
复杂度为
O
(
n
)
ASL = \Sigma_{i=1}^n P_i C_i\\ 设P_i = \frac{1}{n} 即每次查找的概率相同 \\ASL_{SS} = \Sigma_{i=1}^n P_i C_i=\frac{1}{n}\Sigma_{i=1}^n i=\frac{1+n}{2} \\复杂度为O(n)
ASL=Σi=1nPiCi设Pi=n1即每次查找的概率相同ASLSS=Σi=1nPiCi=n1Σi=1ni=21+n复杂度为O(n)
InsertList
int InsertList(SqList &L, int i, ElemType e){
if(i < 1 || i > L.length + 1) return ERROR:
if(L.length == MAXSIZE) return error;
for(int j = L.length - 1; j >= i; j --){
L.elem[j + 1] = L.elem[j]; //所有成员向后移
}
L.length ++;
return OK;
}
时间复杂度分析 : i + x = n + 1 ( 插入位置和移动次数之和 = n + 1 ) E i n s = 1 n + 1 Σ i = 1 n + 1 ( n − i + 1 ) = 1 n + 1 ( n + ⋯ + 1 + 0 ) = 1 n + 1 n ( n + 1 ) 2 = n 2 即复杂度为 O ( n ) 时间复杂度分析: \\i + x = n+1(插入位置和移动次数之和 = n+1) \\ E_{ins} = \frac{1}{n+1}\Sigma_{i=1}^{n+1}(n-i+1)= \frac{1}{n+1}(n+\cdots+1+0)\\ =\frac{1}{n+1}\frac{n(n+1)}{2} = \frac{n}{2}\\ 即复杂度为O(n) 时间复杂度分析:i+x=n+1(插入位置和移动次数之和=n+1)Eins=n+11Σi=1n+1(n−i+1)=n+11(n+⋯+1+0)=n+112n(n+1)=2n即复杂度为O(n)
ListDelete
int ListDelete_Sq(SqList &L, int i){
if(i < 1 || i > L.length)
return ERROR;
for(int j = i; j <= L.length - 1; j ++)
L.elem[j - 1] = L.elem[j];
L.length --;
return OK;
}
时间复杂度分析: E d e l = 1 n Σ i = 1 n ( n − i ) = 1 n ( n − 1 ) n 2 = n − 1 2 O ( n ) 时间复杂度分析: \\E_{del} = \frac{1}{n}\Sigma_{i=1}^{n}(n-i)=\frac{1}{n}\frac{(n-1)n}{2}=\frac{n-1}{2} \\ O(n) 时间复杂度分析:Edel=n1Σi=1n(n−i)=n12(n−1)n=2n−1O(n)
总结
⋅ 优点: 存储密度大 可以随机存取列表中任意一个元素 ⋅ 缺点: 在插入,删除某一元素时,需要移动大量元素 浪费存储空间 静态的存储形式,数据元素的个数不能自由扩充 ·优点: \\存储密度大 \\可以随机存取列表中任意一个元素 \\·缺点: \\在插入,删除某一元素时,需要移动大量元素 \\浪费存储空间 \\静态的存储形式,数据元素的个数不能自由扩充 ⋅优点:存储密度大可以随机存取列表中任意一个元素⋅缺点:在插入,删除某一元素时,需要移动大量元素浪费存储空间静态的存储形式,数据元素的个数不能自由扩充
List(Linear)
Singly Linked List
I.Definition
typedef struct Lnode{
ElemType data;
struct Lnode *next; //嵌套定义
}Lnode, *Linklist;
定义链表: L i n k l i s t L ; 定义节点指针 P : L N o d e ∗ P ⇔ L i n k L i s t p (但一般用前者) 定义链表: \\Linklist\quad L; \\定义节点指针P: \\LNode *P \Leftrightarrow LinkList \quad p(但一般用前者) 定义链表:LinklistL;定义节点指针P:LNode∗P⇔LinkListp(但一般用前者)
Initialization
Status InitList(Linklist &L){
L = (Linklist) malloc(sizeof(Lnode));
L -> next = NULL;
return OK;
II.I 判断头节点指针域是否为空
int ListEmpty(Linklist L){
if(L -> next) // no empty
return 0;
else return 1;
}
DestroyList_L
Status DestroyList_L(Linklist &L)
{
Lnode *p;
while(L){
p = L;
L = L -> next;
free(p);
}
return OK;
}
ClearList
Status ClearList(Linklist &L){
Lnode *p,*q;
p = L -> next;
while(p){
q = p -> next;
free(p);
p = q;
}
L->next = nullptr;
return OK;
}
Listlength
Status ListLength(Linklist L){
Linklist p;
p = L -> next;
int i = 0;
while(p){
i ++;
p = p -> next;
}
return(i);
}
GetElem_L
Status GetElem_L(Linklist L,int i,ElemType &e){
Lnode *p = L -> next;
int j = 1;
while(p && j < i){
p = p -> next;
++j;
}
if(!p||j>i)return ERROR;
e = p->data;
return OK;
}
LocateElem_L
Status LocateElem_L(Linklist L, ElemType e){
Lnode *p = L -> next;
int j = 1;
while(p && p -> data.score != e.score){
p = p -> next;
j++;
}
if(p) return j;
else return 0;
}
LinkInsert_L
Status LinkInsert_L(Linklist &L, int i, ElemType1 e){
Lnode *p = L;
int j = 0;
while(p && j < i -1){
p = p -> next;
++j;
}
if(!p||j>i-1)
return ERROR;
Linklist s = (Linklist) malloc(sizeof(Lnode)); s -> data = e;
s -> next = p -> next; p -> next = s; //插入新节点
return OK;
}
CreateList_H
void CreateList_H(Linklist &L, int n){
L = (Linklist) malloc(sizeof(Lnode));
L ->next = nullptr;
for(int i = n; i > 0; i --){
Lnode *p = (Linklist) malloc(sizeof(Lnode));
scanf("%d",&p->data.score);
p->next = L->next;
L->next = p;
}
}//头插法,O(n)
void CreateList_R(Linklist &L, int n){ // Rear
L = (Linklist) malloc(sizeof (Lnode));
Lnode *rear = L;
for(int i = 0; i < n; i ++){
Lnode *p = (Linklist) malloc(sizeof(Lnode));
p -> next = NULL;
rear -> next = p;
rear = p;
}
}//尾插法
循环链表的合并
Linklist Connect(Linklist Ta, Linklist Tb){
Lnode *p = Ta -> next;
Ta -> next = Tb -> next -> next;//Tb的表头链接Ta的表尾
free(Tb -> next);// 释放Tb表头节点
return Tb;
}
双向列表的定义
typedef struct DuLNode {
ElemType data;
struct DuLNode *prior, *next;
}DuLNode,*DuLinkList;
//空的双向链表是只有头节点的
双向列表的插入
Status ListInsert_DuL(DuLinkList &L, int i, ElemType e){
DuLNode *p = (DuLinkList) malloc(sizeof (DuLNode));;
if(!(p = GetElem_Dul(L,i))) return ERROR; //FIXME:前面应该加一个GetElem(L,i)的定义
DuLNode *s = (DuLinkList) malloc(sizeof (DuLNode));
s -> data = e;
s -> prior = p -> prior;
p -> prior -> next = s;
s -> next = p;
p -> prior = s;
return OK;
}
//O(n);
双向列表的删除
Status ListDelete_DuL(DuLinkList &L, int i, ElemType &e){
DuLNode *p = (DuLinkList) malloc(sizeof (DuLNode));
if(!(p = GetElem_Dul(L,i))) return ERROR; //FIXME:前面应该加一个GetElem(L,i)的定义
DuLNode *s = (DuLinkList) malloc(sizeof (DuLNode));
e = p -> data;
p -> prior -> next = p -> next;
p -> next -> prior = p -> prior;
free(p);
return OK;
}
//O(n)
线性表的合并操作
void Union(Linklist &La, Linklist &Lb){
int La_len = ListLength(La);
int Lb_len = ListLength(Lb);
for(int i = 1; i <= Lb; i ++){
GetElem_L(Lb,i,e);
if(!LocateElem_L(La,e)) LinkInsert_L(&La,++La_Len,e);
}
}
有序表的合并
void MergeList_Sq(SqList LA, SqList LB, SqList &LC){
ElemType *pa,*pb,*pc;
pa = LA.elem;
pb = LB.elem;
LC.length = LA.length + LB.length;
LC.elem = new ElemType[LC.length];
pc = LC.elem;
ElemType *pa_last, *pb_last;
pa_last = LA.elem + LA.length - 1;
pb_last = LB.elem + LB.length - 1;
while(pa <= pa_last && pb <= pb_last){
if(*pa <= *pb) *pc ++ = *pa ++;
else *pc ++ = *pb ++;
}
while (pa <= pa_last) *pc ++ = *pa ++;
while (pb <= pb_last) *pc ++ = *pb ++;
}//MergeList_Sq;
//链表:
void MergeList_L(Linklist &a, Linklist &b, Linklist &c){
Lnode *pa,*pb,*pc;
pa = a -> next; pb = b -> next;
pc = c = a;
while(pa && pb){
if(pa -> data <= pb -> data){
pc -> next = pa;
pc = pa;
pa = pa -> next;
}
else{
pc -> next = pb;
pc = pb;
pb = pb -> next;
}
}
pc -> next = pa ? pa : pb;
delete b;
}