IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构笔记 -> 正文阅读

[数据结构与算法]数据结构笔记

(持续更新)

第一绪论

本章总览

绪论:数据结构的基本概念
算法
算法的时间复杂度 空间复杂度
C/C++: ()分支
数组
函数
指针、地址
Struct结构体

1.1数据结构的基本概念

1.1.1基本概念和术语

数据:数据是信息的载体,是描述事物属性的数、字符及所有能输入到计算机--------
数据元素:描述一个个体,是数据的基本单位,通常作为一个整体进行考虑和处理。
数据项:是数据元素的不可分割的最小单位。
数据对象:具有相同性质的数据元素的集合,是数据的一个子集。
数据结构:相互之间存在一种或多种特定关系的数据元素的集合。
数据类型:值的集合以及定义在此集合上的一组操作的总称。
原子类型:不可分 bool int (4B)
结构类型:其值可分,struct结构体
抽象数据类型(ADT):抽象数据组织及与之相关的操作
数据对象、数据关系、基本操作集
定义是逻辑结构;实现是物理存储。

1.1.2数据结构的三要素

1.逻辑结构:
集合 各个元素同属于一个集合,别无其他关系
线性一对一 (前驱和后继)第2、3章
树形
一对多 第4章
图(网)状*******多对多 第5章
2.数据的运算——针对某种逻辑结构,结合实际需求,定义基本算法
——实现是针对存储结构的
3.物理结构(存储结构)
顺序存储 逻辑上相邻存储物理位置也相邻
链式存储 逻辑上相邻存储物理位置也可不相邻,用指针反应关系
各节点的存储空间可以不连续,但存储单元的地址必须连续
索引存储 索引表(关键字、地址)
散列存储 根据关键字直接计算元素的存储地址(哈希存储)

1.数据的逻辑结构是以面向实际问题的角度出发的,只采用抽象表达方式,独立于存储结构。
2.满二叉树既可以是顺序存储,也可以链式存储。
3.线性结构:线性表、栈、队列、串、数组
4.非线性结构:集合、树、图
5.对于两种不同的数据结构,逻辑结构或物理结构一定不同吗:
数据结构的三要素:逻辑结构、存储结构、数据运算。因此,两种不同的数据结构,逻辑结构与物理结构可以不同(正常情况下);也可以相同(数据运算不同,逻辑结构与物理结构不同也称为两种数据结构)eg:二叉树与二叉排序树,两种不同的数据结构。二者均可以使用二叉树的逻辑结构与物理结构。但是建立树、插入结点、删除结点和查找结点等数据运算是不同的。
查找为例,二叉树 O(n),二叉排序树O(log 2 n)
6.对相同的逻辑结构,同一种运算在不同存储方式下实现。其运算效率不同。举例子
线性表既可以用顺序存储也可以链式存储;
插入或删除:
顺序:O(n) 链式:O(1)
n —1 取平均 n/2

1.2算法和算法评价

1.2.1算法的基本概念

算法:对特定问题求解步骤的一种描述,是指令的有限序列,其中每条指令表示一个或多个操作。
特性:
有穷性:算法是有穷的,程序可以是无穷的,死循环不是算法。
确定性:明确的含义,对于相同的输入只能对应相同的输出。
可行性:通过已经实现的基本运算执行有限次来实现。
输入:零个或多个,来自某个特定的对象的集合。
输出:一个或多个
目标(特质):
正确性:正确解决求解问题。
可读性:帮助理解,注释。
健壮性:输入非法数据,能够处理,不会产生奇怪的输出结果。
高效率与低存储量需求:时间少,不费内存。
时间复杂度 空间复杂度

1.2.2算法效率的度量

1.时间复杂度
事前预估算法时间开销与问题规模的关系 T与n 的关系
阶数高的部分 T(n)=O(n) 不要系数。
多项相加,只保留最高阶的项,且系数变为1;
多项式相乘,都保留
常对幂指阶
注: 1.顺序执行只会影响常数项,可以忽略;
2.只需条循环中的一个基本操作分析与n的关系
3.多层,只需关注最深层循环
最好、最坏、平均1/n
2.空间复杂度
S与n的关系
算法原地工作———算法所需内存空间为常量
加法规则:阶数最高的
函数调用:==递归调用的深度;数组不同;看情况分析

1.算法:准确而完整、有限时间
2.同一个算法,实现语言的级别越高,执行效率就越低。
3.一次求合———二次项;再求和———三次项

第二章线性表

2.1定义和操作

定义:线性表具有相同数据类型的n个数据元素的有限序列。
位序从1开始,下标从0开始
特点:元素有限;
逻辑上的顺序性,有先后次序;
每个元素都是单个元素;
类型相同,存储空间相同;
抽象性,仅讨论逻辑关系,一对一;
操作
InitList(&L):初始化。分配内存空间 从无到有
DestoryList(&L):销毁。释放内存空间 从有到无
LocateElem(L,e):按值查找操作
GetElem(L,i):按位查找操作
ListInsert(&L,i,e):插入;在表L中第i个位置插入指定元素e;
ListDelete(&L,i,&e):删除
Length(L):求表长。数据元素的个数。
PrintList(L):输出
Empty(L):判空,若空,返回true;否则返回false;
& 表示C++语言中的调用。
Tips:
1.对数据的操作(记忆思路)——— 创销、增删改查
2.C语言函数的定义———
<返回值类型>函数名(<参数1类型>参数1,<参数2类型>参数2,…)
3.实际开发中,可根据实际需求定义其他的基本操作
4.函数名和参数的形式、命名都可改变 Key:命名要有可读性
5.什么时候要传入引用“&”,对参数的修改结果需要“带回来”

2.2线性表的顺序表示

1.顺序表定义:
线性表的顺序存储又称顺序表。用一组地址连续的存储单元一次存储线性表中的数据单元,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
数据元素的物理位置:起始位置+数据元素的大小*(n-1)
一个数据元素的大小:C语言:sizeof(ElemType)
Eg sizeof(int) = 4B
Typedef struct{
Int num;//号数
Int people; //人数
}Customer;
Sizeof(Customer) = 8B

2.特点:表中元素的逻辑顺序与其物理顺序相同。
删除和插入需要移动大量元素;
随机访问;通过首地址和元素序号可在时间O(1)内找到;
存储密度高,每个结点只存储数据元素;
3.线性表中的任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。
4.一维数组:
静态分配:数组的大小和空间事先固定;
数组满了:可以放弃治疗,顺序表的表长刚开始确定后就无法更改(存储空间是静态的)
设置数组很大:浪费资源

#include <stdio.h>
#define MaxSize 10      //定义最大长度 
typedef struct{
    int data[MaxSize];  //用静态的“数组”存放数据元素 ElemType:int
    int Length;         //顺序表的当前长度
}SqList;                //顺序表的类型定义

//基本操作——初始化一个顺序表
void InitList(SqList &L){
    for(int i=0; i<MaxSize; i++){
        L.data[i]=0;   //将所有数据元素设置为默认初始值0,如果没有这一步,内存中会有遗留的“脏数据”
    }
    L.Length=0;        //顺序表初始长度为0
}

int main(){
    SqList L;          //声明一个顺序表
                       //在内存里分配存储顺序表L的空间
                       //包括MaxSize*sizeof(ElemType)和存储length的空间
    InitList(L);       //初始化这个顺序表
    //...
    return 0;
}

动态分配: malloc 申请资源;free 释放空间
C语言:L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize)
C++: L.data=new ElemType(InitSize)

#include <stdlib.h> //malloc,free函数的头文件
#define InitSize 10 //默认的最大长度

typedef struct{
    int *data;       //指示动态分配数组的指针
    int MaxSize;    //顺序表的最大容量
    int length;     //顺序表的当前长度
}SeqList;

void InitSize(SeqList &L){
    L.data = (int*)malloc(sizeof(int)*InitSize);  //用malloc函数申请一片连续的存储空间
    L.length = 0;
    L.MaxSize = InitSize;
}

int main(){
    SeqList L;
    InitSize(L);
    //...其余操作
    IncreaseSize(L,5);
    return 0;
}

//增加动态数组的长度
void IncreaseSize(SeqList &L, int len){
    int *p=L.data
    L.data = (int*)malloc((L.MaxSize+len)*sizeof(int));
    for(int i=0; i<L.length; i++){
        L.data[i] = p[i]         //将数据复制到新区域
    }
    L.MaxSize = L.MaxSize + len; //顺序表最大长度增加len
    free(p);                     //释放原来的内存空间
}

若不设置默认值,会有脏数据。

6.实现(判断i的合理性)
插入: ListInsert(&L,i,e) O(n)

基于静态分配的代码实现

#define MaxSize 10      //定义最大长度 
typedef struct{
    int data[MaxSize];  //用静态的“数组”存放数据元素 ElemType:int
    int Length;         //顺序表的当前长度
}SqList;                //顺序表的类型定义

//基本操作——在L的位序i处插入元素e
bool ListInsert(SqList &L, int i, int 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--){    //将第i个元素及其之后的元素后移
        L.data[j]=L.data[j-1];
    }
    L.data[i-1]=e;  //在位置i处放入e
    L.length++;      //长度加1
    return true;
}

int main(){
    SqList L;          //声明一个顺序表
    InitList(L);       //初始化这个顺序表
    //...插入几个元素
    ListInsert(L,3,3);
    return 0;
}
关注最深层循环语句——L.data[j]=L.data[j-1]的执行次数与问题规模n——L.length的关系;
最好情况:插入表尾,不需要移动元素,i=n+1,循环0次;最好时间复杂度 = O(1)
最坏情况:插入表头,需要将原有的n个元素全都向后移动,i=1,循环n次;最坏时间复杂度 = O(n)
平均情况:假设新元素插入到任何一个位置的概率p(=1/n+1)相同
平均循环次数 = np + (n-1)p + (n-2)p ++ 1×p = [ n(n+1)/2 ]×[ 1/(n+1) ] = n/2

平均时间复杂度 = O(n)

**删除**ListDelete(&L,i,e):删除表L中的第i个位置的元素,并用e返回删除元素的值

> 基于静态分配的代码实现

```c
#define MaxSize 10      //定义最大长度 
typedef struct{
    int data[MaxSize];  //用静态的“数组”存放数据元素 ElemType:int
    int Length;         //顺序表的当前长度
}SqList;                //顺序表的类型定义

bool LisDelete(SqList &L, int i, int &e){ // e用引用型参数 
    //判断i的范围是否有效
    if(i<1||i>L.length) 
        return false;

    e = L.data[i-1]    //将被删除的元素赋值给e

    for(int j=L.length; j>i; j--){    //将第i个后的元素前移
        L.data[j-1]=L.data[j];
    }
    L.length--;      //长度减1
    return true;
}

int main(){
    SqList L;          //声明一个顺序表
    InitList(L);       //初始化这个顺序表
    //...插入几个元素
    int e = -1;        //用变量e把删除的元素“带回来”
    if(LisDelete(L,3,e))
        printf("已删除第三个元素,删除元素值=%d\n",e);
    else
        printf("位序i不合法,删除失败\n");
    return 0;
}

时间复杂度分析

关注最深层循环语句——L.data[j-1]=L.data[j]的执行次数与问题规模n——L.length的关系;
最好情况:删除表尾元素,不需要移动元素,i=n,循环0次;最好时间复杂度 = O(1);
最坏情况:删除表头元素,需要将后续的n-1个元素全都向前移动,i=1,循环n-1次;最坏时间复杂度 = O(n);
平均情况:假设删除任何一个元素(1,2,3,…,length)的概率相同 p=1/n
平均循环次数 = (n-1)p + (n-2)p + … + 1×p = [ n(n-1)/2 ]×[ 1/(n) ] = n-1/2

平均时间复杂度 = O(n)
按位查找(顺序表)
GetElem(L,i) : 按位查找操作——获取表L中第i个位置元素的值

基于静态分配的代码实现

#define MaxSize 10            //定义最大长度 
typedef struct{
    ElemType data[MaxSize];  //用静态的“数组”存放数据元素 
    int Length;              //顺序表的当前长度
}SqList;                     //顺序表的类型定义

ElemType GetElem(SqList L, int i){
    // ...判断i的值是否合法
    return L.data[i-1];      //注意是i-1
}

基于动态分配的代码实现

#define InitSize 10  //顺序表的初始长度

typedef struct{
    ElemType *data;  //指示动态分配数组的指针
    int MaxSize;     //顺序表的最大容量
    int length;      //顺序表的当前长度
}SeqList;

ElemType GetElem(SqList L, int i){
    // ...判断i的值是否合法
    return L.data[i-1]; //就算是指针也能用数组下标哦!
}

时间复杂度分析

O(1)
由于顺序表的各个数据元素在内存中连续存放,因此可以根据起始地址和数据元素大小立即找到第i个元素———“随机存取”特性;

按值查找
LocateElem(L, e): 按值查找操作,在表L中查找具有给定关键字值的元素;

基于动态分配的代码实现

#define InitSize 10            //定义最大长度 
typedef struct{
    ElemTyp *data;  //用静态的“数组”存放数据元素 
    int Length;              //顺序表的当前长度
}SqList;   

//在顺序表L中查找第一个元素值等于e的元素,并返回其位序
int LocateElem(SqList L, ElemType e){
    for(int i=0; i<L.lengthl i++)
        if(L.data[i] == e)  
            return i+1;     //数组下标为i的元素值等于e,返回其位序i+1
    return 0;               //推出循环,说明查找失败
}

Q: 如果顺序表里存放的是结构类型的数据元素,可不可以用 == 进行比较?

A: 不能!结构类型的比较,需要依次对比各个分量来判断两个结构体是否相等;
例子:

typedef struct{
    int num;
    int people;
}Customer;

void test(){
    Customer a;
    Customer b;
    //...
    if (a.num == b.num && a.people == b.people){
        printf("相等");
    }else{
        printf("不相等");
    }
}

时间复杂度分析

最深处循环语句: if(L.data[i] == e) 与问题规模n=L.length(表长)的关系;
最好情况:查找目标元素在表头,循环1次,最好时间复杂度=O(1)
最坏情况:查找目标元素在表尾,循环n次,最好时间复杂度=O(n)
平均情况:假设目标元素出现在任何一个位置的概率相同,p=1/n
平均循环次数 = 1×1/n + 2×1/n +…+ n×1/n = [ n(n+1)/2 ] × 1/n = (n+1)/2

平均时间复杂度 = O(n)

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-11 16:51:09  更:2021-07-11 16:53:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/9 8:17:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码