排序和查找算法那麽多,但是那些方法更好? 那些方法更有优势? 自己应该主要掌握那几张算法 ? 或者自己当前的数据应该怎么排序或者查找?



今天我们来对应一个实际问题来搭配使用排序算法以及查找算法,来简单的通过几个搭配案例来解答这些问题。


1. 首先在我们先讨论一下几种排序方法的时间复杂度和空间复杂的。通过此图你能直接的了解,那种情况下使用哪一种方式。

unity 局部规避算法 unity 常用算法_数据

1. 提示:时间复杂度,准确来说是描述一个算法在问题规模不断增加时对应的时间增长曲线。

unity 局部规避算法 unity 常用算法_unity 局部规避算法_02




比如: 要找一个数组里面最大的一个数,需要把这个数组的n个变量遍历一遍,操作次数为n,那么这个算法的复杂度就是O(n)。


那么要用冒泡排序一个数组,那么对于n个变量的数组,不仅要遍历数组,还要对比每个n,然后再进行交换位置变量n的平方,记作unity 局部规避算法 unity 常用算法_数组_03


有时候我们对变量操作的次数是个多项式比如 unity 局部规避算法 unity 常用算法_unity 局部规避算法_04 + unity 局部规避算法 unity 常用算法_数组_03 +n ,你那就取数量级最大的那个,那就是记作 O(unity 局部规避算法 unity 常用算法_unity 局部规避算法_04)


然后由图可知, 相对稳定的排序算法有: 冒泡, 插入,基数,归并。其中最常用的就是冒泡,他可以处理许多轻量级的数据,那数据一旦过大,问题规模n增加,那么使用稳定的归并比较好

2. 提示:空间复杂的呢。和时间复杂度的逻辑是一样的。只是时间复杂度是针对时间的,而空间复杂度是指运行完一个程序所需要的内存的大小

2. 接下来我们再讨论一下几种查找算法的时间复杂度的。老规矩,图片伺候

unity 局部规避算法 unity 常用算法_数组_07


unity 局部规避算法 unity 常用算法_算法_08

提示 : 其中c是一个常量,如果一个算法的复杂度为c 。时间复杂度为 log2n 、n 、 n*log2n ,那么这个算法时间效率比较高 。但是如果是2n ,3n ,n!,那么n稍微大一些就会令这个算法就很耗时了,居于中间的几个则差强人意。

那么我们就可以根据每个排序和查询算法的时间复杂度的优劣势,进行一个搭配使用。下面将有部分案例,以供参考(实现平台:Unity3D。实现语言 : C#)

1.冒泡排序+顺序查找 
2.归并排序+二分查找 

未待完续....



暖心小贴士: 频繁的函数(方法)调用会大大降低了算法的效率。函数是不能滥写的,这可能会大大降低程序运行效率。



3. 说明了每个排序与查询算法的时间复杂度。那么现在就让我们直接针对一个问题进行时间的运用吧 。



例题: 假设有N颗树木,设每颗树的位置和高度已经初始。现在要找到高度大于[ float变量(hight)]的所有树所在的坐标。
  1. 首先假设有了树的类。
public class Tree
{
	    public Vector3 m_pos;  // 树的位置
	    public float m_hight;    //树的高度
	    public Tree(Vector3 pos,float hight)   //实例化一颗树
	    {
	        m_pos = pos;
	        m_hight = hight;
	    }
	
	    public Tree(float hight)   //实例化一颗树
	    {
	        m_hight = hight;
	    }
}

接下来使用对应的排序方法和查找方法来解题。

1. 冒泡排序 + 普通顺序查找 方法:(此方法在数据规模小的时候适用,在大部分情况下适用)

public class BubbleSort
{
    public Tree[] trees = new Tree[100];   // 假设已经有了100颗已经实例的树。

    List<Tree> trees_pos = new List<Tree>();
    
    //获取高度为 hight 的所有树木
    public List<Tree> GetAppointHight(float hight)
    {
    
        StartSort();  //首先先排序
        trees_pos.Clear();

	// 顺序查找
        for(int i  = 0; i < trees.Length; i++)
        {
            if(trees[i].m_hight > hight)
            {
                trees_pos.Add(trees[i]);
            }
        }
        return trees_pos;
    }
    
    //正宗冒泡排序
    void StartSort()
    {
        for (int i = 0; i < trees.Length - 1; i++)
        {
            for (int j = 0; j < trees.Length - 1 - i; j++)
            {
                if (trees[j].m_hight > trees[j + 1].m_hight)
                {
                    Tree tree = trees[j];
                    trees[j] = trees[j + 1];
                    trees[j + 1] = tree;
                }
            }
        }
    }
}

2. 归并排序 + 二分查找查找 方法:(此方法在数据规模大的时候适用,在大部分情况下适用)

友情链接:归并排序的解析

40亿的数据,普通排序最大需要40亿次循环才能找出结果。二分查找最大值需要32次!!!因为他的运行时间是 对数时间(log2 4亿)。

public class MergeSort
{
    public Tree[] trees = new Tree[1000000];  // 假设已经有一百万颗已经实例的树
    public Tree[] new_trees;   //储存用于归并排序之后数据的并入
    
    //通过这个函数获取 树中 高度 大于 hight 的所有树木
    public void GetAppointHight(float hight)
    {
	    new_trees = new Tree[trees.Length];   
	    
        StartMergeSort(0, trees.Length - 1);  // 对数组进行 归并排序

        int index = TwoPointSearch(hight);   //排序之后。二分查找出需要高度所在的下标

        for(int i = index; i < new_trees.Length; i++)    //输出高度大于 hight 的所有树木
        {
            Debug.Log(" All hight : index :" + i + " : " + new_trees[i].m_hight);
        }
    }


    //二分查找 算法
    int TwoPointSearch(float hight)
    {
        int low = 0;
        int high = new_trees.Length;

        while (low <= high)
        {
            int mid = (low + high) / 2;

            if (hight > new_trees[mid].m_hight)   // 如果要找的数在后半部分
            {
                low = mid + 1;
            }
            else if(hight < new_trees[mid].m_hight)  // 负责在前半部分
            {
                high = mid - 1;
            }
            else{

                return mid;
            }
        }

        return -1;
    }

    
    //二路归并(递归实现)
    void StartMergeSort(int low, int high)
    {
        if (low < high)
        {
            int mid = (low + high) / 2;
            StartMergeSort(low, mid);      //左边有序
            StartMergeSort(mid + 1, high);   //右边有序

            Merge(low, mid, high);     //再将两个有序序列合并
        }
    }

    //排序
    public void Merge(int low, int mid, int high)
    {
        int i, j, k = 0;
        i = low;
        j = mid + 1;//避免重复比较a[mid]

        while (i <= mid && j <= high)//数组a[low,mid]与数组(mid,high]均没有全部归入数组temp中去
        {
            if (trees[i].m_hight <= trees[j].m_hight)        //如果a[i]小于等于a[j]
                new_trees[k++] = trees[i++]; //则将a[i]的值赋给temp[k],之后i,k各加一,表示后移一位
            else
                new_trees[k++] = trees[j++]; //否则,将a[j]的值赋给temp[k],j,k各加一
        }


        while (i <= mid)             //表示数组a(mid,high]已经全部归入temp数组中去了,而数组a[low,mid]还有剩余
            new_trees[k++] = trees[i++];     //将数组a[low,mid]剩下的值,逐一归入数组temp

        while (j <= high)           //表示数组a[low,mid]已经全部归入到temp数组中去了,而数组(mid,high]还有剩余
            new_trees[k++] = trees[j++];     //将数组a(mid,high]剩下的值,逐一归入数组temp

        for (i = 0; i < k; i++)     //将归并后的数组的值逐一赋给数组a[low,high]
            trees[low + i] = new_trees[i];     //注意,应从a[low+i]开始赋值
    }
}