数据结构

一本合适的书

  算法第4版

可视化帮助

  https://visualgo.net/zh

  b树:https://www.cs.usfca.edu/~galles/visualization/BTree.html

 对增长数量级的常见假设

  常数阶 1 

  对数阶 log n

  线性阶 n

  线性对数阶 n log n

  平方阶 n^2

  立方阶 n^3

  指数阶 2^n

排序

选择排序

找到数组中最小的那个元素,将它和数组的第一个元素交换位置。在剩下的元素中找到最小元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。

void selectSort(int data[], int n) {
    int temp;
    for (int i = 0; i < n - 1; i++) {
        int mix = i;
        for (int j = i + 1; j < n; j++) {
            if (data[j] < data[mix]) {
                mix = j;
            }
        }
        if (i != mix) {
            temp = data[i];
            data[i] = data[mix];
            data[mix] = temp;
        }
    }
}

 插入排序

通常整理桥牌的方法是一张一张的来,将每一张插入到其他已经尤须的牌中的适当位置。计算机中,为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。

void bInsertSort(int data[], int n) {
    int low, high, mid;
    int temp;
    for (int i = 1; i < n; i++) {
        low = 0;
        //把data[i]元素插入到它的前面data[0-(i-1)]中
        temp = data[i];
        high = i - 1;
        //该while是折半,缩小data[i]的范围(优化手段)
        while (low <= high) {
            mid = (low + high) / 2;
            if (data[mid] > temp) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        int j = i;
        //让data与已经排序好的数组的各个元素比较,小的放前面
        while ((j > low) && data[j - 1] > temp) {
            data[j] = data[j - 1];
            --j;
        }
        data[low] = temp;
    }
}

希尔排序

插入排序对大规模乱序数组排序很慢,因为它只会交换相邻的元素,元素只能一点一点从一端移动到另一端。希尔排序为了加快速度简单的改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

void shellSort(int * data, int n) {
    int step, i, j, key;
    //将数组按照step分组,不断二分到每组只剩下一个元素
    for (step = n / 2; step > 0; step /= 2) {
        //将每组中的元素排序,小的在前
        for (i = step; i < n; i++) {
            key = data[i];
            for (j = i - step; j >= 0 && key < data[j]; j -= step) {
                data[j + step] = data[j];
            }
            //和上面的for循环一起,将组中小的元素换到数组的前面
            data[j + step] = key;
        }
    }
}

归并排序

(递归地)将它分成两半分别排序,然后将结果归并起来。保证将任意长度为N的数组排序所需时间和NlogN成正比。缺点则是需要额外空间和N成正比。

void mergeSort(int data[], int first, int last) {
    int mid = 0;
    //将数组不停的二分分组再组合,直到每组只剩一个元素
    if (first < last) {
        mid = (first + last) / 2;
        mergeSort(data, first, mid);
        mergeSort(data, mid + 1, last);
        merge(data, first, mid, last);
    }
    return;
}
void merge(int data[], int low, int mid, int high) {
    int i, k;
    //定义一个临时数组存放传进来的无序数组排好序之后的数组
    int *temp = (int *) malloc((high - low + 1) * sizeof(int));
    //将无序数组分成两个序列
    int left_low = low;
    int left_high = mid;
    int right_low = mid + 1;
    int right_high = high;
    //将两个序列比较排序,小的排前
    for (k = 0; left_low <= left_high && right_low <= right_high; k++) {
        if (data[left_low] <= data[right_low]) {
            temp[k] = data[left_low++];
        } else {
            temp[k] = data[right_low++];
        }
    }
    //左序列如果有剩下元素未排序,加到临时数组的末尾
    if (left_low <= left_high) {
        for (i = left_low; i <= left_high; i++) {
            temp[k++] = data[i];
        }
    }
    //右序列如果有剩下元素未排序,加到临时数组的末尾
    if (right_low <= right_high) {
        for (i = right_low; i <= right_high; i++) {
            temp[k++] = data[i];
        }
    }
    //将排好序的小分组转移到原数组中
    for (i = 0; i < high - low + 1; i++) {
        data[low + i] = temp[i];
    }
    free(temp);
    return;
}

快速排序

基本算法

快速排序是一种分治的排序算法。将一个数组分成两个子数组,将两部分独立地排序。快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序;而快速排序方式则是当两个子数组都有序时数组也就自然有序了。

优先队列

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (largest-in,first-out)的行为特征。

堆的定义

数据结构二叉堆能够很好的实现优先队列的基本操作。

排序算法的性能特点

 

符号表

符号表是一种存储键值对的数据结构,支持两种操作:插入(put),查找(get)

二分查找(基于有序数组)

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

二叉查找树

一颗二叉查找树(BST)是一颗二叉树,其中每个节点都含有一个Comparable的键(以及相关的值)且每个结点的键都大于其左子树中的任意结点的键,而小于右子树的任意结点的键;

简单的符号表实现的成本总结

 

平衡查找树

定义

父节点的左子树和右子树的高度之差不能大于1

2-3查找树

定义

2-3树运行每个节点保存1个或者两个的值。对于普通的2节点(2-node),他保存1个key和左右两个自己点。对应3节点(3-node),保存两个Key,2-3查找树的定义如下:

  1. 要么为空,要么:
  2. 对于2节点,该节点保存一个key及对应value,以及两个指向左右节点的节点,左节点也是一个2-3节点,所有的值都比key要小,右节点也是一个2-3节点,所有的值比key要大。
  3. 对于3节点,该节点保存两个key及对应value,以及三个指向左中右的节点。左节点也是一个2-3节点,所有的值均比两个key中的最小的key还要小;中间节点也是一个2-3节点,中间节点的key值在两个跟节点key值之间;右节点也是一个2-3节点,节点的所有key值比两个key中的最大的key还要大。

如果中序遍历2-3查找树,就可以得到排好序的序列。在一个完全平衡的2-3查找树中,根节点到每一个为空节点的距离都相同。

散列表

散列表(也叫哈希表),是根据键而直接访问在内存存储位置的数据结构。

散列函数 

Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

各种符号表实现的渐进性能的总结

 

无向图

边没有方向的图称为无向图

有向图

有向图是由一组顶点和一组有方向的边组成的,每条有方向的边都连接着有序的一对顶点。

有向图中,一个顶点的出度为由该顶点指出的边的总数;一个顶点的入度为指向该顶点的边的总数。

有向图中,有向路径由一系列顶点组成,对于其中的每个顶点都存在一条有向边从它指向序列中的下一个顶点。有向环为一条至少含有一条边且起点和终点相同的有向路径。简单有向环是一条(除了起点和终点)不含重复顶点和边的环。路径或者环的长度即为其中包含的边数。

最小生成树

现在假设有一个很实际的问题:我们要在n个城市中建立一个通信网络,则连通这n个城市需要布置n-1一条通信线路,这个时候我们需要考虑如何在成本最低的情况下建立这个通信网? 
于是我们就可以引入连通图来解决我们遇到的问题,n个城市就是图上的n个顶点,然后,边表示两个城市的通信线路,每条边上的权重就是我们搭建这条线路所需要的成本,所以现在我们有n个顶点的连通网可以建立不同的生成树,每一颗生成树都可以作为一个通信网,当我们构造这个连通网所花的成本最小时,搭建该连通网的生成树,就称为最小生成树。

最短路径

用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。

跳表

跳表是一个随机化的数据结构,可以被看做二叉树的一个变种,它在性能上和红黑树,AVL树不相上下,但是跳表的原理非常简单,目前在Redis和LeveIDB中都有用到。
它采用随机技术决定链表中哪些节点应增加向前指针以及在该节点中应增加多少个指针。跳表结构的头节点需有足够的指针域,以满足可能构造最大级数的需要,而尾节点不需要指针域。
采用这种随机技术,跳表中的搜索、插入、删除操作的时间均为O(logn),然而,最坏情况下时间复杂性却变成O(n)。相比之下,在一个有序数组或链表中进行插入/删除操作的时间为O(n),最坏情况下为O(n)。
 
数据结构模型

 

Queue

什么是队列

队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列),跟我们生活中的排队类似。

队列的种类

  • 单队列(单队列就是常见的队列, 每次添加元素时,都是添加到队尾,存在“假溢出”的问题也就是明明有位置却不能添加的情况)
  • 循环队列(避免了“假溢出”的问题)

Java 集合框架中的队列 Queue

Java 集合中的 Queue 继承自 Collection 接口 ,Deque, LinkedList, PriorityQueue, BlockingQueue 等类都实现了它。 Queue 用来存放 等待处理元素 的集合,这种场景一般用于缓冲、并发访问。 除了继承 Collection 接口的一些方法,Queue 还添加了额外的 添加、删除、查询操作。

Set

什么是 Set

Set 继承于 Collection 接口,是一个不允许出现重复元素,并且无序的集合,主要 HashSet 和 TreeSet 两大实现类。

在判断重复元素的时候,HashSet 集合会调用 hashCode()和 equal()方法来实现;TreeSet 集合会调用compareTo方法来实现。

补充:有序集合与无序集合说明

  • 有序集合:集合里的元素可以根据 key 或 index 访问 (List、Map)
  • 无序集合:集合里的元素只能遍历。(Set)

HashSet 和 TreeSet 底层数据结构

HashSet 是哈希表结构,主要利用 HashMap 的 key 来存储元素,计算插入元素的 hashCode 来获取元素在集合中的位置;

TreeSet 是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序;

List

什么是List

在 List 中,用户可以精确控制列表中每个元素的插入位置,另外用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。 与 Set 不同,List 通常允许重复的元素。 另外 List 是有序集合而 Set 是无序集合。

List的常见实现类

ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。

LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率高。

Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。

Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。相关阅读:java数据结构与算法之栈(Stack)设计与实现

Map

1 二叉树

二叉树(百度百科)

(1)完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。

(2)满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

(3)平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

2 完全二叉树

完全二叉树(百度百科)

完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。

3 满二叉树

满二叉树(百度百科,国内外的定义不同)

国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。

数据结构之堆的定义

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

4 二叉查找树(BST)

浅谈算法和数据结构: 七 二叉查找树

二叉查找树的特点:

  1. 若任意节点的左子树不空,则左子树上所有结点的 值均小于它的根结点的值;
  2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点(no duplicate nodes)。

5 平衡二叉树(Self-balancing binary search tree)

平衡二叉树(百度百科,平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等)

6 红黑树

红黑树特点:

  1. 每个节点非红即黑;
  2. 根节点总是黑色的;
  3. 每个叶子节点都是黑色的空节点(NIL节点);
  4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
  5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

红黑树的应用:

TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。

为什么要用红黑树?

简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 漫画:什么是红黑树?(也介绍到了二叉查找树,非常推荐)

推荐文章:

7 B-,B+,B*树

二叉树学习笔记之B树、B+树、B*树

《B-树,B+树,B*树详解》

《B-树,B+树与B*树的优缺点比较》

B-树(或B树)是一种平衡的多路查找(又称排序)树,在文件系统中有所应用。主要用作文件的索引。其中的B就表示平衡(Balance)

  1. B+ 树的叶子节点链表结构相比于 B- 树便于扫库,和范围检索。
  2. B+树支持range-query(区间查询)非常方便,而B树不支持。这是数据库选用B+树的最主要原因。
  3. B*树 是B+树的变体,B*树分配新结点的概率比B+树要低,空间使用率更高;

8 LSM 树

[HBase] LSM树 VS B+树

B+树最大的性能问题是会产生大量的随机IO

为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。

LSM树由来、设计思想以及应用到HBase的索引

BFS及DFS

 

posted on 2018-05-25 13:53  1zfang1  阅读(181)  评论(0编辑  收藏  举报

导航