整理:数据结构、算法

知识点

查找
平均时间复杂度
查找条件
算法描述
顺序查找
O(n)
无序或有序队列
按顺序比较每个元素,直到找到关键字为止
二分查找(折半查找)
O(logn)
有序数组
查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。 如果在某一步骤数组为空,则代表找不到。
二叉排序树查找
O(logn)
二叉排序树
在二叉查找树b中查找x的过程为:1. 若b是空树,则搜索失败2. 若x等于b的根节点的数据域之值,则查找成功;3. 若x小于b的根节点的数据域之值,则搜索左子树4. 查找右子树。
哈希表法(散列表)
O(1)
先创建哈希表(散列表)
根据键值方式(Key value)进行查找,通过散列函数,定位数据元素。
分块查找
O(logn)
无序或有序队列
将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,……。然后使用二分查找及顺序查找。

1 直接插入排序:

2 希尔排序:

<span style="font-weight: bold;"--<

3 简单选择排序:

4 堆排序:

<span style="font-weight: bold;"--<

5 冒泡排序:

6 快速排序:

<span style="font-weight: bold;"--<

7 归并排序:

<span style="font-weight: bold;"--<

8 桶排序:

9 基数排序:

10 计数排序:

二分查找

二叉树遍历

归并排序参考 http://blog.csdn.net/morewindows/article/details/6678165/

八大排序参考 http://blog.csdn.net/hguisu/article/details/7776068

排序算法稳定性:定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

这效率,惊呆了

快速排序空间复杂度平均是O(NlogN):http://harttle.com/2015/09/27/quick-sort.html

归并排序的空间复杂度是O(N):http://blog.csdn.net/yuzhihui_no1/article/details/44223225

归并排序在规模较大时比希尔和堆排序好;堆排序适合大数据;快排效率最高但大数据不行:http://blog.csdn.net/p10010/article/details/49557763

=====================================================

重点排序:

希尔排序          增量序列可取n/2

堆排序

快速排序

归并排序

代码示例: https://gist.github.com/HsingPeng/1980c786afd8c1a7fc169abdc4db66d6

堆:完全二叉树

二叉树

定义:二叉树是一个连通的无环图,并且每一个顶点的度不大于3

性质:

1 深度K的二叉树 最多2^k-1个节点

2 第i层的节点总数不超过 ,, i>=1

3 如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1

4 有n个结点的完全二叉树的深度为

5 有N个结点的完全二叉树各结点如果用顺序方式存储:

          若I为结点编号则 如果I>1,则其父结点的编号为I/2

          如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;(从0计算:2*I +1)

          如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。(从0计算:2*I +2)

6 给定N个节点,能构成h(N)种不同的二叉树。h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。

卡特兰数?h(n) = h(1)*h(n-1) + h(2)*h(n-2) + ... + h(n-1)h(1),n>=2

括号匹配问题

========================================================

2017-5-18 12:47:28

重点整理:

包括:基本思想、复杂度、优缺点、场景

重点排序:快排、堆排序、归并排序

排序示例代码:https://gist.github.com/HsingPeng/1980c786afd8c1a7fc169abdc4db66d6

树的遍历示例代码:https://gist.github.com/HsingPeng/592db15b48753b177722541e201ff279

1 快速排序:

基本思想:

选择一个基准元素,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,

然后再按此方法对这两部分数据分别进行快速排序,

整个排序过程可以递归进行,以此达到整个数据变成有序序列。

复杂度:

平均O(n log n)     最好O(n log n)     最差n^2     空间O(n log n)     不稳定

优点:

目前基于比较的内部排序中被认为是最好的方法。

当待排序的关键字是随机分布时,快速排序的平均时间最短。

住快排一般情况下常数项小,复杂度相同但是消耗更小

缺点:

受基准元素影响大

时间复杂度不稳定,最差情况时间长。

场景:

一般情况或初始状态无序

2 堆排序:

基本思想:

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。

堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

复杂度:

平均O(n log n)     最好O(n log n)     最差O(n log n)    空间O(1)     不稳定

优点:

排序速度稳定,不需要额外空间

数据量越大排序越快

缺点:

堆排序在建立堆和调整堆的过程中会产生比较大的开销

场景:

堆排序在建立堆和调整堆的过程中会产生比较大的开销,在元素少的时候并不适用。

但是,在元素比较多的情况下,还是不错的一个选择。尤其是在解决诸如“前n大的数”一类问题时,几乎是首选算法。

3 归并排序:

基本思想:

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

复杂度:

平均O(n log n)     最好O(n log n)     最差O(n log n)    空间O(n log n)     稳定

优点:

归并排序是稳定的

缺点:

常数比快排略大,需要额外空间

场景:

大数据,外部排序

重点查找:二分查找、散列表、树(二叉树、B树、B+树、B*树、红黑树)

1 二分查找(线性表

基本思想:

假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

复杂度:

最差O(log n)

优点:

比较次数少,查找速度快,平均性能好;

缺点:

要求待查表为有序表,且插入删除困难。

场景:

适用于不经常变动而查找频繁的有序列表。

2 散列表

基本思想:

散列表是根据关键码值(Key value)而直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。

最关键的两点:哈希函数和冲突处理

冲突处理:

  1. 开放地址法                其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi
  2. 链地址法(拉链法)     将哈希地址相同的元素构成一个单链表

复杂度:

O(1)

优点:

实现了随机访问,所以性能比较快。

缺点:

当一个查找关键字对应多个散列地址;需要查找一个范围时(模糊查询),效果不好。

需要处理散列冲突。

散列冲突:不同的关键字经过散列函数的计算得到了相同的散列地址。

场景:

在海量数据处理中有着广泛应用

3 树

3.1 二叉查找树(BST

  1. 所有非叶子结点至多拥有两个儿子(Left和Right);
  2. 所有结点存储一个关键字;
  3. 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;

查找平均时间复杂度O(log n)     最坏情况退化成链表 O(n)

插入一个结点的代价与查找一个不存在的数据的代价完全相同,O(log n)O(n)

删除时间复杂度,最少O(1),最大不会超过O(logN)

3.2 平衡二叉搜索树(AVL树)

平衡二叉树或为空树,或为如下性质的二叉排序树:

  1. 左右子树深度之差的绝对值不超过1;
  2. 左右子树仍然为平衡二叉树.

    查找时间复杂度保持在O(log n)

    插入时间复杂度在O(logN)左右

    删除时间复杂度O(2logN)

    3.3 红黑树(RBT树)

    红黑树是一种非严格平衡的二叉查找树

    1. 每个结点要么是红的,要么是黑的。
    2. 根结点是黑的。
    3. 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
    4. 如果一个结点是红的,那么它的俩个儿子都是黑的。
    5. 对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。

      含有n个节点的红黑树的高度至多为2*log(n+1),它包含的内节点个数至少为2^(h/2)-1个。

      红黑树能保证在最坏情况下,基本的动态几何操作的时间均为O(logn)

      3.4 平衡多路查找树(B树、B-树、B~树)

      一颗m阶的B树特性如下:

      (1)树中每个节点最多含有m个孩子(m ≥ 2);

      (2)除根结点和叶节点外,其他每个节点至少有ceil(m/2)个孩子

      (3)根节点至少有两个孩子(除非B树只有一个节点-根节点);

      (4)所有叶节点出现在同一层,叶节点不包含任何关键字信息(每一个NULL指针即当做叶子结点);

      (5)每个非终端节点含有n个关键字,即(n,P0,K1,P1,K2,P2,...,Kn,Pn)。其中Ki(i = 1,2,...,n)为关键字,且关键字按顺序升序排列Ki-1且指针Pi-1指向子树中的所有节点的关键字均小于Ki且都大于Ki-1,关键字的个数n必须满足ceil(m/2)-1≤n≤m-1。比如,有j个孩子的非叶节点恰好有j-1个关键字。

      3.5 B+树

      一棵m阶的B+树和m阶的B树的异同点在于:

      - 有n棵子树的结点中含有n个关键字; (有些资料上说与B 树n棵子树有n-1个关键字 保持一致,无关紧要,只是实现不同)

      - 所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (而B 树的叶子节点并没有包括全部需要查找的信息)

      - 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)

      B+树的磁盘读写代价更低

      B+树的查询效率更稳定支持基于范围的查询

      3.6 B*树

      在B+树的基础上(所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针),B*树中非根和非叶子结点增加指向兄弟的指针;B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)。

      B*树分配新结点的概率比B+树要低,空间使用率更高

      总结如下:

      - B树:有序数组+平衡多叉树;

      - B+树:有序数组链表+平衡多叉树;

      - B*树:一棵丰满的B+树

      • B树(二叉):二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
      • B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
      • B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
      • B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;

      顺便说一句,无论是B树,还是B+树、b树,由于根或者树的上面几层被反复查询,所以这几块可以存在内存中,换言之,B树、B+树、B树的根结点和部分顶层数据在内存中,大部分下层数据在磁盘上

      其他:队列、栈、图

      资料:

      数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

      http://blog.csdn.net/sup_heaven/article/details/39313731

      二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)的比较

      http://blog.csdn.net/bingjing12345/article/details/7830474

      ========================================================

      有些概念:

      分治、贪心、动态规划

      动态规划

      1 讲解

      1.1 讲解1:七月在线--8月面试求职班

      动态规划可以看成递归

      动态规划 定义

      • 原问题 -> 子问题 -> 原问题

      • 最优子结构

      • 子问题最优决策可导出原问题最优决策

      • 无后效性

      • 重叠子问题

      • 去冗余

      • 空间换时间

      动态规划有两个非常重要的性质,最优子结构重叠子问题

      动态规划就是去冗余

      问题共性

      • 套路

           • 最优,最大,最小,最长,计数

      • 离散问题

           • 容易设计状态(整数01背包问题)

      • 最优子结构

           • N-1可以推导出N

      直观的理解,只考虑整数,不考虑小数,就是离散。

      题目的状态规模是有限制的

      基本步骤

      • 四个步骤

      • 设计暴力算法,找到冗余

      • 设计并存储状态(一维,二维,三维数组)

      • 递归式(状态转移方程)

      • 自底向上计算最优解(编程方式)

      实例:

      https://leetcode.com/problems/house-robber

      斐波那契数列

      N!

      小兵向前冲

      01背包问题

      最长公共子序列

      1.2 讲解2:《编程之法》

      动态规划一般用来求解最优化问题,其适用条件是要求待求解的最优化问题具备两个因素:最优子结构子问题重叠。通过求解一个个最优子问题将解存入一张表中,当后续子问题的求解需要用到之前子问题的解时直接查表,每次查表的代价为常数时间。

      与贪心法的区别:

      贪心法是特殊的动态规划(子问题不重叠),贪心法是“最优子结构且局部最优”,动态规划是“最优独立重叠子结构且全局最优”

      求解过程关键:

      1. 状态的定义
      2. 方案的枚举(仅枚举必要的)
      3. 最优子问题(一个状态的最优值取决于达到这个状态的全部子状态最优值) 和无后效性保证(状态之间的最优值不互相影响)
      4. 子问题有重叠
      5. 编写状态转移方程(当前节点的值完全由其前驱结点确定)

      实例:

      最大连续乘积子数组

      从N个整数中找了(n-1)个元素乘积最大的那一组

      字符串编辑距离

      格子取数问题

      交替字符串

      1.3 讲解3:《剑指Offer》第二版

      如果能够把小问题的最优解组合起来能够得到整个问题的最优解,就可以用动态规划。

      特点1:求一个问题的最优解

      特点2:整体问题的最优解是依赖各个子问题的最优解

      特点3:小问题之间还有相互重叠的更小的子问题

      特点4:从上往下分析问题,从下往上求解问题

      贪婪算法和动态规划不一样,贪婪的每一步都能确定是最优解。

      所以应用贪婪时,需要用数学方式证明贪婪选择是正确的。

      实例:

      剪绳子(第二版)

      连续子数组的最大和(第一版)

      2 问题举例

      2.1 问题一:连续子数组的最大和 《剑指Offer》第二版

          /**

           * 连续子数组的最大和

           *

           * 方法一:数组规律

           * @param array

           * @return

           */

           public static int maxSubString(int[] array) {

               int max = Integer.MIN_VALUE;

               int last = 0;

               for (int p : array) {

                   if (last >= 0) {

                       last = last + p;

                   } else {

                       last = p;

                   }

                   if (max < last) {

                       max = last;

                   }

               }

               return max;

           }

          /**

           * 连续子数组的最大和

           *

           * 方法二:动态规划

           * @param array

           * @return

           */

          public static int maxSubString1(int[] array) {

              int max = Integer.MIN_VALUE;

              int last = 0;

              for (int p : array) {

                  last = Math.max(p, last + p);

                  max = Math.max(max, last);

              }

              return max;

          }

      2.2 问题二:最大连续乘积子数组 《编程之法》

           /**

            * 最大连续乘积子数组

            *

            * 比如:{-2.5, 4, 0, 3, 0.5, 8, -1} 最大连续是 3 0.5 8

            *

            * @param array

            * @return

            */

          public static double maxSubProduct(double[] array) {

              double max = array[0];

              double lastMin = array[0];

              double lastMax = array[0];

              for (int i = 1 ; i < array.length ; i++) {

                  double current = array[i];

                  lastMin = Math.min(Math.min(current * lastMin, current * lastMax), current);

                  lastMax = Math.max(Math.max(current * lastMax, current * lastMin), current);

                  if (lastMax > max) {

                      max = lastMax;

                  }

              }

              return max;

          }

      2.3 问题三:最长递增子序列(LIS)

      最长递增子序列 O(NlogN)算法

      https://www.felix021.com/blog/read.php?1587

      nlogn

      最长递增子序列

      https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/05.06.md

      n^2

          /**

           * 最长递增子序列

           *

           * 比如:

           * {2, 1, 4, 3, 1, 5, 6} 返回: 4

           * [157,232,6] 返回 2

           * @param A

           * @return

           */

          public static int findLongest(int[] A) {

              int max = 0;

              int[] last = new int[A.length];

              last[0] = 1;

              for (int i=1 ; i<A.length ; i++) {

                  int current = 0;

                  for (int j=0 ; j<i ; j++) {

                      int temp = 0;

                      if (A[j] < A[i]) {

                          temp = last[j] + 1;

                      } else {

                          temp = 1;

                      }

                      if (temp > current) {

                          current = temp;

                      }

                  }

                  last[i] = current;

                  if (max < last[i]) {

                      max = last[i];

                  }

              }

              return max;

          }

      2.4 问题四:分装粉笔 360 2018 秋招笔试

      小明一共有n根彩色粉笔,m根白色粉笔,

      现在可以用a根彩色粉笔和b根白色粉笔组成一盒卖x元,

      或者c根白色粉笔组成一盒卖y元,或

      者d根彩色粉笔组成一盒卖z元,

      小明最多可以用这些粉笔卖多少元?不一定要把粉笔卖完,小明只希望利益最大化。

      输入:

      n m

      a b c d

      x y z

      输入样例:

      5 15

      1 2 3 6

      2 1 3

      输出样例:

      11

      ========================================================

发表评论

邮箱地址不会被公开。 必填项已用*标注