美文网首页
数据结构 经典算法复习

数据结构 经典算法复习

作者: ZalleDay | 来源:发表于2019-03-22 19:37 被阅读0次
public class DataStruct {
    public static void main(String[] args){
        DataStruct dataStruct = new DataStruct();
        int[]  arr = {3,1,8,5,9,6,2,4};
      //  dataStruct.insertion_sort(arr,arr.length);
      //  dataStruct.shellSort(arr,arr.length);
      //  dataStruct.bubbleSort(arr);
       // dataStruct.mergeSort(arr,0,arr.length-1);
          dataStruct.heapSort(arr,arr.length);
      //  dataStruct.quikSort(arr,0,arr.length-1);
      //  dataStruct.heapSort2(arr,arr.length - 1);
          dataStruct.print(arr);

    }


    public void print(int[] arr){
        for (int i = 0; i < arr.length; i++)
            System.out.print(arr[i] + " ");
        System.out.println(" ");
    }

    //插入排序
    /*
    * 先选择从坐标1开始  每选择一个i值  就把0到i值区间排序  
*我们找到对应的位置  把比target大的值向前移动一个位置  在进行插入
    * */
    public void insertion_sort(int arr[], int length){
        int i,j;
        for (i = 1; i < length; i++){
            int tmp = arr[i];
            for (j = i; j > 0 && tmp < arr[j-1]; j--){
                arr[j] = arr[j-1];
            }
            arr[j] = tmp;
        }
    }

    //希尔排序
    /**
     *这个是插入排序的修改版,根据步长由长到短分组,进行排序,直到步长为1为止,属于插入排序的一种。
     * 先由步长分组  每一组使用我们的插入排序
     */

    public void shellSort(int arr[], int length){
        for (int gap = length / 2; gap > 0; gap /= 2){

            for (int i = gap; i < length; i++){
                int tmp = arr[i];
                int j;
                for (j = i; j >= gap &&  arr[j - gap] > tmp; j -= gap){
                    arr[j] = arr[j-gap];
                }
                arr[j] = tmp;
            }
        }

    }

    //冒泡排序
    //相对优雅的冒泡  有一个值来提前判断要不要停止排序
    public void bubbleSort(int[] arr) {
        boolean swapp = true;

        for (int j = 1; j < arr.length; j++) {
            swapp = false;
            for (int i = 0; i < arr.length - j; i++) {
                if (arr[i] > arr[i + 1]) {
                    arr[i] += arr[i + 1];
                    arr[i + 1] = arr[i] - arr[i + 1];
                    arr[i] -= arr[i + 1];
                    swapp = true;
                }
            }
            if (swapp == false) break;
        }

    }

    //归并排序
    /*
    *用二分的手法  直接先分后后和
    * */
    public void mergeSort(int arr[], int l, int r){  //归并也是双闭合
        if (r > l){
            int mid = l +  (r - l) / 2;
            mergeSort(arr,l,mid);
            mergeSort(arr,mid+1,r);
            merge(arr,l,mid,r);

        }
    }

    public void merge(int[] arr,int l, int mid,int r){ // l ~ mid  mid+1 ~ r
        int i = 0;
        int j = 0;

        int n1 = mid - l + 1; //先拿到n1 的数量
        int n2 =  r - mid;      //先分别取两数组长度

        int[] left = new int[n1];
        int[] right = new int[n2];

        for (int x = 0; x < left.length; x++)
            left[x] = arr[x + l];
        for (int x = 0; x < right.length; x++)
            right[x] = arr[x + mid + 1];

        int t = l;
        while (i < left.length && j < right.length){
            if (left[i] < right[j])
                arr[t++] = left[i++];
            else
                arr[t++] = right[j++];

        }
        while (i < left.length)
            arr[t++] = left[i++];
        while (j < right.length)
            arr[t++] = right[j++];

    }

    public void swap(int[] nums, int index1, int index2){
        int tmp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = tmp;

    }

    //堆排序   最大堆的最大元素在根节点   堆中每个父节点比子节点大
    /**
     *  这里我们的堆排序的起始点从  0 开始  
所以他的左子节点的序号是 2 * i +1 右结点的序号是  2  * i + 2
     *  如果从起始点从1 开始的话  我们的左子节点为  2 * i   右节点为 2 * 1;
     *
     */

    public void heapify(int arr[], int len, int index){  
//index 代表 parent  建立堆  不符合递归调用
        int value = arr[index];

        for (int j = 2 * index + 1; j < len; j = j * 2 + 1){
            if (j < len -1 && arr[j] < arr[j + 1]) j++;  //比较左右结点大小  找出最大的
            if (value > arr[j]) break;
            arr[index] = arr[j];
            index = j; //记录下来 value 应该放置的位置
        }
        arr[index] = value;

    }


    public  void heapSort(int arr[], int n) {
        // 建立堆
        for (int i = arr.length / 2 - 1 ; i >= 0; i--)  //调整为堆
            heapify(arr, arr.length, i);


        // 一个个从堆顶取出元素
        for (int i = arr.length-1; i>= 0; i--)
        {
            System.out.print(arr[0] + "  ");
            swap(arr,0,i);      //取
            heapify(arr, i, 0);     //取完之后需要重建堆
        }
        System.out.println("\n");

    }

    //起始结点从 1 开始  len为数组长度
    public void heapify2(int arr[], int len, int index) {
        int largestIndex = index;
        int left = 2 * index;
        int right = 2 * index + 1;

        if (len >= left && arr[left] > arr[largestIndex]) {
            largestIndex = left;
        }

        if (len >= right && arr[right] > arr[largestIndex])
            largestIndex = right;

        if (index != largestIndex) {
            swap(arr, largestIndex, index);

            heapify2(arr, len, largestIndex);
        }
    }


    //注意边界条件  从0 开始是 n /2   1 开始是n / 2 - 1  这是从1 开始
    public  void heapSort2(int[] arr , int len){
        for (int i = len / 2 ; i >= 1; i--)
            heapify2(arr,len,i);


             //取堆顶
        for (int i = len; i >= 1; i--){
            System.out.print(arr[1] +"  ");
            swap(arr,1,i);
            heapify2(arr,i-1,1);
        }
        System.out.println("\n");


    }


        public void quikSort(int[] nums,int  begin, int end){  //左闭右闭   快排必须双闭合
        if (end > begin){
            int index = qSort(nums,begin,end);

            quikSort(nums,begin,index-1);
            quikSort(nums,index+1,end);
        }

    }

    public int qSort(int[] nums,int begin, int end){
        int value = nums[begin];
        while (begin < end) {
            while (begin < end && nums[end] >= value)
                end--;
            if (begin < end)
                nums[begin++] = nums[end];
            while (begin < end && nums[begin] <= value)
                begin++;
            if (begin < end)
                nums[end--] = nums[begin];
        }
        nums[begin] = value;
        return begin;
    }

}


二叉搜索树, 平衡二叉树(AVL) 红黑树 B树(平衡多路搜索树) B+树(在B树上改造)

二叉搜索树

二叉排序树(Binary Sort Tree),又称[二叉查找树(Binary Search Tree),亦称[二叉搜索树]
二叉搜索树,主要特点是左节点比根节点小,右节点比根节点大,并且左右子树都是二叉搜索树。缺点是在极端情况下,比如插入都是有序的,就会出现退化的情况有序序列树退化成链表。
左小右大

image.png

二叉平衡树 AVL

在二叉搜索树的基础加上一规则
左子树的深度和右子树的深度只差 不大于1

红黑树

红黑树红黑树让树尽可能平衡,就是为了降低树的高度。红黑树会比AVL树多一层,但是它具有稳定的优点.
特点:

  • 每个节点要么是红色,要么是黑色。
  • 根节点必须是黑色
  • 红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)。
  • 对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点。
image.png

B树

树是一种平衡多路搜索树,他的每个节点可以拥大于等于2个子节点,M路的B树最多能拥有M个子节点,一个节点中有 m 个子节点则存在 m-1 个记录,记录按照递增次序进行排列,叶节点都在同一层上。B树之所以多路(也就是每个节点上可存多个记录)是为了降低高度,路数越多,树高度越低,查询性能也高。但也不能是无限的,否则就退化成有序数组了。

image.png

B+树

B+树是在B树基础上进行改造,他的数据都在叶子结点,同时叶子结点之间还加了指针形成一个链表。

image.png

Q0.数据库索引有哪些,优缺点?

hash索引和B+树索引
hash索引等值查询效率高,但是不能排序,因此不能进行范围查询
B+树索引数据有序,能够进行范围查询

Q1.为什么不用二叉查找树作为数据库索引?

二叉查找树,查找到指定数据,效率其实很高logn。但是数据库索引文件有可能很大,关系型数据存储了上亿条数据,索引文件大则上G,不可能全部放入内存中,
而是需要的时候换入内存,方式是磁盘页。一般来说树的一个节点就是一个磁盘页。如果使用二叉查找树,那么每个节点存储一个元素,查找到指定元素,需要进行大量的磁盘IO,效率很低。
而B树解决了这个问题,通过单一节点包含多个data,大大降低了树的高度,大大减少了磁盘IO次数。

Q2.B树和二叉查找树的性能对比?

B树包括B+树的设计思想都是尽可能的降低树的高度,以此降低磁盘IO的次数,因为一个索引节点就表示一个磁盘页,页的换入换出次数越多,表示磁盘IO次数越多,越低效。
B树算法减少定位数据所在的节点时所经历的磁盘IO次数,从而加快存取速度。
假设一个节点可以容纳100个值,那么3层的B树可以容纳100万个数据。(根节点100值,第二层可以存储99个节点(k-1),也就是99100 个值,第三层可以存储
(99
100-1)*100)结果是近似100万个数据。而如果使用二叉查找树,则需要将近20层,也就是进行20次磁盘IO,性能差距如此之大。
如mongoDB数据库使用,单次查询平均快于Mysql(但侧面来看Mysql至少平均查询耗时差不多)。

Q3.B+对比B树的优点?

因为B树的每个节点除了存储指向子节点的索引之外,还有data域,因此单一节点存储的指向子节点的索引并不是很多,树高度较高,磁盘IO次数较多,
而B+树单一节点存储的指向子节点的索引更多,B+树空间利用率高,因此B+树高度更低,磁盘IO次数更少,性能更好。
因为B树的中间节点存储了数据,所以整个树的每一层都有可能查找到要查找的数据,查询性能不稳定,
而B+树所有的data都存储在叶子节点,且叶子节点位于同一层,因此查询性能稳定。
B树如果想要进行范围查找,需要频繁的进行二叉树的中序遍历,进行范围查找比较复杂,
B+树要查找的元素都位于叶子节点,且连接形成有序链表,便于范围查找。

Q4.B树,B+树使用场景。

B树主要用于文件系统,和部分数据库索引,如文档型数据库mongodb
B+树主要用于mysql数据库索引。

Q5.为什么数据库索引不用红黑树而用B+树?

红黑树当插入删除元素的时候会进行频繁的变色与旋转(左旋,右旋),来保证红黑树的性质,浪费时间。
但是当数据量较小,数据完全可以放入内存中,不需要进行磁盘IO,这时候,红黑树时间复杂度比B+树低。
比如TreeSet TreeMap 和HashMap (jdk1.8)就是使用红黑树作为底层数据结构。

总结

  • AVL树是二叉搜索树的优化,左右深度不大于1.,但出现极端情况就为成为链表
  • 红黑树为了解决AVL树的极端情况情况,虽然多在AVL树多加一层搜索,但是可以保持稳定性,让节点尽可能均匀分布.红黑树不太适合由于写操作多的环境,旋转太耗时.
  • B树一般用于文件的存储.类似我们的文件系统,不需要在同层的节点建立指针,B树的搜索时间复杂度为NlogN.
    优点,可以优化树的深度,假如一个节点存100个数据,而三层可以容纳接近100万个数据.B树可以用于稀疏的数据.
  • B+树可以有了B树的优点,还能在同层的节点之中搜索.
  • 数据库的索引右B+树索引和哈希索引,哈希索引用于快速查找,B+树由于范围搜索.B+树适合用于稠密的数据

这是一篇说B树 B+树的文章
https://blog.csdn.net/u013411246/article/details/81088914

相关文章

  • 数据结构 经典算法复习

    二叉搜索树, 平衡二叉树(AVL) 红黑树 B树(平衡多路搜索树) B+树(在B树上改造) 二叉搜索树...

  • 刷书

    重点复习数据结构,算法。大话数据结构,算法第四版,牛客刷题,剑指offer题,LeetCode,牛客算法课 计算机...

  • 【JAVA】复习数据结构——顺序表

    最近需要复习数据结构和算法,数据结构曾经上课好好学过的,不过现在很多都忘记了,所以决定专门开个专题给数据结构和算法...

  • 数据结构与算法 (Kotlin语言描述) / 陈光剑

    《数据结构与算法 (Kotlin语言描述)》/ 陈光剑 内容简介 本书主要介绍基本数据结构以及相关的经典算法,强调...

  • KMP算法及优化

    转载请注明出处: KMP算法及优化 今天看到同学在复习数据结构书上的KMP算法,忽然发觉自己又把KMP算法忘掉了,...

  • day003_python列表和元组

    1. 程序设计 经典说法:程序设计 = 数据结构 + 算法 实际工程:基本逻辑 + 数据结构 + 设计思路 2. ...

  • Swift的十大经典排序算法总结

    Swift的十大经典排序算法总结 排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为内部排序和外部排...

  • 思维导图之数据结构+算法

    数据结构+算法 = 程序 数据结构比较 参考文章 数据结构与算法数据结构与算法(java)

  • 数据结构与算法--一(概念偏)

    1.0 数据结构的基础概念 1.0.1 为什么要学习数据结构 在计算机界流行着一句经典名言"数据结构+算法=程序设...

  • 数据结构与算法 - 查找

    数据结构与算法系列文章数据结构与算法 - 时间复杂度数据结构与算法 - 线性表数据结构与算法 - 树形结构数据结构...

网友评论

      本文标题:数据结构 经典算法复习

      本文链接:https://www.haomeiwen.com/subject/aotdvqtx.html