软件设计师(五)数据库技术基础+数据结构

本文深入探讨了数据库技术的基础,包括数据库系统、DBMS的功能和特性、分类、体系结构,以及数据库的三级模式和映像。同时,讨论了数据模型、完整性约束、SQL语言、数据独立性、事务管理和并发控制等核心概念。此外,还涵盖了数据结构的基本概念,如线性表、链表、栈、队列、树和图,以及各种排序算法的时间复杂度和稳定性。
摘要由CSDN通过智能技术生成

数据库技术基础

1.数据库系统:数据库,硬件,软件,人员

2.DBMS(数据库管理系统)的功能:数据定义,数据库操作,数据库运行管理,数据组织、存储和管理,数据库的建立和维护,与其他软件系统的通信功能等

3.DBMS 的特征:数据结构化且统一管理,有较高的数据独立性,数据控制功能(数据库的安全性保护、数据的完整性、并发控制、故障恢复)

4.DBMS 分类:

关系数据库系统(实体间的联系用关系表示)

面向对象的数据库系统(以对象形式对数据建模)

对象关系数据库系统(在关系数据模型基础上提供处理新的数据类型操作的能力)

5.数据库系统体系结构:

集中式(数据、数据管理、数据库功能等都集中在一起)

分布式(物理上分布+逻辑上分布)

C/S 模式(客户端负责数据表示服务、服务器负责数据库服务)

并行结构(多个 CPU 物理上连在一起处理)

6.数据库的三级模式:

概念模式(数据库中全部数据的逻辑结构和特征的描述、只涉及型的描述而不涉及具体的值)

外模式(用户与数据库系统的接口、用户用到那部分数据的描述)

内模式(数据物理结构和存储方式的描述、数据在数据库内部的表示方式)

外模式对应视图;概念模式对应基本表;内模式对应存储文件

7.数据库的两级映像:

概念模式/内模式映射(实现概念模式与内模式的转换)

外模式/概念模式映射(实现外模式与概念模式的转换)

8.数据的独立性:

物理独立性(数据库的内模式改变时数据的逻辑结构不变)

逻辑独立性(用户的应用程序与数据库的逻辑结构相互独立)

9.数据模型:

概念数据模型(E-R 模型等)

基本数据模型(层次模型:用树型结构表示数据间的联系;网状模型:用网络结构表示数据间的联系;关系模型:用表格结构表示实体间的联系;面向对象模型:对象标识+封装+对象的属性+类和类层次+继承)

10.数据模型三要素:数据结构,数据操作,数据的约束条件

11.E-R 图:实体(矩形),联系(菱形),属性(椭圆形)

12.完整性约束:实体完整性,参照完整性,用户自定义完整性

13.关系代数运算:并,交,差,笛卡尔积,投影,选择,连接,除

并:两个关系不同的部分,S1∪S2→S1和S2不同的记录

交:两个关系相同的部分,S1∩S2→S1和S2相同的记录

差:前一个关系有而后一个关系没有的部分,S1-S2→S1有但S2没有记录

连接:属性相连,去掉重复列

笛卡尔积:两个关系每条记录都连接一次

投影:指定属性展示出来

选择:指定记录展示出来

14.SQL 语言的特点:综合统一,高度非过程化,面向集合的操作方式,两种使用方式(自含式、嵌入式),语言简洁易学易用

15.SQL 语言的组成:语数据定义言,交互式数据操纵语言,事务控制,嵌入式SQ 和动态SQL,完整性,权限管理

16.SQL 数据定义:创建(create),修改(alter),删除(drop):表(table),视图(view[as select]),索引(index[on])

17.SQL 数据查询:select...from...where...group by...having...order by...

18.插入数据:insert into...values...

19.修改数据:update...set...=...where...

20.删除数据:delete from...where...

21.授权:grant...on...to...(with grant option)

22.回收权限:revoke...on...from...

23.函数依赖:反映属性间的联系(X→Y);

完全函数依赖:(学生 ID,所修课程 ID)→成绩;

部分函数依赖 :(学生 ID,所修课程 ID)→学生姓名;

平凡函数依赖:X→Y 且 Y 包含于X;

非平凡函数依赖:X→Y 且Y 不包含于X;

传递函数依赖:X→Y,Y→Z

求候选键:

1、将函数依赖转化为有向图

2、找出入度为0的属性(或属性集合),以该属性(或属性集合)为起点,尝试遍历有向图,若能遍历完所有节点,则为一个候选键。

3、若没有入度为0的节点,找中间节点进行遍历

例子:关系R(A1,A2,A3,A4),函数依赖为F={A1→A2,A3→A2,A2→A3,A2→A4}

有向图为:

A1入度为0,且能够遍历完所有节点,所以A1为候选键

24.规范化:

1NF:每个分量都不可再分;

2NF:消除非主属性对码的部分函数依赖;

3NF:消除非主属性对码的传递函数依赖

25.模式分解标准:无损连接,保持函数依赖

26.事务的 ACID 性质:原子性,一致性,隔离性,持久性

27.事务管理:事务开始(begin transaction),事务提交(commit),事务回滚(rollback)

28.数据库故障:事务内部故障,系统故障,介质故障,计算机病毒

29.数据备份方法:静态转储和动态转储,海量转储和增量转储,日志文件

30.数据恢复步骤:反向扫描文件日志,对事物的更新操作执行逆操作,继续反向扫描和更新,直到事务的开始标志

31.并发控制的技术:封锁(写锁、读锁)

32.数据不一致性:丢失修改,不可重复读,读脏数据

33.在数据库逻辑结构设计阶段,需要(需求分析)阶段形成的(需求说明文档、数据字典和数据流图)作为设计依据

数据结构

1.数据结构:数据元素的集合及元素间的相互关系和构造方法

2.线性表的存储结构:顺序存储,链式存储

3.单链表节点:typedef struct node{ int data; struct node *link; }NODE,*LinkList;

4.双向链表:每个节点有两个指针,分别指出直接前驱和直接后继

5.循环链表:尾节点指针指向第一个节点

6.静态链表:借助数组来描述线性表的链式存储结构

7.栈:后进先出;初始化栈:InitStack(S) 判栈空:StackEmpty(S) 入栈:Push(S,x) 出栈:Pop(S) 读取栈顶元素:Top(S) 顺序存储+链式存储

8.队列:先进先出,尾入头出;初始化队:初始化队:InitQueue(Q) 判队空:Empty(Q) 入

队列的特点是先进先出,若用循环单链表表示队列,则:入队和出队操作都不需要遍历链表

解析:入队和出队都只需移到指针指向位置

入队:新节点s->next指向首结点,然后rear->next指向新的结点s

出队:rear->next=rear->next->next

9.串:仅由字符构成的有限序列,是取值范围受限的线性表

10.数组:定长线性表在维数上的扩张,一般不做插入删除运算

11.矩阵:

特殊矩阵(元素分布有一定的规律:对称矩阵、三角矩阵、对角矩阵);

稀疏矩阵(非零元素远少于零元素且律:用三元组存储(行号,列号,值))

12.广义表(表中有表):表头(表中第一个元素);表尾(表中除去表头剩下的部分)

13.树:递归的,元素之间有明显的层次关系

14.完全二叉树应采用顺序存储结构,一般二叉树则应采用链式存储结构

15.二叉树的链式存储结构:typedef struct BiTnode{ int data; struct BiTnode *lchild, *rchild; }BiTnode, *BiTree;

16.二叉树的遍历:先序遍历(先访问根节点),中序遍历(第二访问根节点),后序遍历(最后访问根节点),层序遍历(利用队列、每次出同一层的节点时进他们的子节点层)

17.线索二叉树:加上线索(直接前驱和直接后继)的二叉树

18.最优二叉树(哈夫曼树):一类带权路径长度最短的树

19.树的存储结构:双亲表示法(顺序存储);孩子表示法(链式存储);孩子兄弟表示法(链式存储,两个指针分别为第一个孩子和下一个兄弟)

20.图:一个节点的前驱节点和后继节点数目没有任何限制

21.图的表示:G=(V,E);V:顶点的集合;E:边的集合

22.网:边带权值的图

23.图的相关概念

24.图的存储结构:邻接矩阵表示法,邻接链表表示法

25.图的遍历:深度优先搜索,广度优先搜索

26.生成树:极小连通子图,针对连通图

27.最小生成树(权值和最小的生成树)

算法:

普尼姆算法(在相邻边的基础上求最小,与边数无关,适于边稠密的网);

克鲁斯科尔算法(在不构成环的基础上找最小边直至连通,与顶点数无关,适于边稀疏的网)

28.AOV 网:有向图中顶点表示活动,有向边表示活动间的优先关系

29.拓扑排序:将AOV 网中所有顶点按优先顺序排成一个线性序列的过程

30.AOE 网:有向图中有向边表示活动,边上的权值表示该活动持续的时间

31.关键路径:从源点到汇点的路径中长度最长的

32.最短路径:从源点到其余各顶点的最短路径-----迪杰斯克拉算法

33.平均查找长度:关键字和给定值进行过比较的记录个数的平均值

34.静态查找方法:顺序查找;折半查找;分块查找

35.动态查找:表结构本身在查找过程中是动态生成的

36.二叉排序树:左子树上所有节点的值小于根节点的值,右子树上所有节点的值大于根节点的值

37.平衡二叉树(AVL 树):左子树和右子树高度之差的绝对值不超过 1

38.B_树(m 阶):每个节点子树个数<=m,根节点子树个数=0 或>=2,其他节点子树个数=0或>=m/2

39.哈希表:通过哈希函数(以记录的关键字为自变量)得到记录的存储地址;定长按一定函数规律存放数据;哈希地址+关键字

40.哈希表的重点:构造哈希函数(直接定址法,数字分析法,平方取中法,折叠法,随机数法,除留余数法);解决冲突(开放定址法,链地址法,再哈希法)

41.简单排序(时间复杂度 O(n2),空间复杂度 O(1))

直接插入排序(插入第 i 个时前 i-1 个以排序好)

冒泡排序(相邻两个比较排序,每次循环确定一个极值)

简单选择排序(第 i 个依次与后面每个元素比较排序,每次循环确定一个极值,不稳定)

42.高端内部排序:

希尔排序(先将整个序列分割成若干序列分别进行直接插入排序,再对整个序列进行一次直接插入排序,不稳定);

快速排序(将整个记录分割成独立的两部分,两个指针分别指向对应部分的两端,往中间移动比较排序,递归,不稳定);

堆排序(建立初始堆输出并删除堆顶关键字,再建立新堆得到新的关键字依次输出,不稳定)

归并排序(将若干个有序序列合并为新的有序序列);

基数排序(按组成关键字的各个数位的值进行排序)

43.排序算法的时间复杂度、空间复杂度以及是否是稳定排序

直接插入排序、希尔排序、直接选择排序、冒泡排序:O(n^2),O(1)

稳定:直接插入、冒泡、归并、基数

对于具有n个元素的一个数据序列,若只需得到其中第k个元素之前的部分排序,最好采用:堆排序

解析:对于希尔排序、直接插入排序,只有在排序过程后才能确保全部序列以及前k个元素的最终排序。

记忆:快速排序时间复杂度【最好和最坏】和空间复杂度,不稳定排序

直接插入排序:从数组的第二位开始,依次倒叙向前遍历排序,满足条件则交换顺序

时间复杂度是O(n²),空间复杂度是O(1),稳定排序

private void InsertSort(int[] array) {
    if (array == null || array.length < 2) {
        return;
    }

    for (int i = 1; i < array.length; i++) {
        // 标志位,防止待插入元素最小
        boolean flag = false;
        int cur = array[i];
        for (int j = i - 1; j > -1; j--) {
            // 当前值和它前面的值比较,如果比前面的值小,前面的值后移,当前值替换前面的值;否则设置标识位
            if (cur < array[j]) {
                array[j + 1] = array[j];
                array[j] = cur;
            } else {
                flag = true;
                break;
            }
        }
        if (!flag) {
            array[0] = cur;
        }
    }
}

希尔排序:对直接插入排序的优化,将待排序的数组元素按下标的一定增量分组,分成多个子序列,然后对各个子序列进行直接插入排序算法排序;然后依次缩减增量再进行排序,直到增量为1时,进行最后一次直接插入排序,排序结束。

时间复杂度是O(nlog2n),空间复杂度是O(1),非稳定排序

private void shellSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }

    // 3for + if
    for (int d = arr.length/2; d >= 1; d = d/2) {
        // 增量后半截
        for (int i = d; i < arr.length; i++) {
            // 增量前半截
             for (int j = i - d; j > -1; j -= d) {
                 if (arr[j] > arr[j + d]) {
                     // 后半截和前半截差量进行插入排序
                     int tmp = arr[j];
                     arr[j] = arr[j + d];
                     arr[j + d] = tmp;
                 }
            }
        }
    }
}

简单选择排序:每一趟从待排序的数据元素中选择最小(或最大)的一个元素作为首元素,直到所有元素排完为止,例:21 48 21* 63 17,第一趟排序,21与17交换,导致21与21*的相对位置发生变化

时间复杂度是O(n2),空间复杂度是O(1),不稳定排序

private void selectSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }

    for (int i = 0; i < arr.length; i++) {
        // 记录每次循环中的最小值
        int minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[minIndex] > arr[j]) {
                minIndex = j;
            }
        }

        // 最小值不是当前值,交换位置
        if (minIndex != i) {
            int temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
        }
    }
}

冒泡排序:对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。

时间复杂度是O(n2),空间复杂度是O(1),稳定排序

private void bubbleSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }

    for (int i = 0; i < arr.length; i++) {
        boolean flag = false;

        // 每次冒最大值到数组最后一位
        for (int j = 0; j < arr.length - i -1; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;

                flag = true;
            }
        }

        // 本次循环没有移动位置,说明数组已有序,退出循环
        if (!flag) {
            break;
        }
    }
}

堆排序:过程可以具体分为三步,创建堆,调整堆,堆排序。

  1. 创建堆,以数组的形式将堆中所有的数据重新排序,使其成为最大堆/最小堆。
  2. 调整堆,调整过程需要保证堆序性质:在一个二叉堆中任意父节点大于其子节点。
  3. 堆排序,取出位于堆顶的第一个数据(最大堆则为最大数,最小堆则为最小数),放入输出数组B中【也可以是它自己】,再将剩下的堆做调整,堆的迭代/重复运算直至输入数组A中只剩下最后一个元素。

时间复杂度是O(n*log(n)),空间复杂度是O(1),不稳定排序

private void heapSort(int[] arr) {
    for (int i = arr.length - 1; i > 0; i--) {
        // 调整堆
        for (int j = cur; j > 0; j--) {
            if (arr[i] > arr[i/2]) {
                swap(arr, i, i/2);
            }
        }
        // 排序堆
        swap(arr, i, 0);
    }
}

private static void swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

快速排序:选择一个基准值,一轮排序后,基准值左边的值都比它小,右边的值都比它大;对基准值左右两边的数进行相同的操作,直到基准值左右两边的值只有一个

时间复杂度是O(nlog n),空间复杂度是O(logn),不稳定排序

划分过程中,最佳的基准元素选择的方法是选择待划分数组的(中位数)元素。此时,算法在最坏情况下的时间复杂度为(不考虑所有元素均相等的情况)(O(n))

中位数

private void fastSort(int[] arr, int begin, int end) {
    // 记录每一次开始和结束位置,如果 begin >= end 递归结束
    int i = begin;
    int j = end;
    if (begin >= end) {
        return;
    }
    
    // 选出哨兵元素【i和j】和基准元素
    int base = arr[begin];
    while (i < j) {
        // 从右开始选择第一个比基准小的值
        while (arr[j] >= base && i < j) {
            j--;
        }
        // 从左开始选择第一个比基准大的值
        while (arr[i] <= base && i < j) {
            i++;
        }
        // 交换i和j的值
        if (i != j) {
            swap(arr, i, j);
        }
    }

    // 当i==j时,当前值和基准值做交换,作为新一轮基准值
    swap(arr, i, begin);

    // 基准左边值
    fastSort(arr, begin, i - 1);
    // 基准右边值
    fastSort(arr, i + 1, end);
}

private static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

归并排序:分治法,将数组拆分成一个个小的数组,排序后,再合并成一个大的数组

时间复杂度是O(nlogn)【合并的平均时间复杂度为O(n),完全二叉树的深度为log2n】,空间复杂度是O(n),稳定排序

public void main(String[] args) {
    int[] sourceArr = new int[]{9, 3, 1, 4, 2, 7, 8, 6, 5};
    int[] mergeArr = new int[sourceArr.length];
    mergeSort(sourceArr, mergeArr, 0, sourceArr.length - 1);
    System.out.println(Arrays.toString(sourceArr));
}

private static void mergeSort(int[] sourceArr, int[] mergeArr, int low, int high) {
    if (low >= high) {
        return;
    }

    int mid = (low + high)/2;

    // 拆分左边数组
    mergeSort(sourceArr, mergeArr, low, mid);
    // 拆分右边数组
    mergeSort(sourceArr, mergeArr, mid + 1, high);
    // 合并
    merge(sourceArr, mergeArr, low, high, mid);
}

private static void merge(int[] sourceArr, int[] mergeArr, int low, int high, int mid) {
    int i = low;
    int j = mid + 1;
    int k = 0;
    while (i <= mid && j <= high) {
        if (sourceArr[i] <= sourceArr[j]) {
            mergeArr[k++] = sourceArr[i++];
        } else {
            mergeArr[k++] = sourceArr[j++];
        }
    }

    // 左边剩余元素填充进临时数组中
    while (i <= mid) {
        mergeArr[k++] = sourceArr[i++];
    }
    // 右边剩余元素填充进临时数组中
    while (j <= high) {
        mergeArr[k++] = sourceArr[j++];
    }

    // 临时数组值拷贝回原数组
    k = 0;
    while(low <= high){
        sourceArr[low++] = mergeArr[k++];
    }
}

例:对基本有序的整数排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值