大师兄的数据结构学习笔记(十六): 排序

大师兄的数据结构学习笔记(十五): 串

一、关于排序

  • 将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程叫做排序
  • 首先分为内部排序外部排序,若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序;反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序
  • 其次,根据是否需要对数据关键字进行比较,分为比较排序非比较排序
  • 如果在每一次单趟排序后,相同关键字的相对顺序不变,称为稳定排序。例:冒泡,插入,基数,归并属于稳定排序,选择,快速,希尔,归属于不稳定排序

二、直接插入排序

  • 直接插入排序是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。
  • 在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
  • 在数组A=(5,2,4,6,1,3)上插入排序的操作。
  • 对数组运行for循环的迭代。
  • 每次迭代中,黑色的长方形保存取自A[j]的关键字,在第5行的测试中将它与其左边的加阴影的长方形中的值进行比较。
  • 加阴影的箭头指出数组值在第6行向右移动一个位置,黑色的箭头指出在第8行关键字被移到的地方。
  • (f)最终排序好的数组 。
  • 插入排序的最好情况:顺序T=O(N),最坏情况:逆序T=O(N^2)
template<typename ElementType>
void Insertion_sort(ElementType A[], int N)
{   
    int i = 0;
    for (int P = 1; P < N; P++)
    {
        int key = A[P];
        for (i = P-1; i >= 0; i--)
        {
            if (A[i] <= key)
            {
                A[i + 1] = key;
                break;
            }
            A[i + 1] = A[i];
        }
        if (i < 0)A[0] = key;
    }
}

三、希尔排序

  • 由于插入排序有以下两个特点:

1) 插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
2) 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。

  • 基于以上特点,希尔排序(Shell Sort)插入排序进行了改进:

1) 首先将待排序列按固定间隔d分为若干个组。
2) 对这些分组分别进行直接插入排序。
3) 重复1和2若干次,每次间隔减半。
4) 经过以上操作,整个序列的有序度一定会被提高,逆序对一定会变少。
5) 最后,再对整个序列进行一趟直接插入排序。

  • 平均时间复杂度为O(N^{1.3})左右。
template<typename ElementType>
void Shell_sort(ElementType A[], int N)
{
    int gap = N;
    while (gap > 1)
    {
        gap = gap / 3 + 1;
        for (int i = gap; i < N; i++)
        {
            int key = A[i];
            int start = i - gap;
            while (start >= 0 && key <= A[start])
            {
                A[start + gap] = A[start];
                start -= gap;
            }
            A[start + gap] = key;
        }
    }
}

四、简单排序

  • 简单排序非常简单,就是遍历N次数组,每次从当前待排序列中选出最小的放在已拍好序列的末尾。
  • 平均时间复杂度:O(N^2)
template<typename ElementType>
void Select_sort(ElementType A[], int N)
{
    int begin = 0;
    int end = N - 1;
    int max = 0;
    int min = 0;
    for (int i=0;i<N-1;i++)
    {
        max = begin;
        min = begin;
        for (int i = begin; i <= end; i++)
        {
            if (A[i] >= A[max])
            {
                max = i;
            }
            if (A[i] < A[min])
            {
                min = i;
            }
        }
        if (max == begin && min == end)
        {
            swap(A[max], A[end]);
            continue;
        }
        if (max == begin)
        {
            swap(A[max], A[end]);
            swap(A[min], A[begin]);
            continue;
        }
        if (min == end)
        {
            swap(A[min], A[begin]);
            swap(A[max], A[end]);
            continue;
        }
        swap(A[min], A[begin]);
        swap(A[max], A[end]);
        begin++;
        end--;
    }
}

五、堆排序

六、冒泡排序

  • 冒泡排序是一种交换排序,保证右边的元素始终大于左边的元素。
  • 具体做法是将序列当中的元素依次两两比较,如果左侧大于右侧,则交换元素位置,直到没有反序的记录为准。
  • 时间复杂度为O(N^2)
void Bubble_Sort(ElementType A[], int N)
{
    int end = N - 1;
    while (end > 0)
    {
        bool exchange = false;
        for (int i = 0; i < end; i++)
        {
            if (A[i] > A[i + 1])
            {
                swap(A[i], A[i + 1]);
                exchange = true;
            }
        }
        if (!exchange)
        {
            return;
        }
        else
        {
            exchange = false;
        }
        end--;
    }
}

七、快速排序

  • 快速排序基于冒泡排序进行了改进,因为速度快成为最常用的排序算法之。
  • 快速排序基本分治法的思想:

1) 在数组中任取一个元素pivot作为基准,并预设L、R两个下标指针。


2) 移动R下标向左,比较当前数字与pivot比较,如果小于pivot则放在左侧,反之放在右侧

3a) 如果上一次的元素有操作移动位置,则交替移动L下标向右比较,否则继续移动R向左。

3b) 就这样交替移动,直到两个下标重合,就是pivot的位置,这样就完成第一次排序。

4) 第一次排序后,获得两个子序列,pivot左侧元素全部小于pivot,右侧全部大于等于pivot。

5a) 对左右两个子序列递归重复1、2、3的操作。


5b) 如果序列长度为一,则认为是顺序序列。

6) 最终获得顺序数组

  • 基于指针数,可以分为单路快速排序,双路快速排序,三路快速排序。
  • 时间复杂度为O (N\log N)
template<typename ElementType>
void Quick_Sort(ElementType A[], int low,int high)
{
    if (low >= high)return;
    int i, j, temp;
    i = low, j = high;
    int pivot = A[low]; //选择最左边的元素为pivot
    while (i < j)
    {
        while (A[j] >= pivot && i < j)
            j--;
        while (A[i] <= pivot && i < j)
            i++;
        if (i < j)
        {
            temp = A[i];
            A[i] = A[j];
            A[j] = temp;
        }
    }

    A[low] = A[i];
    A[i] = pivot;
    Quick_Sort(A,low,i-1);
    Quick_Sort(A, i + 1, high);
}

八、归并排序

  • 归并排序是建立在归并操作上的排序算法。
  • 快速排序类似归并排序也采用了分治法。
  • 基本操作是:

1) 将序列每次折半拆分,即分解。
2) 将划分后的序列段两两排序合并,即合并。
3) 重复2)直到只剩下一组。

  • 时间复杂度为O (N\log N)
template<typename ElementType>
//合并
void Merge(int A[], int low, int high, int mid)
{
    int n = high - low + 1;
    ElementType* temp = new ElementType[n];
    int i = 0;
    int left = low;
    int right = mid + 1;
    while (left <= mid && right <= high)
    {
        temp[i++] = A[left] <= A[right] ? A[left++] : A[right++];
    }
    while (left <= mid)
    {
        temp[i++] = A[left++];
    }
    while (right <= high)
    {
        temp[i++] = A[right++];
    }
    for (int j = 0; j < n; ++j)
    {
        A[low + j] = temp[j];
    }
    delete[] temp;
}

template<typename ElementType>
//分解
void Merge_Sort(ElementType A[], int low, int high)
{
    if (low == high)return;
    int mid = (low + high) / 2;
    Merge_Sort<ElementType>(A, low, mid);
    Merge_Sort<ElementType>(A, mid + 1, high);
    Merge<ElementType>(A, low, high, mid);
}

九、基数排序

  • 基数排序通过序列中各个元素的值,对排序的N个元素进行若干趟的“分配”与“收集”来实现排序。

1) 分配:将L[i]中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中。



2) 收集:当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列L[ ]。

3) 对新形成的序列L[]重复执行分配和收集元素中的十位、百位...直到分配完该序列中的最高位。

  • 时间复杂度为O(N\log(r)M)
template<typename ElementType>
//计算最大位数
int maxbit(ElementType A[], int N)
{
    int d = 1;
    int p = 10;
    for (int i = 0; i < N; i++)
    {
        while (A[i] >= p)
        {
            p *= 10;
            ++d;
        }
    }
    return d;
}


template<typename ElementType>
void Radix_Sort(ElementType A[], int N)
{
    int d = maxbit<ElementType>(A, N);
    ElementType* temp = new ElementType[N];
    int count[10];
    int k;
    int radix = 1;
    for (int i = 1; i <= d; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            count[j] = 0; //清空计数器
        }
        for (int j = 0; j < N; j++)
        {
            k = (A[j] / radix) % 10; //每个桶中的记录数
            count[k]++;
        }
        for (int j = 1; j < 10; j++)
        {
            count[j] = count[j - 1] + count[j];
        }
        for (int j = N - 1; j >= 0; j--)
        {
            k = (A[j] / radix) % 10;
            temp[count[k] - 1] = A[j];
            count[k]--;
        }
        for (int j = 0; j < N; j++)
        {
            A[j] = temp[j];
        }
        radix *= 10;

    }
    delete[] temp;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容