排序算法整理

一、低级排序

1. 插入排序(低级排序中速度最快的方法)

类似于打扑克牌时自己拿牌:首先摸到第一张牌,那么默认是有序的,接着第二张牌过来,那就将其与其前的第一张牌进行比较,如果大于,那就放在第二个位置;如果小于,那就将第一张牌向后移一个位置,空出第一个位置,给这张新牌。当在来一张新牌的时候,我们就在进行类似于之前的比较工作,如果小于,就挪位置,大于就放后边。直至拿到所有的牌。

#include <iostream>

using namespace std;

void sortNum(int* a,int num)            //因为是直接传入指针,所以不需要返回值,指针指向的内容也会改变
{
    int temp;

    // N 层循环做完整个队列的事
    for(int i = 1;i < num ;i++)
    {
        //一层循环做一件完整的事:比较大小、交换并挪出空位
        for(int j = i - 1;a[j] > a[j + 1] && j >= 0;j--)
        {
            temp = a[j + 1];
            a[j + 1] = a[j];
            a[j] = temp;
        }
    }
}

int main()
{
    const int num = 9;
    int a[num] = {5,4,6,3,2,1,7,3,5};

    sortNum(a,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i];
    }

    return 0;
}

但是,有的时候我们不能确定我们使用的类型,所有我们会使用C++提供的模板来简化多个不同类型,相同操作的函数:

#include <iostream>

using namespace std;

template <class T>
/*函数模板在编译的时候会自动进行实例化,创建不同的函数出来*/
void sortNum(T* a,int num)            //因为是直接传入指针,所以不需要返回值,指针指向的内容也会改变
{
    T temp;

    // N 层循环做完整个队列的事
    for(int i = 1;i < num ;i++)
    {
        //一层循环做一件完整的事:比较大小、交换并挪出空位
        for(int j = i - 1;a[j] > a[j + 1] && j >= 0;j--)
        {
            temp = a[j + 1];
            a[j + 1] = a[j];
            a[j] = temp;
        }
    }
}

int main()
{
    const int num = 9;

    int a[num] = {5,4,6,3,2,1,7,3,5};
    float b[num] = {5.3,4.0,6.2,3.1,2.7,1.5,7.3,3.1,5.5};
    sortNum(a,num);
    sortNum(b,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }
    cout<<endl;
    for(int i = 0;i < num;i++)
    {
        cout<<b[i]<<"\t";
    }

    return 0;
}

2. 冒泡排序

冒泡排序的思想就类似于煮开水你晓得,在一趟循环里,我们会对比临近的两个元素,然后通过比大小、换位置,再进行下一个元素。而且很有意思的是,最大的泡泡(最大数)往往会先沉底,所以我们在循环的时候,可以控制循环的趟数来对代码进行优化。

#include <iostream>

using namespace std;

template <class T>
void sortNum(T* a,int num)
{
    for(int j = 0;j < num - 1;j++)
    {
        for(int i = 0;i < num - 1;i++)
        {
            if(a[i] > a[i + 1])
            {
                T temp = a[i + 1];
                a[i + 1] = a[i];
                a[i] = temp;
            }
        }
    }
}

int main()
{
    const int num = 9;

    int a[num] = {5,4,6,3,2,1,7,3,5};
    float b[num] = {5.3,4.0,6.2,3.1,2.7,1.5,7.3,3.1,5.5};
    sortNum(a,num);
    sortNum(b,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }
    cout<<endl;
    for(int i = 0;i < num;i++)
    {
        cout<<b[i]<<"\t";
    }

    return 0;
}

优化代码:

#include <iostream>

using namespace std;

template <class T>
void sortNum(T* a,int num)
{
    for(int j = 0;j < num - 1;j++)
    {
        for(int i = 0;i < num - 1 -j;i++)
        {
            if(a[i] > a[i + 1])
            {
                T temp = a[i + 1];
                a[i + 1] = a[i];
                a[i] = temp;
            }
        }
    }
}

int main()
{
    const int num = 9;

    int a[num] = {9,4,6,3,2,1,7,3,5};
    float b[num] = {5.3,4.0,6.2,3.1,2.7,1.5,7.3,3.1,5.5};
    sortNum(a,num);
    sortNum(b,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }
    cout<<endl;
    for(int i = 0;i < num;i++)
    {
        cout<<b[i]<<"\t";
    }

    return 0;
}

3. 选择排序

选择排序与冒泡排序类似,但是也有自己的特点,它每次都会从当前未排序的整数中找一个最小的数,将它放在已排序的整数链表的最后。而且选择排序是要比冒泡排序速度快的,因为冒泡排序要经常性的进行交换,而选择排序选出最小的时候,并不会进行交换,而是在最后的位置进行一次交换,所以花的时间就更少一些。

#include <iostream>

using namespace std;

template <class T>
void sortNum(T* a,int num)
{
    for(int j = 0;j < num;j++)
    {
        int flag = j;
        for(int i = j + 1;i < num;i++)
        {
            if(a[flag] > a[i])
            {
                flag = i;
            }
        }
        T temp = a[flag];
        a[flag] = a[j];
        a[j] = temp;
    }
}

int main()
{
    const int num = 9;

    int a[num] = {9,4,6,3,2,1,7,3,5};
    float b[num] = {5.3,4.0,6.2,3.1,2.7,1.5,7.3,3.1,5.5};
    sortNum(a,num);
    sortNum(b,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }
    cout<<endl;
    for(int i = 0;i < num;i++)
    {
        cout<<b[i]<<"\t";
    }

    return 0;
}

二、高级排序

1. 快速排序

最流行的排序算法,速度最快的排序算法。递归方法实现。

pivot:枢轴、枢纽

看网上大多将递归形式,我还是先用迭代来搞,我觉得比递归好理解,嗯,我不管(✿◡‿◡)

#include <iostream>

using namespace std;

void quickSort(int* a,int num)
{
    int i,j;
    for(int pivot = num - 1;pivot > 0;pivot /= 2)    //进行多次循环,选取新的枢轴排序
    {
        for(int t = 0;t < num;t++)                   //进行一次循环,将一个枢轴为基准的数进行排序
        {
            for(i = 0;a[i] < a[pivot];i++)           //定位下一个需要交换的大数脚标
            {
                ;
            }
            for(j = num - 1;a[j] > a[pivot];j--)    //定位下一个需要交换的小数脚标
            {
                ;
            }
            int temp = a[j];                         //将找出的大小数进行交换
            a[j] = a[i];
            a[i] = temp;
        }
    }
}

int main()
{
    int num = 7;
    int a[num] = {7,6,8,1,6,9,2};

    quickSort(a,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }

    return 0;
}

 

 

然后来想一下递归形式,我觉得写递归的前提是你知道一次完整的事情是什么,然后其他的事情重复下来是不是流程相同,如果相同就可以写成递归形式,let's do it:

#include <iostream>

using namespace std;

void quickSort(int* a,int end,int pivot)
{
    for(int t = 0;t <= end;t++)
    {
        int i,j;
        for(int t = 0;t <= end / 2;t++)
        {
            for(i = 0;a[i] < a[pivot];i++)
            {
                ;
            }
            for(j = end;a[j] > a[pivot];j--)
            {
                ;
            }
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        pivot /= 2;
        if(pivot > 0)
        {
            quickSort(a,end,pivot);
        }
    }
}


int main()
{
    int num = 7;
    int a[num] = {7,6,8,1,6,9,2};

    quickSort(a,num - 1,num - 1);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }

    return 0;
}

自己写的代码还是有点丑呀,慢慢练习吧,多敲多学习嘛,也罗列一个别写的代码:

分的比我要彻底一些,而且,但总体思想是一样的。

2. 归并排序

  • 归并

  • 迭代归并

仍旧按照自己的理解,写一个循环版的排序方案:

#include <iostream>

using namespace std;

void mergeSort(int* a,int num)
{
    for(int t = 2;t < num;t++)                  //不同步长的划分,然后分别进行不同组别的排序
    {
        for(int j = 0;j < num / t;j++)          //第二层循环是将下面的每个小组进行排序
        {
            for(int i = j * t;i< (j+1)* t;i++)  //第一层循环是在一个小组内部进行排序
            {
                if(a[i] > a[i+1])
                {
                    int temp = a[i+1];
                    a[i+1] = a[i];
                    a[i] = temp;
                }
            }
        }
    }
}

int main()
{
    int num = 7;
    int a[num] = {7,6,8,1,6,9,2};

    mergeSort(a,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }

    return 0;
}

然后继续提出递归算法:

#include <iostream>

using namespace std;

void mergeSort(int* a,int t,int num)
{
    if(t < num)
    {
        for(int j = 0;j < num / t;j++)
        {
            for(int i = j * t;i < (j + 1) * t;i++)
            {
                if(a[i] > a[i+1])
                {
                    int temp = a[i+1];
                    a[i+1] = a[i];
                    a[i] = temp;
                }
            }
        }
        t++;
        mergeSort(a,t,num);
    }
}

int main()
{
    int num = 9;
    int a[num] = {7,6,8,1,6,9,2,55,3};

    mergeSort(a,2,num);

    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }

    return 0;
}

再学习一哈别人的算法:大佬会重新创建一个数组,然后排序,但是在我看来,这是没有必要的空间开销。。。嗯。。。

 

这是写这篇博客的不知多少天后,因为发现其实之前对归并排序的核心还是有误解,所以在书写代码的时候有些地方有问题,这也就是为什么我的代码效率不高的原因,于是从新写了归并:

#include <iostream>

using namespace std;

void mmerge(int* arr,int L,int R)
{
    int M = (L + R) / 2 + 1;
    int LEFT_SIZE = M - L;
    int RIGHT_SIZE = R - M + 1;
    int left[LEFT_SIZE];
    int right[RIGHT_SIZE];

    int k = L;
    for(int i = 0;i < LEFT_SIZE;i++,k++)
    {
        left[i] = arr[k];
    }
    for(int i = 0;i < RIGHT_SIZE;i++,k++)
    {
        right[i] = arr[k];
    }

    k = L;
    int i,j;
    for(i = 0,j = 0;i < LEFT_SIZE && j < RIGHT_SIZE;k++)
    {
        if(left[i] <= right[j])
        {
            arr[k] = left[i];
            i++;
        }
        else
        {
            arr[k] = right[j];
            j++;
        }
    }

    while(j < RIGHT_SIZE)
    {
        arr[k] = right[j];
        j++;
        k++;
    }

    while(i < LEFT_SIZE)
    {
        arr[k] = left[i];
        i++;
        k++;
    }
}

void mergesort(int* arr,int L,int R)
{
    //递归停止条件:分治法使得当前管理的数组元素拆分成最小的单元即一个数据大小
    if(L == R)
    {
        return ;
    }
    else
    {
        int M = (L + R) / 2 + 1;
        mergesort(arr,L,M - 1);
        mergesort(arr,M,R);
        mmerge(arr,L,R);
    }
}

int main()
{
    int num = 8;
    int arr[num] = {3,7,8,9,4,6,9,10};

    mergesort(arr,0,num - 1);

    for(int t = 0;t < num;t++)
    {
        cout<<arr[t]<<endl;
    }

    return 0;
}

3. 基数排序

基数:十进制的基数是10,二进制的基数是2。如果对扑克牌花色排序就是4进制,如果对1-K排序,就是13进制。

示例:

一般分为两种:MSD:先排最高位; LSD:先排最低位。以LSD为例:

以个位为准:将数字按个位排序,放入对应的链表里,然后再将链表里的数据取出来,按序放入数组中

以十位为准(图上错了,哈哈哈):将数字按十位排序,放入对应的链表里,然后再将链表里的数据取出来,按序放入数组中

以百位为准(图上错了,哈哈哈):将数字按百位排序,放入对应的链表里,然后再将链表里的数据取出来,按序放入数组中

4. 堆排序

表格版

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性复杂性
直接插入排序O(n^2)O(n2)O(n)O(1)稳定简单
希尔排序O(nlog2n)O(n^2)O(n)O(1)不稳定较复杂
直接选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定简单
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定较复杂
冒泡排序O(n^2)O(n2)O(n)O(1)稳定简单
快速排序O(nlog2n)O(n2)O(nlog2n)O(nlog2n)不稳定较复杂
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定较复杂
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(n+r)稳定较复杂

三、查找算法

1. 顺序查找

如果当前查找数组无序,只能使用顺序查找。如果有序,使用折半(二分)查找,或顺序查找均可。顺序查找很慢,100万个数据,平均要查找50万次。

#include <iostream>

using namespace std;

bool sequentialSearch(int* a,int num,const int x)
{
    bool find = false;

    for(int i = 0;i < num;i++)
    {
        if(a[i] == x)
        {
            find = true;
        }
    }

    return find;
}

int main()
{
    const int num = 9;

    int a[num] = {9,4,6,3,2,1,7,3,5};
    int x;
    cin>>x;
    bool find = sequentialSearch(a,num,x);

    cout.setf(ios_base::boolalpha);
    cout<<find<<endl;

    return 0;
}

2. 折半查找(二分查找)

使用前提:必须查找序列式有序的。

#include <iostream>

using namespace std;

template <class T>
void sortNum(T* a,int num)
{
    for(int j = 0;j < num;j++)
    {
        int flag = j;
        for(int i = j + 1;i < num;i++)
        {
            if(a[flag] > a[i])
            {
                flag = i;
            }
        }
        T temp = a[flag];
        a[flag] = a[j];
        a[j] = temp;
    }
}

bool binarySearch(int* a,int num,const int x)
{
    bool find = false;
    for(int i = 0;i != (num - 1);)
    {
        int flag = (i+num)/2;
        if(x > a[flag])
        {
            i = flag+1;
        }
        else if(x == a[flag])
        {
            find = true;
            return find;
        }
        else
        {
            num = flag - 1;
        }
    }
    return find;
}

int main()
{
    const int num = 9;

    int a[num] = {9,4,6,3,2,1,7,3,5};
    sortNum(a,num);
    for(int i = 0;i < num;i++)
    {
        cout<<a[i]<<"\t";
    }
    cout<<endl;

    int x;
    cin>>x;
    bool find = binarySearch(a,num,x);

    cout.setf(ios_base::boolalpha);
    cout<<find<<endl;

    return 0;
}

使用迭代方法实现二分查找:

#include <iostream>

using namespace std;

bool binarySearch(int* a,int start,int end,int x)
{
    bool find = false;
    if(start != end)
    {
        int flag = (start + end) / 2;
        if(x < a[flag])
        {
            find = binarySearch(a,start,flag - 1,x);
        }
        else if(x > a[flag])
        {
            find = binarySearch(a,flag + 1,end,x);
        }
        else
        {
            find = true;
        }
    }
    return find;
}

int main()
{
    const int num = 14;
    int a[num] = {0,1,3,4,5,7,9,10,12,17,22,23,46,78};
    int x;
    cin>>x;

    cout.setf(ios_base::boolalpha);
    cout<<binarySearch(a,0,num-1,x);

    return 0;
}

 

四、递归

递归是神,迭代是人。这节建议去b站上上个课,哈哈哈,有个很棒的讲解视频,地址链接:https://www.bilibili.com/video/av31763085/?p=6

递归其实实质上就是循环,将中间过程得不到的就暂存起来,直到找到出口,然后将过程慢慢返回,逐层返回。

递归占用内存大,需要不断的调用函数,会占用大量的内存,但是其书写更容易理解。

#include <iostream>

using namespace std;

int jieCheng(int num)
{
    if(num == 0)
    {
        return 1;
    }
    else
    {
        num = jieCheng(num-1) * num;
    }
}

int main()
{
    int num;
    cin>>num;

    cout<<jieCheng(num)<<endl;

    return 0;
}

如果用普通循环实现:

#include <iostream>

using namespace std;

int jieCheng(int num)
{
    int sum = 1;
    for(int i = num;i > 0;i--)
    {
        sum *= i;
    }
    return sum;
}

int main()
{
    int num;
    cin>>num;

    cout<<jieCheng(num)<<endl;

    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值