算法学习01:二分查询,选择法、插入法、分治法排序

查询与排序是使用的再频繁不过的两个功能,算法学习系列实现语言为C#。

一般情况下的查询

Int32 Search(Int32[] source, Int32 task)
{
    var index = 0;
    while (index < source.Length)
        if (source[index++] == task)
            return index - 1;

    //返回值为Length则说明未找到
    return source.Length;
}

时间复杂度为O(n),在检索源没有排序的情况下,这即为最高的检索方法。


二分查询

当检索源为顺序排列时,我们可以使用二分查询进行检索。

可将时间复杂度提升至O(lgn).

非递归实现:

Int32 BinarySearch(Int32[] source, Int32 targetVal)
{
    Int32 leftPointer = 0;
    Int32 rightPointer = source.Length - 1;
    while (leftPointer <= rightPointer)
    {
        Int32 pos = (rightPointer + leftPointer) / 2;
        if (source[pos] == targetVal)
            return pos;
        if (source[pos] < targetVal)
            leftPointer = pos + 1;
        else
            rightPointer = pos - 1;
    }
    return source.Length;
}

递归实现:

Int32 BinarySearch(Int32[] source, Int32 startIndex, Int32 endIndex, Int32 targetVal)
{
    if (startIndex > endIndex)
        return source.Length;
    Int32 pos = (startIndex + endIndex) / 2;
    if (source[pos] == targetVal)
        return pos;
    if (source[pos] < targetVal)
        return BinarySearch(source, pos + 1, endIndex, targetVal);
    else
        return BinarySearch(source, startIndex, pos - 1, targetVal);
}

测试结果:

//输出均为8
Console.WriteLine(BinarySearch(new Int32[10] { 4, 3, 2, 1, 3, 8, 9, 19, 88, 57 }, 0, 10, 88));
Console.WriteLine(BinarySearch(new Int32[10] { 4, 3, 2, 1, 3, 8, 9, 19, 88, 57 }, 88));

接下来是一些常用的排序方法。


选择法

时间复杂度O(n*n)。

Int32[] SelectionSort(Int32[] source)
{
    for (int i = 0; i < source.Length; i++)
    {
        var smallestValIndex = i;
        for (int j = i + 1; j < source.Length; j++)
        {
            if (source[smallestValIndex] > source[j])
                smallestValIndex = j;
        }
        var temp = source[i];
        source[i] = source[smallestValIndex];
        source[smallestValIndex] = temp;
    }
    return source;
}

插入法

时间复杂度O(n*n),与选择法相比,适合在链表结构中使用。

Int32[] InsertSort(Int32[] source)
{
    var resultArray = new Int32[source.Length];
    for (int i = 0; i < source.Length; i++)
    {
        var index = 0;
        while (index++ < i)
        {
            if (resultArray[index - 1] > source[i])
                break;
        }
        for (int j = index - 1; j < i; j++)
            resultArray[j + 1] = resultArray[j];
        resultArray[index - 1] = source[i];
    }
    return resultArray;
}

分治排序之合并法

时间复杂度O(n * lgn)。

分治原理例图:

Int32[] DevideAndConquerSort(Int32[] source, Int32 startIndex, Int32 endIndex)
{
    //子数组只有一个元素时,是无需排序的
    if (startIndex + 1>= endIndex)
        return source;
    var middleIndex = (startIndex + endIndex) / 2;
    DevideAndConquerSort(source, startIndex, middleIndex);
    DevideAndConquerSort(source, (startIndex + endIndex) / 2, endIndex);
    MergeSort(source, startIndex, (startIndex + endIndex) / 2, endIndex);
    return source;
}

void MergeSort(Int32[] source, Int32 startIndex, Int32 middleIndex, Int32 endIndex)
{
    var arrayAPointer = startIndex;
    var arrayBPointer = middleIndex;
    var loopLength = endIndex - startIndex;
    var resultArray = new Int32[loopLength];
    for (int i = 0; i < loopLength; i++)
    {
        if (arrayBPointer != endIndex && source[arrayAPointer] > source[arrayBPointer])
            resultArray[i] = source[arrayBPointer++];
        else if (arrayAPointer != middleIndex)
            resultArray[i] = source[arrayAPointer++];
        else
            resultArray[i] = source[arrayBPointer++];
    }
    for (int i = 0; i < loopLength; i++)
    {
        source[startIndex + i] = resultArray[i];
    }
}

分支排序之快速排序法

与合并法相比,适合在n不是很大,又不是很小的时候使用。

分治原理例图:

Int32[] QuickSort(Int32[] source, Int32 startIndex, Int32 endIndex)
{
    if(startIndex >= endIndex)
        return source;
    var index = Partition(source, startIndex, endIndex);
    QuickSort(source, startIndex, index - 1);
    QuickSort(source, index + 1, endIndex);
    return source;
}

Int32 Partition(Int32[] source, Int32 startIndex, Int32 endIndex)
{
    Int32 index = 0;
    while ((index++) < endIndex - startIndex)
    {
        if (source[startIndex + index - 1] >= source[endIndex - 1])
            break;
    }
    if(startIndex + index - 1 != endIndex)
    {
        var temp = source[startIndex + index - 1];
        source[startIndex + index - 1] = source[endIndex - 1];
        source[endIndex - 1] = temp;
    }
    return startIndex + index - 1;
}

总结

时间: 2024-08-19 11:01:52

算法学习01:二分查询,选择法、插入法、分治法排序的相关文章

算法学习 - 01背包问题(动态规划C++)

动态规划 01背包 问题描述 求解思路 代码实现 放入哪些物品 代码 动态规划 我在上一篇博客里已经讲了一点动态规划了,传送门:算法学习 - 动态规划(DP问题)(C++) 这里说一下,遇到动态规划应该如何去想,才能找到解决办法. 最主要的其实是要找状态转移的方程,例如上一篇博客里面,找的就是当前两条生产线的第i个station的最短时间和上一时刻的时间关系. minTime(station[1][i]) = minTime(station[1][i-1] + time[i], station[

C语言实现快速排序法(分治法)

title: 快速排序法(quick sort) tags: 分治法(divide and conquer method) grammar_cjkRuby: true --- 算法原理 分治法的基本思想:将原问题分解为若干个更小的与原问题相似的问题,然后递归解决各个子问题,最后再将各个子问题的解组合成原问题的解. 利用分治法可以将解决办法分为 "三步走" 战略: (1) 在数据集中选定一个元素作为"基准"(pivot) (2) 将所有数据集小于基准的元素放在基准左边

【算法学习笔记】25.贪心法 均分纸牌问题的分析

贪心法: 贪?算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解. 贪心算法不是对所有问题都能得到整体最优解,关键是贪?心策略的选择,选择的贪?策略必须具备?后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关. 太概念化了.总结起来三点: 可行性:必须满足问题的约束 局部最优:当前步骤中所有可行的选择里最佳的局部选择. 不可取消:选择一旦做出,后面的步骤就无法改变. 问题要具有贪心选择性

排序算法学习整理二(选择)

9二.选择排序: 选择排序十分的简单和直观,其的工作原理是每一次从待排序的数组中选出最小(或最大)的一个元素,存放在序列的起始位置.因此,选择排序也是像我们这种萌新最容易写出来的排序算法. 排序步骤: 选出最小的元素 放在数组最前面 选出第二小的元素 放在第二位 重复如此直到完成排序 下面举个栗子: 有一个数组其元素如下 5 1 4 3 2 6 7 0 9,其选择排序流程如下 第一轮: 0 1 4 3 2 6 9 5 7 0最小,0和5换 第二轮: 0 1 4 3 2 6 9 5 7 1最小,不

经典排序算法学习笔记五——直接选择排序

一.直接选择排序 数据结构 数组 最差时间复杂度 O(n^2) 最优时间复杂度 O(n^2) 平均时间复杂度 O(n^2) 最差空间复杂度 О(n) total, O(1) auxiliary 1.算法思想: 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置. 然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾. 以此类推,直到所有元素均排序完毕. 2.伪代码: repeat (numOfElements - 1) times set the first uns

【算法学习笔记】28.枚举法 解题报告 SJTU_OJ 1255 1256 魔戒

1256. 你的魔戒?不,是你的魔戒.加强版 Description 在前往末日火山的途中,佛罗多与他的霍比特人同胞不幸被半兽人抓住了.半兽人要对每个霍比特人进行询问,以找出哪个霍比特人携带了至尊魔戒.每个霍比特人可能会说以下几种话: I have the ring. 我有魔戒. I have not the ring. 我没有魔戒. XXX has the ring. XXX有魔戒.(XXX表示某个霍比特人的名字) XXX has not the ring. XXX没有魔戒. Today is

【算法学习笔记】64. 枚举法 SJTU OJ 1381 畅畅的牙签

枚举法就好了,推理很麻烦,感觉也做不出来. 创造一个结构体,一个是真实的数,一个是花费的牙签数. 构建一位数,两位数,三位数即可. #include <iostream> #include <vector> using namespace std; //从0到9耗费的牙签数 int cost[10]={6,2,5,5,4,5,6,3,7,6}; struct num { int n;//用于计算的数 int c;//耗费的牙签 }; num v[100000]; int main(

算法学习——浮点数二分

浮点数二分不需要考虑太多的边界问题,只需要保证精度满足题目的要求即可,通常在acm中,假如题目精度要求保留n位小数,我们正常设置与标准答案的误差为10的负n+2次方就行. 例题: c++代码: #include<bits/stdc++.h> using namespace std; int main(){ double l = -10000,r = 100000; double x; cin>>x; while( r - l > 1e-8 ){ double mid = (l

【算法学习笔记】61.回溯法 DFS SJTU OJ 1106 sudoku

虽然DLX可以提高效率....但是对于NPC问题也不用太追求效率了,而且还只有一个测试点. 所以 只要DFS不断的填入,直到空格全部被填满:要注意的是DFS中全局变量的更新和恢复. 至于存储的方法,只要考虑每一行每一列每一个小块的不重复即可. #include <iostream> #include <cstring> using namespace std; int cnt = 0 ;//表示剩余的要填的空格的数目 struct point { int x,y; }; point