排序

首先我们看一下排序的种类,排序有比较排序和非比较排序,今天我们主要探讨一下常用的几个排序。
这里写图片描述
插入排序
插入排序是一种简单直观的排序。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
步骤:
1。从第一个元素开始,该元素可以认为已经被排序
2。取出下一个元素,在已经排序的元素序列中从后向前扫描
3。如果该元素(已排序)大于新元素,将该元素移到下一位置
4。重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5。将新元素插入到该位置中
6。重复步骤2

void PrintArray(int* a, size_t n)
{
    for (size_t i = 0; i < n; ++i)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
}

void InsertSort(int* a, size_t n)
{
    assert(a);

    for (size_t i = 0; i < n-1;++i)
    {
        int end = i;
        int tmp = a[end+1];
        while (end >= 0)
        {
            if (a[end] > tmp)
            {
                a[end+1]=a[end];
                --end;
            }
            else
            {
                break;
            }
        }
        a[end+1] = tmp;
    }
}

void TestInsertSort()
{
    int a[] = {2,5,4,9,3,6,8,7,1,0};
    PrintArray(a,sizeof(a)/sizeof(a[0]));
    InsertSort(a, sizeof(a)/sizeof(a[0]));
    PrintArray(a,sizeof(a)/sizeof(a[0]));
}

希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位

void ShellSort(int*a,size_t n)
{
    assert(a);
    int gap = n;

    while (gap > 1)
    {
        gap = gap/3+1;
        for(size_t i = 0; i < n-gap; ++i)
        {
            int end = i;
            int tmp = a[end+gap];
            while (end >= 0)
            {
                if (a[end] > tmp)
                {
                    a[end+gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end+gap] = tmp;
        }
    }
}

void TestShellSort()
{
    int a[] = {2,5,4,9,3,6,8,7,1,0};
    PrintArray(a,sizeof(a)/sizeof(a[0]));
    ShellSort(a, sizeof(a)/sizeof(a[0]));
    PrintArray(a,sizeof(a)/sizeof(a[0]));
}

选择排序
选择排序是一种简单直观的排序算法。
首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。

void SelectSort1(int *a,size_t n)
{
    int begin = 0;
    int end = n-1;
    int min= 0;
    while(begin < end)//o(n)
    {
        min = begin;
        for(size_t i = begin; i<=end; i++)
        {
            if(a[min] > a[i])
            {
                min = i;// 选择出最小数
            }
        }
        swap(a[begin],a[min]);
        ++begin;
    }
}
//优化
void SelectSort2(int* a, size_t n)
{
    int begin = 0;
    int end = n-1;
    int min = 0;
    int max = 0;
    while(begin < end)//o(1/n)
    {
        min = begin;
        max = begin;
        for(size_t i =  begin; i <= end; i++ )
        {
            if(a[min]>a[i])
            {
                min = i;
            }
            if(a[max]<a[i])
            {
                max = i;
            }
        }
        swap(a[max],a[end]);
        if(end == min)//防止min max互换
        {
            a[min] = a[max];
        }
        swap(a[min],a[begin]);
        ++begin;
        --end;
    }
}

void TestSelectSort()
{
    int a[] = {9,5,4,9,3,6,8,7,1,0};
    PrintArray(a,sizeof(a)/sizeof(a[0]));
    //SelectSort1(a, sizeof(a)/sizeof(a[0]));
    SelectSort1(a, sizeof(a)/sizeof(a[0]));
    PrintArray(a,sizeof(a)/sizeof(a[0]));
}

堆排序
堆积排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

void AdjustDwon(int* a, size_t root, size_t n)
{
    size_t parent = root;
    size_t child = parent*2+1;
    while (child < n)
    {
        if (child+1 < n && a[child+1] > a[child])
        {
            ++child;
        }

        if (a[child] > a[parent])
        {
            swap(a[child], a[parent]);
            parent = child;
            child = 2*parent+1;
        }
        else
        {
            break;
        }
    }
}

void HeapSort(int* a, size_t n)
{
    assert(a);
    for (int i = (n-2)/2; i >=0; --i)
    {
        AdjustDwon(a, i, n);
    }

    size_t end = n-1;
    while (end > 0)
    {
        swap(a[0], a[end]);
        AdjustDwon(a, 0, end);
    }
}

冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
步骤:
1。比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3。针对所有的元素重复以上的步骤,除了最后一个。
4。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

void BubbleSort(int* a, size_t n)
{
    assert(a);
    int end = n;
    while(end > 0)
    {
        bool exchange = false;//交换标志
        // 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上  
        for(int i = 1;i < end;++i)
        {
            if(a[i-1] > a[i])
            {
                swap(a[i-1],a[i]);
                exchange = true;
            }
        }
        // 如果标志为false,说明本轮遍历没有交换,已经是有序数列,可以结束排序
        if (exchange == false)
        {
            break;
        }

        --end;
    }
}

快速排序
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。
步骤:
1。从数列中挑出一个元素,称为 “基准”(pivot),
2。重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3。递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

// 左右指针法
int PartSort1(int* a, int left, int right)
{
    int mid = GetMidIndex(a, left, right);
    swap(a[mid], a[right]);

    int key = a[right];
    int begin = left;
    int end = right;
    while (begin < end)
    {
        while (begin < end && a[begin] <= key)
        {
            ++begin;
        }

        while (begin < end && a[end] >= key)
        {
            --end;
        }

        if (begin < end)
            swap(a[begin], a[end]);
    }

    swap(a[begin], a[right]);
    return begin;
}

// 挖坑法
int PartSort2(int* a, int left, int right)
{
    int key = a[right];
    while (left < right)
    {
        // 找大
        while (left < right && a[left] <= key)
        {
            ++left;
        }

        // 找到比key大的数据放到右边的坑
        a[right] = a[left];

        while (left < right && a[right] >= key)
        {
            --right;
        }

        a[left] = a[right];
    }

    a[left] = key;
    return left;
}

int PartSort3(int* a, int left, int right)
{
    int prev = left-1;
    int cur = left;
    int key = a[right];
    while (cur < right)
    {
        if (a[cur] < key && ++prev != cur)
        {
            swap(a[prev], a[cur]);
        }

        ++cur;
    }

    swap(a[++prev], a[right]);

    return prev;
}

// [left, right]
void QuickSort(int* a, int left, int right)
{
    assert(a);

    if (left >= right)
        return;

    if (right - left < 5)
    {
        InsertSort(a+left, right-left+1);
    }
    else
    {
        int div = PartSort1(a, left, right);
        QuickSort(a, left, div-1);
        QuickSort(a, div+1, right);
    }
}
#include <stack>
void QuickSortNonR(int* a, int left, int right)
{
    assert(a);
    stack<int> s;
    if (left < right)
    {
        s.push(right);
        s.push(left);
    }

    while (!s.empty())
    {
        int begin = s.top();
        s.pop();
        int end = s.top();
        s.pop();

        int div = PartSort1(a, begin, end);
        if (begin < div-1)
        {
            s.push(div-1);
            s.push(begin);
        }

        if (div+1 < end)
        {
            s.push(end);
            s.push(div+1);
        }
    }
}

归并排序
步骤:
1。申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2。设定两个指针,最初位置分别为两个已经排序序列的起始位置
3。比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4。重复步骤3直到某一指针达到序列尾
5。将另一序列剩下的所有元素直接复制到合并序列尾

void Merge(int* a, int* tmp, int begin1,int end1, int begin2, int end2)
{
    size_t pos = begin1;
    size_t index = begin1;
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
            tmp[index++] = a[begin1++];
        else
            tmp[index++] = a[begin2++];
    }

    while (begin1 <= end1)
    {
        tmp[index++] = a[begin1++];
    }

    while (begin2 <= end2)
    {
        tmp[index++] = a[begin2++];
    }

    memcpy(a+pos, tmp+pos, sizeof(int)*(end2-pos+1));
}

void _MergeSort(int* a,int* tmp, int left, int right)
{
    if (left >= right)
        return;

    int mid = left+(right-left)/2;
    // 0 0 1 1
    // [left mid] [mid+1, right]

    _MergeSort(a, tmp, left, mid);
    _MergeSort(a, tmp, mid+1, right);

    Merge(a, tmp, left, mid, mid+1, right);
}

void MergeSort(int* a,size_t n)
{
    assert(a);

    int* tmp = new int[n];
    _MergeSort(a,tmp, 0, n-1);
    delete[] tmp;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值