《数据结构》课程绪论

本文作为《数据结构》课程的绪论,介绍了数据结构的基本概念,如数据、数据元素、数据结构的逻辑和存储结构。讨论了逻辑结构的表示方法和类型,包括集合、线性结构、树形结构和图型结构。同时,阐述了顺序存储结构、链式存储结构、索引存储结构和哈希存储结构的特点。此外,还探讨了算法的时间复杂度分析,包括最好、最坏、平均时间复杂度,并通过二分查找、选择排序和二路归并排序举例说明。
摘要由CSDN通过智能技术生成

《数据结构》课程绪论

使用教材:

《数据结构教程》第5版 李春葆 清华大学出版社

参考书:

《数据结构与算法分析 C语言描述》(第2版) Mark Allen Weiss 机械工业出版社
《算法导论》(第3版) Charles E. Leiserson 等 机械工业出版社
《算法》(第4版) Robert Sedgewick Kevin Wayne 人民邮电出版社
《STL源码剖析》 侯捷 华中科技大学出版社

使用语言:C/C++

1.数据结构基本概念

数据与数据结构

  • 数据(data):描述客观事物的数和字符的集合。
  • 数据元素(data element):数据的基本单位
  • 数据项(data item):具有独立含义的数据最小单位,又叫字段或域。
  • 数据结构(data structure):所有数据元素及其构成的关系

数据结构包括的三个方面

  • 逻辑结构(logical structure)(数据的逻辑关系)
  • 存储结构(storage structure)(物理结构,如何在存储器上表示)
  • 运算(operation)(对数据实施的操作,增删改查排序等)

逻辑结构的表示方法

  • 图表
  • 二元组 B=(D,R) ,其中 D 为数据集合,R为数据的所有关系的集合
  • 每一个关系是序偶的集合,对于序偶 <x,y> <script type="math/tex" id="MathJax-Element-8"> </script>( x,yD ), x y的前驱(predecessor), y x的后继(successor)。使用尖括号表示 x y是一个偏序关系,圆括号表示为等价关系。

逻辑结构的类型

  • 集合
  • 线性结构
  • 树形结构
  • 图型结构

存储结构

处理存储结构,既要保证存储所有的元素 xD ,也要存储数据之间的逻辑关系 rR
1. 顺序存储结构:所有元素在存储器中都是连续存放的,逻辑相邻的元素也必然物理相邻。支持快速的查找,插入和删除较慢;
2. 链式存储结构:每个逻辑元素使用内存中的结点存储。所有结点的地址不一定是连续的。C/C++使用指针来链接每一个结点。Java等语言使用引用。支持快速的插入和删除,但是不能随机存取,需要额外浪费指针空间。
3. 索引存储结构:利用关键字,地址的方式建立索引,可以利用关键字的有序性快速查找到关键字地址。查找效率高,但是索引表需要额外空间开销。
4. 哈希(散列)存储结构:直接建立值->存储地址的映射,支持快速查找和插入,不存储数据之间的逻辑关系。

2.算法及其分析

算法的特点

  • 有穷性
  • 确定性
  • 可行性
  • 有输入输出

算法设计的目标

  • 正确性
  • 可使用性
  • 可读性
  • 健壮性
  • 高效率、低存储

算法的时间分析

T(n) 表示元操作出现的频度(注意是实际执行的次数,例如求一个数 n 的所有因子,只需要判断t[1,n]是不是其因子即可,如果是则 t n/t都是其因子,因此其内层的判断语句实际执行的次数是 n 次,因此时间复杂度为 O(n)
通常用“大O记号”表示其渐进复杂度。渐进复杂度取其最高阶,忽略系数和低阶项。

最好、最坏、平均时间复杂度

设输入规模 n ,所有输入实例的集合为D I 表示输入。
平均时间复杂度的定义E(n)=P(I)T(I),ID
最好时间复杂度: B(n)=min{T(n)},ID ,
最坏时间复杂度: W(n)=max{T(n)},ID .

递归算法分析

先求出递归式,然后对其化简,或使用递归树、主定理等方法。

算法复杂度分析举例
二分查找最坏情况
/**
* 二分查找(升序数组中查找)
* @param int[] A 待查找的数组
* @param int length 数组的长度
* @param int value 待查找的元素
* @return int 该元素在数组中的下标(0开始,未找到则返回-1)
*/
int binary_search(int A[], int length, int value) {
    int begin = 0, end = length - 1, mid;
    while (begin <= end) {
        mid = (begin + end) / 2;
        if (A[mid] == value)
            return mid;
        else if (A[mid] > value)
            end = mid - 1;
        else
            begin = mid + 1;
    }
    return -1;
}

二分查找的核心语句是while循环。
最坏情况下,数组A中没有值为value的元素,不妨设 A={a1,a2,...,an} ,且 a1<a2<...<an v<a1 。于是:
第一次比较后,剩余 N1=n2 个元素( n2n2

第二次比较后剩余 N2=n212 个元素( n212n22 ),…,

最后一次(第 k 次)比较剩余Nk=1个元素,又 Nkn2k ,于是 n2k1 ,故 2kn ,解得 klog2n 。其他情况同理。
因此最坏情况下的时间复杂度为 O(log2n)

选择排序
/**
* 选择排序(升序)
* @param int[] A 待排序数组
* @param int length 数组长度
* @return int 函数返回值,0表示成功
*/
int selection_sort(int A[], int length){
    int i, j;
    int min, tmp;
    for (i = 0; i < length - 1; i++) {  
        min = i;
        for (j = i + 1; j < length; j++)
            if (A[min] > A[j])
                min = j;
        tmp = A[i];
        A[i] = A[min];
        A[min] = tmp;
    }
    return 0;
}

核心语句是一条if判断语句,
对于外层for循环,执行 n 次;
内层for循环,执行(n1)+(n2)+...+1=n22次;
因此其时间复杂度是 O(n×n22)=O(n2)

二路归并排序
/**
* 二路归并排序
* @param int[] A 待排序数组
* @param int length 数组长度
* @return int 函数返回值,0成功
*/
int merge_sort(int A[], int length) {
    int * aux = (int *)malloc(sizeof(int) * length);
    merge_sort_sub(A, 0, length - 1, aux);
    return 0;
}
/**
* 二路归并排序子函数
* @param int[] A 源数组
* @param int begin 当前排序的子数组的起始下标
* @param int end 当前排序的子数组的结束下标
* @param int[] aux 临时数组
*/
int merge_sort_sub(int A[], int begin, int end, int aux[]) {
    int midpoint;
    int i, j, k;
    if (end <= begin)
        return;
    midpoint = (end + begin) / 2;
    merge_sort_sub(A, begin, midpoint, aux);
    merge_sort_sub(A, midpoint + 1, end, aux);
    for (k = begin; k <= end; k++)
        aux[k] = A[k];
    i = begin;
    j = midpoint + 1;
    for (k = begin; k <= end; k++) {
        if (i <= midpoint && j <= end) {
            if (aux[i] > aux[j])
                A[k] = aux[j++];
            else
                A[k] = aux[i++];
        }
        else if (i > midpoint) {
            A[k] = aux[j++];
        }
        else if (j > end) {
            A[k] = aux[i++];
        }
    }
    return 0;
}

在递归过程中,设merge_sort_sub一个长度为 n 的数组需要的执行频度为T(n)
merge_sort_sub(A, begin, midpoint, aux)贡献为 T(n2)
merge_sort_sub(A, midpoint + 1, end, aux)的贡献为 T(n2)
最后需要对 n 个元素进行归并,贡献为n
因此有递归式 T(n)=T(n2)+T(n2)+n
假设 n 2的幂的特殊情况,则上式为
T(n)=2T(n2)+n
所以
2T(n2)=4T(n4)+n
4T(n4)=8T(n8)+n

2k1T(n2k1)=2kT(n2k)+n
容易知道起始条件 T(1)=1 ,那么令 n2k=1 ,解得 k=log2n
所以 T(n)=kn+2kT(n2k)=nlog2n+log2n
所以 T(n)=O(nlog2n)
当为一般情况时,可根据主定理的情况2( a=2,b=2,f(n)=n ),得出渐进复杂度为 O(nlog2n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值