交换排序详解

1.冒泡排序

起泡排序,别名“冒泡排序”,该算法的核⼼思想是将⽆序表中的所有记录,通过两两⽐较关键字, 得出升序序列或者降序序列。

例如,对⽆序表{49,38,65,97,76,13,27,49}进⾏升序排序的具体实现过程如图所示:

上图所示是对⽆序表的第⼀次起泡排序,最终将⽆序表中的最⼤值 97 找到并存储在表的最后⼀个位置。

由于 97 已经判断为最⼤值,所以第⼆次冒泡排序时就需要找出除 97 之外的⽆序表中的最⼤值,⽐ 较过程和第⼀次完全相同。

通过⼀趟趟的⽐较,⼀个个的“最⼤值”被找到并移动到相应位置,直到检测到表中数据已经有序,或者⽐较次数等同于表中含有记录的个数,排序结束,这就是起泡排序。

2. 关于冒泡的优化

基本的冒泡排序的实现⽅式,就是两个for循环,持续⽐较和交换。这种实现⽅式有⼀个明显的弊 端,就是不论数组是否有序,两层 for 循环都要执⾏⼀遍,⽽我们是希望数组有序的时候,仅进⾏⼀轮判断,或者⼀轮都不进⾏(当然不判断,排序算法是不能知道数组是否有序的)。

⼀次优化:
这⾥我们增加了⼀个标识数组是否有序 ,当冒泡排序过程中没有交换操作时, swapped = false , 也意味着数组有序;否则数组⽆序继续进⾏冒泡排序。不要⼩看这个变量奥,因为这个变量, 当数组有序的时候,冒泡排序的时间复杂度将降⾄ (因为其只需要执⾏⼀遍内层的 for 循环就可以结束冒泡排序),没有这个变量,数组有序也需要很大的时间复杂度。

二次优化:
        确定出已经有序部分和⽆序部分的边界

//bubbleSort.h
#ifndef SORT_BUBBLESORT_H
#define SORT_BUBBLESORT_H
#include "../sortHelper.h"

void bubbleSortV1(SortTable* table);
void bubbleSortV2(SortTable* table);
void bubbleSortV3(SortTable* table);

#endif //SORT_BUBBLESORT_H

 

//bubbleSort.c
#include "bubbleSort.h"
/*找最大值,把最大的放到了右边
 * j[0,n-1] i>i+1 swap
 * j[0,n-2] i>i+1 swap
 * */
void bubbleSortV1(SortTable* table)
{
    for(int i=0;i<table->length;++i)
    {
        for(int j=0;j<table->length-i-1;++j)
        {
            if(table->data[j].key>table->data[j+1].key)
            {
                swapElement(&table->data[j],&table->data[j+1]);
            }
        }
    }
}
//引入是否有序标记,当发现某一轮冒泡时有序,就退出循环
void bubbleSortV2(SortTable* table)
{
    for(int i=0;i<table->length;++i)
    {
        int isSorted=0;
        for(int j=0;j<table->length-i-1;++j)
        {
            if(table->data[j].key>table->data[j+1].key)
            {
                swapElement(&table->data[j],&table->data[j+1]);
                isSorted=1;
            }
        }
        if(isSorted==0)break;
    }
}
/* 引入newIndex,标记最后一次交换的位置,下次冒泡排序是,只需要遍历到这个newIndex位置处 */
void bubbleSortV3(SortTable* table)
{
    int newIdex;
    int n=table->length;
    do{
        newIdex=0;
        for(int i=0;i<n-1;++i)
        {
            if(table->data[i].key>table->data[i+1].key)
            {
                swapElement(&table->data[i],&table->data[i+1]);
                newIdex=i+1;
            }
            n=newIdex;
        }
    }while(newIdex>0);

}

3. 快速排序

快速排序算法是在起泡排序的基础上进⾏改进的⼀种算法,其实现的基本思想是:通过⼀次排序将 整个⽆序表分成相互独⽴的两部分,其中⼀部分中的数据都⽐另⼀部分中包含的数据的值⼩,然后继续 沿⽤此⽅法分别对两部分进⾏同样的操作,直到每⼀个⼩部分不可再分,所得到的整个序列就成为了有序序列。

//quickSort.h
#ifndef SORT_QUICKSORT_H
#define SORT_QUICKSORT_H
#include "../sortHelper.h"
/* 快速排序:
 * 冒泡排序在每一轮中,只把1个元素冒泡到数组的一端
 * 1. 快速排序,在每一轮挑选一个基准元素,让其他比它大的元素移动到一边,比它小的移动到另外一边
 * 从而把表元素拆分成两个部分。
 * 2. 基准元素的选择
 * 随机选择一个元素作为基准元素,并让基准元素和第一个元素进行交换
 * 3. 元素交换
 * 分为双边循环法和单边循环法
 * */
void quickSortV1(SortTable *table);
void quickSortV2(SortTable *table);
#endif //SORT_QUICKSORT_H
#include <stdlib.h>
#include <time.h>
#include "quickSort.h"
/* 单边循环法,设置一个mark标记,表示小于基准元素的区域边界
 * 1. 从基准元素的下一个位置开始遍历数组
 * 2. 如果遍历到的元素大于基准元素,就继续向后遍历
 * 3. 如果遍历到的元素小于基准元素
 * 	3.1 把mark++
 * 	3.2 把当前mark的值(大于基准元素)和找出来小的值交换
 * 4. 最后把mark和基准元素交换位置
 * */
static int partitionSingle(SortTable *table, int startIndex, int endIndex) {
    keyType tmp = table->data[startIndex].key;
    int mark = startIndex;

    for (int i = startIndex + 1; i <= endIndex; ++i) {
        if (table->data[i].key < tmp) {
            mark++;
            swapElement(&table->data[mark], &table->data[i]);
        }
    }
    swapElement(&table->data[mark], &table->data[startIndex]);
    return mark;
}
/* 双边循环法
 * 1. 设置左、右标记,基准点认为最左边的元素
 * 2. 先从右标记开始,让right标记指向的元素和基准元素比较,如果大于pivot,则左移
 * 		如果小,则停止right标记移动,切换到left标记移动
 * 3. 在left标记中,如果小于基准,则右移,如果大于基准,那么left标记就停止移动
 * 4. 此时交换左右的值
 * 5. 接下来继续第二轮循环
 * 6. 最后,当left和right相等时,基准就找到了,那么将基准值放入这个位置
 * */
static int partitionDouble(SortTable *table, int startIndex, int endIndex) {
    int pivot = startIndex;
    int left = startIndex;
    int right = endIndex;
    srand(time(NULL) + 1);
    swapElement(&table->data[startIndex], &table->data[rand() % (endIndex - startIndex) + startIndex]);
    while (left != right) {
        while (left < right && table->data[right].key > table->data[pivot].key) right--;
        while (left < right && table->data[left].key <= table->data[pivot].key) left++;
        if (left < right) {
            swapElement(&table->data[right], &table->data[left]);
        }
    }
    swapElement(&table->data[pivot], &table->data[left]);
    return left;
}

static void quickSort1(SortTable *table, int startIndex, int endIndex) {
    if (startIndex >= endIndex) {
        return;
    }
    int pivot = partitionDouble(table, startIndex, endIndex);
    quickSort1(table, startIndex, pivot - 1);
    quickSort1(table, pivot + 1, endIndex);
}

static void quickSort2(SortTable *table, int startIndex, int endIndex) {
    if (startIndex >= endIndex) {
        return;
    }
    int pivot = partitionSingle(table, startIndex, endIndex);
    quickSort1(table, startIndex, pivot - 1);
    quickSort1(table, pivot + 1, endIndex);
}

void quickSortV1(SortTable *table) {
    quickSort1(table, 0, table->length - 1);
}

void quickSortV2(SortTable *table) {
    quickSort2(table, 0, table->length - 1);
}
#include "bubbleSort.h"
#include "quickSort.h"

int main() {
    int n = 10000;
    SortTable *table1 = generateRandomArray(n, 0, n);
    SortTable *table2 = copySortTable(table1);
    SortTable *table3 = copySortTable(table1);

    testSort("quick Sort V1", quickSortV1, table1);
    //testSort("quick Sort V2", quickSortV2, table2);
    testSort("Bubble Sort V3", bubbleSortV3, table3);

    releaseSortTable(table1);
    releaseSortTable(table2);
    releaseSortTable(table3);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值