数据结构与算法学习笔记

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结构体包含以下几个属性:

  1. ElemType data[MAXSIZE]:用来存储顺序表中的元素,其中ElemType是元素的数据类型,MAXSIZE是顺序表的最大容量。
  2. int length:表示当前顺序表中元素的个数。
  3. 其他可能需要的属性,如最大容量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 

  1. *(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指针向后移动,直到满足循环条件结束。让我们逐步解释这段代码:

  1. while(p&&j<i): 这是一个while循环的条件判断语句,循环会在满足条件p非空j小于i时执行。条件p非空表示指针p指向的节点存在,条件j小于i表示循环次数未达到要求。

  2. p = p->next;: 这行代码让指针p指向当前节点的下一个节点,即沿着链表的next指针向后移动一个节点。

    1. ++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.栈和队列 

(未完待续 )

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值