❤️六万字《算法和数据结构》之《画解数据结构》总纲,算法零基础教程❤️(建议收藏)

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️六万字《算法和数据结构》之《画解数据结构》总纲,算法零基础教程❤️(建议收藏)相关的知识,希望对你有一定的参考价值。

前言

  据说「 前言 」 写太多会被人唾弃,所以,这次直接进入正题。

画解数据结构


点击我跳转末尾 获取 粉丝专属 《算法和数据结构》源码。

第一章
线性表

❤️《画解数据结构》(1-1)画解顺序表❤️
❤️《画解数据结构》(1-2)画解链表❤️
❤️《画解数据结构》(1-3)画解栈❤️
❤️《画解数据结构》(1-4)画解队列❤️
❤️《画解数据结构》(1-5)画解双端队列❤️
❤️《画解数据结构》(1-6)画解哈希表❤️

第二章

❤️《画解数据结构》(2-1)画解树❤️
❤️《画解数据结构》(2-2)画解二叉树❤️
❤️《画解数据结构》(2-3)画解二叉搜索树❤️
❤️《画解数据结构》(2-4)画解堆❤️
❤️《画解数据结构》(2-5)画解AVL树❤️
❤️《画解数据结构》(2-6)画解线段树❤️
❤️《画解数据结构》(2-7)画解字典树❤️
❤️《画解数据结构》(2-8)画解霍夫曼树❤️
❤️《画解数据结构》(2-9)画解并查集❤️

第三章

❤️《画解数据结构》(3-1)画解图❤️
❤️《画解数据结构》(3-2)画解二分匹配❤️
❤️《画解数据结构》(3-3)画解最短路❤️
❤️《画解数据结构》(3-5)画解最小生成树❤️
❤️《画解数据结构》(3-4)画解强连通❤️


正文

第一章
线性表
(1-1)画解顺序表

一、顺序表的概念

1、顺序存储

  顺序存储结构,是指用一段地址连续的存储单元依次存储线性表的数据元素。

2、存储方式

  在编程语言中,用一维数组来实现顺序存储结构,在C语言中,把第一个数据元素存储到下标为 0 的位置中,把第 2 个数据元素存储到下标为 1 的位置中,以此类推。

3、长度和容量

  数组的长度指的是数组当前有多少个元素,数组的容量指的是数组最大能够存放多少个元素。如果数组元素大于最大能存储的范围,在程序上是不允许的,可能会产生意想不到的问题,实现上是需要规避的。

  如上图所示,数组的长度为 5,即红色部分;容量为 8,即红色 加 蓝色部分。

4、数据结构定义

#define MAXN 1024
#define DataType int        // (1)

struct SeqList {
    DataType data[MAXN];    // (2)
    int length;             // (3)
}; 
  • ( 1 ) (1) (1) 数组类型为DataType,定义为int
  • ( 2 ) (2) (2) SeqList定义的就是一个最多存放MAXN个元素的数组,MAXN代表数组容量;
  • ( 3 ) (3) (3) length代表数组长度,即当前的元素个数。

二、常用接口实现

1、只读接口

1)索引

  索引 就是通过 数组下标 寻找 数组元素 的过程。C语言实现如下:

DataType SeqListIndex(struct SeqList *sq, int i) {
    return sq->data[i];          // (1)
}
  • ( 1 ) (1) (1) 调用方需要注意 i i i 的取值必须为非负整数,且小于数组最大长度。否则有可能导致异常,引发崩溃。
  • 索引的算法时间复杂度为 O ( 1 ) O(1) O(1)

2)查找

  查找 就是通过 数组元素 寻找 数组下标 的过程,是索引的逆过程。
  对于有序数组,可以采用 二分 进行查找,时间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n);对于无序数组,只能通过遍历比较,由于元素可能不在数组中,可能遍历全表,所以查找的最坏时间复杂度为 O ( n ) O(n) O(n)
  简单介绍一个线性查找的例子,实现如下:

DataType SeqListFind(struct SeqList *sq, DataType dt) {
    int i;
    for(i = 0; i < sq->length; ++i) { // (1)
        if(sq->data[i] == dt) {
            return i;                 // (2)
        }    
    }
    return -1;                        // (3)
}
  • ( 1 ) (1) (1) 遍历数组元素;
  • ( 2 ) (2) (2) 对数组元素 和 传入的数据进行判等,一旦发现相等就返回对应数据的下标;
  • ( 3 ) (3) (3) 当数组遍历完还是找不到,说明这个数据肯定是不存在的,直接返回 − 1 -1 1

3)获取长度

  获取 数组的长度 指的是查询当前有多少元素。可以直接用结构体的内部变量。C语言代码实现如下:

DataType SeqListGetLength(struct SeqList *sq) {
    return sq->length; 
}

2、可写接口

1)插入

  插入接口定义为:在数组的第 k k k 个元素前插入一个数 v v v。由于数组是连续存储的,那么从 k k k 个元素往后的元素都必须往后移动一位,当 k = 0 k=0 k=0 时,所有元素都必须移动,所以最坏时间复杂度为 O ( n ) O(n) O(n)。C语言代码实现如下:

int SeqListInsert(struct SeqList *sq, int k, DataType v) {
    int i;
    if(sq->length == MAXN) {
        return 0;                        // (1) 
    } 
    for(i = sq->length; i > k; --i) {
        sq->data[i] = sq->data[i-1];     // (2) 
    }
    sq->data[k] = v;                     // (3) 
    sq->length ++;                       // (4) 
    return 1;                            // (5) 
}
  • ( 1 ) (1) (1) 当元素个数已满时,返回 0 0 0 代表插入失败;
  • ( 2 ) (2) (2) 从第 k k k 个数开始,每个数往后移动一个位置,注意必须逆序;
  • ( 3 ) (3) (3) 将第 k k k 个数变成 v v v
  • ( 4 ) (4) (4) 插入了一个数,数组长度加一;
  • ( 5 ) (5) (5) 返回 1 1 1 代表插入成功;

2)删除

  插入接口定义为:将数组的第 k k k 个元素删除。由于数组是连续存储的,那么第 k k k 个元素删除,往后的元素势必要往前移动一位,当 k = 0 k=0 k=0 时,所有元素都必须移动,所以最坏时间复杂度为 O ( n ) O(n) O(n)。C语言代码实现如下:

int SeqListDelete(struct SeqList *sq, int k) {
    int i;
    if(sq->length == 0) {
        return 0;                        // (1) 
    } 
    for(i = k; i < sq->length - 1; ++i) {
        sq->data[i] = sq->data[i+1];     // (2) 
    } 
    sq->length --;                       // (3) 
    return 1;                            // (4)  
}
  • ( 1 ) (1) (1) 返回0代表删除失败;
  • ( 2 ) (2) (2) 从前往后;
  • ( 3 ) (3) (3) 数组长度减一;
  • ( 4 ) (4) (4) 返回1代表删除成功;

三、优缺点

1、优点

  1)无须为表示表中元素逻辑关系而增加额外的存储空间;
  2)随机存取元素时可以达到 O ( 1 ) O(1) O(1),效率高;

2、缺点

  1)插入和删除时需要移动大量元素;
  2)必须一开始就确定存储空间的容量;

四、数组相关算法

1、线性枚举

1)问题描述

  给定一个长度为 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \\le n \\le 10^5) n(1n105) 的整型数组,求所有数组元素中的其中的最小值。

2)动图演示

3)示例说明

  蓝色的数据代表的是数组数据,红色的数据代表当前枚举到的数据,这样就可以遍历所有的数据进行逻辑处理了。

4)算法描述

  遍历数组,进行条件判断,条件满足则执行逻辑。这里的条件就是 枚举到的数 是否小于 当前最小值,执行逻辑为 将 当前枚举到的数 赋值给 当前最小值

5)源码详解

int findMin(int* nums, int numsSize){
    int i, min = 100000;
    for(i = 0; i < numsSize; ++i) {     // (1)
        if(nums[i] < min) {             // (2)
            min = nums[i];
        }
    }
    return min;                         // (3)
}
  • ( 1 ) (1) (1) 遍历数组中所有的数;
  • ( 2 ) (2) (2) 如果 当前枚举到的数 比记录的变量min小,则将它赋值给min;否则,不做任何处理;
  • ( 3 ) (3) (3) 最后,min中存储的就是整个数组的最小值。

2、前缀和差分

1)问题描述

  给定一个 n ( n ≤ 1 0 5 ) n (n \\le 10^5) n(n105) 个元素的整型数组 a i a_i ai,再给出 m ( m ≤ 1 0 5 ) m(m \\le 10^5) m(m105) 次询问,每次询问是一个区间 [ l , r ] [l, r] [l,r],求 h ( l , r ) = ∑ k = l r a k h(l,r) = \\sum_{k=l}^r a_k h(l,r)=k=lrak

2)动图演示

3)样例分析

  如上图所示,只需要记录一个前缀和,然后就可以通过一次减法将区间的值计算出来。时间复杂度 O ( 1 ) O(1) O(1)。这种就是差分的思想。

4)算法描述

  第一个枚举,利用一个数组sum,存储前 i i i 个元素的和。
  第二个枚举,读入 m m m 组数据 l , r l, r l,r,对每组数据,通过 O ( 1 ) O(1) O(1) 获取答案,即 s u m r − s u m l − 1 sum_r - sum_{l-1} sumrsuml1

5)源码详解

int sum[maxn];
int* prefixSum(int* nums, int numsSize, int m, int *l, int *r){
    int i;
    int *ret;
    for(i = 0; i < numsSize; ++i) {
        sum[i] = nums[i];
        if(i) 
            sum[i] += sum[i-1];                 // (1) 
    }
    ret = (int *) malloc( m * sizeof(int) );    // (2) 
    for(i = 0; i < m; ++i) {
    	int leftsum = l==0? 0 : sum[l-1];       // (3) 
    	int rightsum = sum[r];
    	ret[i] = rightsum - leftsum;            // (4) 
    }
    return ret;
}
  • ( 1 ) (1) (1) 计算前缀和;
  • ( 2 ) (2) (2) 需要返回的数组;
  • ( 3 ) (3) (3) 这里是为了防止数组下标越界;
  • ( 4 ) (4) (4) 核心 O ( 1 ) O(1) O(1) 的差分计算;

3、双指针

1)问题描述

  给定一个长度为 n ( 1 ≤ n ≤ 1 0 7 ) n (1 \\le n \\le 10^7) n(1以上是关于❤️六万字《算法和数据结构》之《画解数据结构》总纲,算法零基础教程❤️(建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章

❤️肝爆六万字 + 各种图解案例《大数据ETL开发之Kettle工具》小白保姆级教程❤️建议收藏

❤️五万字《十大排序算法》动图讲解❤️(建议收藏)

❤️五万字《十大排序算法》动图讲解❤️(建议收藏)

❤️《画解数据结构》之 顺序表八大算法总结❤️(建议收藏)

❤️六万字最全C语言动漫式教程,全程连载中❤️(建议收藏)

❤️爆肝六万字最全总结Java数据库编程MyBatis(建议收藏)