数据结构总结(十大排序,各种树详解)

课本排序的算法比较
*

排序

插入排序包括:
直接插入排序:从无序第一个元素开始和有序区的最后一个比较,不符合就取这个元素
从有序区的第一个元素开始逐一比较找到位置插入,再移动元素。
, 折半插入排序:从无序第一个元素开始和有序区的最后一个比较,不符合就取出这个
元素,在有序区进行折半查找找到合适的位置,再插入最后移动元素
希尔排序:
交换排序:
冒泡排序:浮上去(从右到左),沉下去(从左到右)
快速排序:思路如下图平均时间复杂度O(nlog2n),当i和j相等或者j<i时表示第一趟结束,
再选取下一个元素作为关键字作为
在这里插入图片描述
看这条题:

在这里插入图片描述
选择排序:
简单的选择排序:就是从无序区中选择关键字最小的元素与无序区的第一个元素进行交换
然后无序区的的第一个元素就变成了有序区的最后一个元素,每一趟
选择给有序区加入一个元素,因为每一次都是选择最小的,有序区从
0个到最后的排好全部元素,所以有序区的关键字都不大于无序区的
关键字。
堆排序:堆分为最大堆和最小堆,其实就是完全二叉树。最大堆要求节点的元素都要不小
于其孩子,最小堆要求节点元素都不大于其左右孩子,两者对左右孩子的大小关系不做任
何要求,其实很好理解。有了上面的定义,我们可以得知,处于最大堆的根节点的元素一
定是这个堆中的最大值
在这里插入图片描述
在这里插入图片描述

**
堆是一个完全二叉树,但不一定是满二叉树,根节点比两个子节点都小的叫小根堆,比两个子节点都大的叫大根堆。堆的存储通常是通过数组来实现的
如下图
在这里插入图片描述

二叉排序树也叫二叉搜索树(BST):

**
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
(2)若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
**

平衡二叉树

**
平衡二叉树(Balanced Binary Tree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等,平衡因子:根节点的左子树的高度减去右子树的的高度,可能是1,-1,0.

**

线性表的查找

**
1.顺序查找:平均时间复杂度:O(n)
2.折半查找:要求是有序的表(也就是经过排序的表),时间复杂度为:O(log2n)
3.索引存储结构和分块查找:索引的存储结构是在存储数据的同时还建立了附加的索引的索引表,索引表的每一项叫做索引项,索引项的形式为(关键字,地址)关键字是唯一标识一个节点,地址指向节点的指针,也可以是相对的指针(如数组的下标)。索引的存储结构进行关键字查找是是现在索引表中快速查找(因为索引表中的关键字是先前排好序的,是一个有序的表),然后通过对应的地址找到主数据表中的元素。它的时间复杂度取决于查找索引表中关键字的时间复杂度。
可以通过顺序来查找或者折半查找。
4.分块查找:
主要思路:把元素分成b块,前b-1块是s=n/b个元素,最后一块是小于s或等于个元素,这些块是有规则的,前一块的最大关键字小于后一块的最小关键字,这就是分块有序,但是块中元素是无序的,然后再把每一块的最大关键字抽出来建立一张索引表。所以分块查找分为两个阶段:1.查找索引表找到在哪个块,2.查找这个块里面的每一个元素。它的时间按复杂度由这两个阶段决定,每一个阶段都可以采取顺序查找和折半查找。

树表的查找

1.二叉排序树
一般指在二叉排序树上的查找,书本还涉及到二叉排序树的插入,创建(实质是在一棵空树上进行插入),删除(前提是查找再删除),时间复杂度为O(log2n)

2.平衡二叉树:
查找和二叉排序树的完全一样,都是O(log2n),
插入节点有四种类型(LL型,RR型,LR型,RL型),比较难,代码实现比较难,看图知道什么意思就好。
删除和二叉排序树的类似(查找->删除)

哈希表的查找

哈希表:又叫散列表,思路:储存的元素由n个,设一个长度为m(m一定要大于或者等于n)个连续的存储单元,这m个存储单元叫做哈希表,储存的是n个元素的关键字经过一个哈希函数变换得到的一个函数值。
构造哈希表的方法有:直接定值法h(k)=k+c,除留余数法h(k)=k mod p,(p小于等于m);
哈希冲突:两个不同的关键字得到一个相同的哈希函数值,解决哈希冲突的办法有:
线性探测法:d0=h(k),di=(d0+1)mod k
平方探测发:d0=h(k),di=(d0+i^2)mod k

B树就是(B-树)

  • m阶树:一棵树的所有节点最多有m棵子树,那么这棵树就叫做m阶树
    B-tree树即B树,B即Balanced,平衡的意思。因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让人产生误解。如人们可能会以为B-树是一种树,而B树又是另一种树。而事实上是,B-tree就是指的B树。和B+树用于外存的数据结构。
    B树 是一种多路搜索树(并不是二叉的),(二叉排序树可以说是一种两路排序树,一个关键字,2个子节点):
    1.定义任意非叶子结点最多只有M个儿子;且M>2;

     2.根结点的儿子数为[2, M];
    
     3.除根结点以外的非叶子结点的儿子数为[M/2, M];
    
     4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
    
     5.非叶子结点的关键字个数=指向儿子的指针个数-1;
    
     6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];(每个节点的关键字是排好序的)
    
     7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的
    

子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

   8.所有叶子结点位于同一层;(表示已经是平衡的了,平衡因子为0)

根节点的指针P1指向的子树中所有的关键字都小于17,即8,12都小于17,p2的指针指向的的鉴于17和35之间,26和30大于17小于35,而P3指针指向子树所有的关键字都大于35在这里插入图片描述
根节点的指针P1指向的子树中所有的关键字都小于17,即8,12都小于17,p2的指针指向的的鉴于17和35之间,26和30大于17小于35,而P3指针指向子树所有的关键字都大于35,类似于二叉排序树
B树的查找,首先在根节点进行二分查找(因为每个节点是排好序的),找不到就进入所属范围的子节点进行二分查找,重复直到找到为止,
B-树的特性:

   1.关键字集合分布在整颗树中;

   2.任何一个关键字出现且只出现在一个结点中;

   3.搜索有可能在非叶子结点结束;

   4.其搜索性能等价于在关键字全集内做一次二分查找;

   5.自动层次控制;

B+树是一种B树的变形,也是一种多路搜索树
与B-树不同的是,每个节点的关键字个数和子节点的个数一样,每个节点的关键字是属于[Ki,Ki+1)之间,左边是闭区间,而B树是开区间
看图:
根节点的P1指向的的节点是与根节点的5开始的,10和20小于根节点的第二个关键字28
根节点的P1指向的的节点是与根节点的5开始的,10和20小于根节点的第二个关键字28

B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在

非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

   B+的特性:

   1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好

是有序的;

   2.不可能在非叶子结点命中;

   3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储

(关键字)数据的数据层;

   4.更适合文件索引系统;

B*树

是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;
在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率

从1/2提高到2/3;
在这里插入图片描述
*

红黑树一种二叉树的改进)

1、红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。

2、平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。

红黑树的性质:

1.节点是红色或黑色。

2.根节点是黑色。

3.每个叶子节点都是黑色的空节点(NIL节点,不是null)。

4 每个红色节点的两个节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

平衡二叉树的性质:

它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。

桶排序

桶排序的思想近乎彻底的分治思想。

桶排序假设待排序的一组数均匀独立的分布在一个范围中,并将这一范围划分成几个子范围(桶)。

然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。

接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列。
实现逻辑
设置一个定量的数组当作空桶子。
寻访序列,并且把项目一个一个放到对应的桶子去。
对每个不是空的桶子进行排序。
从不是空的桶子里把项目再放回原来的序列中。

平均时间复杂度:O(n + k)
最佳时间复杂度:O(n + k)
最差时间复杂度:O(n ^ 2)
空间复杂度:O(n * k)
稳定性:稳定

基数排序

基数排序(Radix sort)是一种非比较型整数排序算法。
实现的逻辑:
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。
从最低位开始,依次进行一次排序。
这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

看图就懂
在这里插入图片描述

二分查找代码

package com.lqh.suanfa_study.shidapaixusuanfa.erfenchazhao;

/**
 * @author 刘钦华
 * @date 2020/4/1 20:57
 */

public class main {

    public static void main(String[] strings){
        int[] arr={1,2,3,4,5,6,7,8,9,10};
        System.out.println(search(arr,9));
    }

    public static int search(int[] arr,int key){

        int start =0;
        int end=arr.length-1;
        int middle;
        while (start<=end){
            middle=(start+end)/2;
            if (key>arr[middle]){
                start=middle;
            }else if (key<arr[middle]){
                end=middle;
            }else if (key==arr[middle])
                return middle;
        }
        return -1;//找不到

    }
}

直接和折半插入排序代码

package com.lqh.suanfa_study.shidapaixusuanfa.charupaixu;

/**
 * @author 刘钦华
 * @date 2020/4/4 14:01
 */

public class main {

    public static void main(String[] strings){
        int[] arr={9,8,7,6,5,4,3,2,1};
        //insertSort1(arr);
        binInsertSort(arr);
        for (int i=0;i<arr.length;i++){
            System.out.print(" "+arr[i]);
        }
    }

    public static void insertSort1(int[] arr){//直接插入排序,向前面一个一个的插入
        for (int i=1;i<arr.length;i++){//从第二个开始向前插入到正确的位置

            for (int j=i;j>0;j--){//向前进行比较,比前面一个小的就插入
                if (arr[j]<arr[j-1]){
                    int temp=arr[j];
                    arr[j]=arr[j-1];
                    arr[j-1]=temp;
                }
            }

        }
    }

    //折半插入排序
    public static void binInsertSort(int[] arr){

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

            int temp=arr[i];
            int low=0;
            int high=i-1;

            while (low<=high){//二分查找到合适的位置,查找出来时low一定等于high,插入的的位置是high+1
                int mid=(low+high)/2;
                if (temp<arr[mid]){
                    high=mid-1;
                }else
                    low=mid+1;
            }

            //整体往后移动
            for (int j=i-1;j>high;j--){
                arr[j+1]=arr[j];
            }

            //插入元素
            arr[high+1]=temp;
        }

    }

}

简单的选择排序代码

package com.lqh.suanfa_study.shidapaixusuanfa.jiandanxuanzepaixu;

/**
 * @author 刘钦华
 * @date 2020/4/5 1:26
 */

public class main {

    public static void main(String [] strings){
        int[] arr={9,8,7,6,5,4,3,2,1};
        simpleSelectSort(arr);
        for (int i=0;i<arr.length;i++){
            System.out.print(" "+arr[i]);
        }
    }

    public static void simpleSelectSort(int[] arr){
        for (int i=0;i<arr.length-1;i++){

            int min=i;//这个是关键
            //寻找第i小的值
            for (int j=i+1;j<arr.length;j++){
                if (arr[min]>arr[j])
                    min=j;
            }
            //若min有变化,就将找到的第i个小的数值与第i个位置上的数值交换
            if (min!=i){
                int temp=arr[i];
                arr[i]=arr[min];
                arr[min]=temp;
            }

        }

    }
}

冒泡排序代码

package com.lqh.suanfa_study.shidapaixusuanfa.maopaopaixu;

/**
 * @author 刘钦华
 * @date 2020/4/1 16:32
 */

public class main {

    public static void main(String[] args) {
        //冒泡排序算法
        int[] numbers=new int[]{1,5,8,2,3,9,4,1,2,2,2222};
            maopao(numbers);
            pt(numbers);

    }

   static public void maopao(int[] numbers ){//冒泡排序
        int i,j;
        for(i=0;i<numbers.length-1;i++)
        {
            for(j=0;j<numbers.length-1-i;j++)
            {
                if(numbers[j]>numbers[j+1])
                {
                    int temp=numbers[j];
                    numbers[j]=numbers[j+1];
                    numbers[j+1]=temp;
                }
            }
        }
    }

  static   public void pt(int[] numbers ){
        System.out.println("从小到大排序后的结果是:");
        for(int i=0;i<numbers.length;i++)
            System.out.print(numbers[i]+" ");
    }
}

快排代码

package com.lqh.suanfa_study.shidapaixusuanfa.kuaipai;

/**
 * @author 刘钦华
 * @date 2020/4/1 18:39
 */
public class QuickSort {

    /**
     * 根据下标交换数组的两个元素
     * @param arr 数组
     * @param index1  下标1
     * @param index2  下标2
     */
    public static void swap(int[] arr, int index1, int index2) {
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

    /**
     * 递归循环实现快排
     * @param arr 数组
     * @param startIndex 快排的开始下标
     * @param endIndex 快排的结束下标
     */
    public static void quickSort(int[] arr, int startIndex, int endIndex) {
        if(arr != null && arr.length > 0) {
            int start = startIndex, end = endIndex;
            //target是本次循环要排序的元素,每次循环都是确定一个元素的排序位置,这个元素都是开始下标对应的元素
            int target = arr[startIndex];
            //开始循环,从两头往中间循环,相遇后循环结束
            while(start<end) {
                //从右向左循环比较,如果比target小,就和target交换位置,让所有比target小的元素到target的左边去
                while(start < end) {
                    if(arr[end] < target) {
                        swap(arr, start, end);
                        break;
                    }else {
                        end--;
                    }
                }

                //从左向右循环比较,如果比target大,就和target交换位置,让所有比target大的元素到target的右边去
                while(start < end) {
                    if(arr[start] > target) {
                        swap(arr, start, end);
                        break;
                    }else {
                        start++;
                    }
                }
            }
            //确定target的排序后,如果target左边还有元素,继续递归排序
            if((start-1)>startIndex) {
                quickSort(arr, startIndex, start-1);
            }
            //确定target的排序后,如果target右边还有元素,继续递归排序
            if((end+1)<endIndex) {
                quickSort(arr, end+1, endIndex);
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = new int[]{4,1,2,2,5,3,2,9,10,6,7};
        quickSort(arr,0,10);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+",");
        }
    }

    static void kuaipai(int[] arr ,int startIndex,int endIndex){
            int i=startIndex,j=endIndex;
            int target=arr[i];//要找位置的元素
            while (i<j){

                while (i<j){//从后面往前找小的
                    if (arr[j]<target)
                    {
                        swap(arr,i,j);
                        break;
                    }
                    else
                        j--;
                }

                while (i<j){//从前往后找大的
                    if (arr[i]>target)
                    {
                        swap(arr,i,j);
                        break;
                    }
                    else
                        i++;
                }

            }

            if (i-1>startIndex){
                kuaipai(arr,startIndex,i-1);
            }
            if (j+1<endIndex){
                kuaipai(arr,j+1,endIndex);
            }
    }
}

归并排序代码

package com.lqh.suanfa_study.shidapaixusuanfa.guibingpaixu;

/**
 * @author 刘钦华
 * @date 2020/4/1 19:44
 */
public class Main {

    public static void main(String[] args) {
        int[] arr = {11,44,23,67,88,65,34,48,9,12};
        int[] tmp = new int[arr.length];    //新建一个临时数组存放
        mergeSort(arr,0,arr.length-1,tmp);
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
    }

    public static void merge(int[] arr,int low,int mid,int high,int[] tmp){
        int i = 0;
        int j = low,k = mid+1;  //左边序列和右边序列起始索引


        while(j <= mid && k <= high){//这个循环下来左右序列肯定有一个是全部复制到tmp临时数组里面,这个是归并回溯的核心
            if(arr[j] < arr[k]){
                tmp[i++] = arr[j++];
            }else{
                tmp[i++] = arr[k++];
            }
        }
        //若左边序列还有剩余,则将其全部拷贝进tmp[]中
        while(j <= mid){
            tmp[i++] = arr[j++];
        }

        while(k <= high){
            tmp[i++] = arr[k++];
        }

        for(int t=0;t<i;t++){
            arr[low+t] = tmp[t];
        }
    }

    public static void mergeSort(int[] arr,int low,int high,int[] tmp){
        if(low<high){
            int mid = (low+high)/2;
            mergeSort(arr,low,mid,tmp); //对左边序列进行归并排序
            mergeSort(arr,mid+1,high,tmp);  //对右边序列进行归并排序
            merge(arr,low,mid,high,tmp);    //合并两个有序序列
        }
    }

}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值