1.绪论
1.1复杂度
1.1.1大O复杂度表示法
T(n)= O( f(n) )
T(n):代码执行时间c
f(n):每行代码执行次数总和
O代表T(n)和f(n)成正比
1.1.2 复杂度分析法则
1)单段代码看高频:比如循环。
2)多段代码取最大:比如一段代码中有单循环和多重循环,那么取多重循环的复杂度。
3)嵌套代码求乘积:比如递归、多重循环等
4)多个规模求加法:比如方法有两个参数控制两个循环的次数,那么这时就取二者复杂度相加。
1.1.3 时间复杂度分析
1.只关注循环执行次数最多的一段代码
2.加法法则:总复杂度等于量级最大的那段代码的复杂度
3.乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
1.2抽象数据类型表示与实现
1.2.1预定义常量和类型
//函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status 是函数类型,其值是函数结果状态代码
typedef int Status;
1.2.2表示
数据结构表示用类型定义(typedef)描述,数据元素类型约定为ElemType,用户自行定义
1.2.3函数描述
函数类型 函数名 (函数参数表){
//算法说明
语句序列
//函数名
}
辅助变量可不做变量说明
数据元素名:abcde
整型变量名:ijklmn
指针变量名:pqr
当函数返回值为函数结果状态代码时,函数为Status类型
形参表中,以&打头的参数即为引用参数
1.2.4 赋值语句
简单赋值:变量名=表达式;
串联赋值:变量名1=变量名2=……=变量名k=表达式;
成组赋值:(变量名1,……,变量名k)=(表达式1,……,表达式k);
结构名=结构名;
结构名=(值1,……,值k);
变量名[]=表达式;
变量名[起始下标..终止下标]=变量名[起始下标..终止下标];
交换赋值:变量名<-->变量名
条件赋值:变量名=条件表达式?表达式T:表达式F;
1.2.5选择语句
条件语句1 if(表达式)语句;
条件语句2 if(表达式)语句;
else语句;
开关语句1
switch(表达式){
case 值1 :语句序列1;break;
...
case 值n :语句序列n;break;
default:语句序列n+1
}
开关语句2
switch{
case 值1 :语句序列1;break;
...
case 值n :语句序列n;break;
default:语句序列n+1
}
1.2.6循环语句
for语句 for(赋初值表达式序列;条件;修改表达式序列)语句
while语句 while(条件)语句;
do-while语句 do{
语句序列;
}while(条件)
1.2.7结束语句
函数结束语句:return表达式
return
case结束语句:break
异常结束语句:exit(异常代码)
1.2.8输入和输出语句
输入语句 scanf([格式串],变量1,...,变量n)
输出语句 printf([格式串],表达式1,...,表达式n)
通常省略格式串
1.2.9注释
单行注释//文字序列
1.2.10基本函数
最大值 max(表达式1,...,表达式n)
最小值 min(表达式1,...,表达式n)
绝对值 abs(表达式)
不足整数值 floor(表达式)
进位整数值 ceil(表达式)
判定文件结束 eof(文件变量)或eof
判定行结束 eoln(文件变量)或eoln
1.2.11逻辑运算约定
与运算 &&:对于A&&B,当A值为0时,不再对B求值
或运算 ||:对于A||B,当A值为非0时,不再对B求值
2.线性表
2.1线性表类型定义
一个线性表是n个元素的有限序列
抽象数据类型线性表定义
ADT{
数据对象:D{ai|ai∈ElemSet,i=1,2,...,n,n>=0}
数据关系:R1{<ai-1,ai>ai-1,ai∈D,i=1,2,...,n}
基本操作:
InitList(&L)//构造空线性表L
DestoryList(&L)
ClearList(&L)
ListEmpty(L)
ListLength(L)//返回L中数据元素个数
GetElem(L,i,&e)//用e返回L中第i个数据元素的值
LocateElem(L,e,compare())
PriorElem(L,cur_e,&pre_e)
NextElem(L,cur_e,&Next_e)
ListInsert(&L,i,e)//在L中第i个位置之前插入新数据元素e,L的长度加一
ListDelete(&L,i,&e)
ListTraverse(L,visit())
}ADT List
算法2.1
void MergeList(List La, List Lb, List &Lc){
//已知像线性表La和Lb中数据元素按值非递减排列
//归并La和Lb得到新的线性表Lc,Lc数据元素也按值非递减排列
InitList(Lc);
i=j=1;k=0;
La_len=Listlength(La);Lb_len=Listlength(Lb);
while((i<=La_len)&&(j<=Lb_len)){
GetElem(La,i,ai);//为什么这里ai之前不加&呢
GetElem(Lb,j,bj);
if(ai<=bj){ListInsert(Lc,++k,ai);++i;}
else{ListInsert(Lc,++k,bj);++j;}
}
while(i<=La_len){
GetElem(La,i++,ai);ListInsert(Lc,++k,ai);
}
while(j<=Lb_len){
GetElem(Lb,j++,bj);ListInsert(Lc,++k,bj);
}
}//MergeList
2.2线性表顺序表示和实现(顺序存储结构)
线性表顺序存储结构示意图
存储地址 内存状态 位序
b a1 1 |
b+l a2 2 |
... |
b+(i-1)l ai i |
... |
b+(n-1)l an n |
b+nl |
b+(maxlen-1)l |
//--------线性表的动态分配顺序存储结构--------
#define LIST_INIT_SIZE 100//线性表存储空间初始分配量
#define LISTINCREMENT 10//线性表存储空间分配增量
typedef struct{
ElemType *elem;//存储空间基址
int length;//当前长度
int listsize;//当前分配的存储容量
}Sqlist;//定义一个结构体
当插入元素而空间不足式,进行再分配,为顺序表增加一个大小为LISTINCREMENT个数据元素的空间
Status InitList_Sq(SqList &L){
//构造一个空线性表L
L.elem = (ElemType * )malloc(LIST_INIT_SIZE * sizeof(ElemType));
if(! L.elem) exit(OVERFLOW);//存储分配失败
L.length = 0;//空表长度为0
L.listsize = LIST_INIT_SIZE;//初始存储容量
return OK;
}//InitList.Sq
ElemType:元素数据类型
INIT_LIST_TYPE:线性表初始大小
算法2.3(插入和删除操作)
Status ListInsert_Sq(SqList &L, int i, ElemType e){
//在顺序线性表L中第i个位置之前插入新的元素e
//i的合法值为1<=i<=ListLength_ Sq(L)+1
if(i < l || i > L.length + 1)return ERROR;//i值不合法
if(L.length>=L.listsize){ //当前存储空间已满,增加分配
newbase = (ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(! newbase)exit(OVERFLOW); //存储分配失败
L.elem = newbase; //新基址
L.listsize += LISTINCREMENT; //增加存储容量
}
q=&(L.elem[i-1]); //q为插入位置
for(p=&(L.elem[L.length-1]);p>=q;--p) *(p+1)=*p; //插入位置之后元素右移
*q = e; //插入e
++L.length; //表长增1
return OK;
}// ListInsert_Sq
读懂算法 2.3
1. ListInsert_Sq是一个函数,用于向顺序表中插入元素.
2.其中,SqList是一个结构体类型,表示顺序表;L是指向顺序表的引用;i表示要插入的位置;e表示要插入的元素;Status是一个枚举类型,表示函数执行的状态,通常为成功或失败。
通常情况下,SqList结构体包含以下几个属性:
- ElemType data[MAXSIZE]:用来存储顺序表中的元素,其中ElemType是元素的数据类型,MAXSIZE是顺序表的最大容量。
- int length:表示当前顺序表中元素的个数。
- 其他可能需要的属性,如最大容量MAXSIZE等。
在算法中,SqList结构体通常通过引用传递给函数,以便在函数中对顺序表进行操作。通过SqList结构体,可以方便地表示和操作顺序表中的元素。
3.newbase = (ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
这行代码是使用C语言中的realloc函数来重新分配顺序表L的存储空间。让我们逐步解释这行代码:
1. `(L.listsize+LISTINCREMENT)*sizeof(ElemType)`: 这部分计算了需要重新分配的内存空间的大小。`L.listsize`表示当前顺序表L的总容量,`LISTINCREMENT`表示要增加的容量大小,`sizeof(ElemType)`表示每个元素的大小。通过这个计算,得到了新的总容量。
2. `realloc(L.elem, ...)`: realloc函数用于重新分配之前由malloc、calloc或realloc分配的内存块的大小。在这里,`L.elem`是指向之前分配的内存块的指针,`newbase`是一个新的指针,用于指向重新分配后的内存块。
3. `newbase = ...`: 这部分将重新分配后的内存块的地址赋给`newbase`,以便后续使用。
综合起来,这行代码的作用是对顺序表L的存储空间进行扩容,将原有的数据复制到新的更大的内存空间中,并更新`L.elem`指针指向新的内存空间。这样可以避免顺序表容量不足时的内存溢出问题,提高了顺序表的灵活性和性能。
算法2.4(删除)
Status ListDelete_Sq(SqList &L, int i, ElemType &e){
//在顺序线性表L中删除第i个元素并用e返回其值
//i的合法值为1<=i<=ListLength_Sq(L)
if((i<1)||(i>L.length))return ERROR;//i值不合法
p = &(L.elem[i-1]);//p为被删除元素的位置
e = *p;//被删除元素的值赋给e
q = L.elem + L.length-1;//表尾元素的位置
for(++p;p<=q;++p)*(p-1)=*p;//被删除之后的元素左移
--L.length;//表长减一
return Ok;
}//ListDelete_Sq
读懂算法4.2
-
*(p-1)=*p
: 这行代码的作用是将指针p指向的元素的值赋给p指向的前一个位置的元素。*(p-1)
表示指针p的前一个位置的元素,*p
表示指针p当前指向的元素。
算法2.6(元素赋值)
2.3线性表链式表示和实现(链式存储结构)
2.3.1线性链表
链式存储结构特点是:用一组任意存储单元存储线性表的数据元素
存储信息+指示其直接后继 -> 结点(node)
数据域 指针域(指针或链)
n个结点链接成一个链表,由于每个结点只包含一个指针域,固又称线性链表或单链表
链表的存取必须从头指针开始,最后一个结点为“空”(NULL)
//----------线性表的单链表存储结构---------
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode. *LinkList;
GetElem在单链表中的实现
Status GetElem_L(LinkList L,int i, ElemType &e){
//L为带头结点的单链表的头指针
//当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
p = L->next;j = 1; //初始化p,p指向第一个结点,j为计数器
while(p&&j<i){ //顺指针向后查找,直到p指向第i个元素或p为空
p = p->next; ++j;
}
if(!p||j>i)return ERROR; //第i个元素不存在
e=p->data; //取第i个元素
return OK;
}//GetElem_L
while(p&&j<i){ //顺指针向后查找,直到p指向第i个元素或p为空
p = p->next; ++j;
}
这段代码是一个while循环,它的作用是让指针p沿着链表的next指针向后移动,直到满足循环条件结束。让我们逐步解释这段代码:
-
while(p&&j<i)
: 这是一个while循环的条件判断语句,循环会在满足条件p
非空且j
小于i
时执行。条件p
非空表示指针p指向的节点存在,条件j
小于i
表示循环次数未达到要求。 -
p = p->next;
: 这行代码让指针p指向当前节点的下一个节点,即沿着链表的next指针向后移动一个节点。-
++j;
: 这是对循环计数器j
进行自增操作,用于记录循环的次数
-
算法2.8 2.9(单链表中实现插入和删除操作)
Status ListInsert_L(LinkList &L,int i,ELemType e){
//在带头结点的单链线性表中的第i个位置前插入元素e
p=L;j=0;
while(p&&j<i-1){p=p->next;++j}
if(!p||j>i-1)return ERROR;
s=(LinkList)malloc(sizeof(LNode));//生成新结点
s->data=e;s->next=p->next; //插入L中
p->next=s;
return OK;
}//ListInsert_L
在C语言中,`->` 是一个操作符,用于访问结构体或联合体变量的成员。它是对`.`操作符的一种扩展,用于指向结构体或联合体的指针变量。通过`->`操作符,我们可以通过指针访问结构体或联合体变量中的成员。例如,如果有一个结构体指针变量p,我们可以使用`p->member`来访问结构体中的成员变量member。
Status ListDelete_L(LinkList &L,int i,ElemType &e){
//在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
p=L;j=0;
while(P->next&&j<i-1){ //寻找第i个结点,并令p指向其前驱
p=p->next;++j;
}
if(!(p->next)||j>i-i) return ERROR;//删除位置不合理
q=p->next; p->next=q->next; //删除并释放结点
e=q->data; free(q);
return OK;
}//ListDelete_L
算法2.10(逆序位建立单链表)
算法2.11(将两个有序链表合并为一个有序链表)
算法2.12(静态单链表存储结构)
//-------------线性表静态单链表存储结构------------
#define MAXSIZE 1 000
typedef struct{
ElemType data;
int cur;
}component,SlinkList[MAXSIZE];
2.3.2循环链表
2.3.3双向链表
2.4一元多项式的表示及相加
3.栈和队列
(未完待续 )