基本排序算法的实现

前言

  • 数据
    Item[1:len]
    Item[0] -->不存储数据 (功能:哨兵节点OR暂存数据

内部排序

排序过程在内存中实现

插入排序

查找位置,移动元素,插入

直接插入排序

/**
 * 直接插入排序  顺序存储,链式存储
 * 0:i-1    查找位置 j+1--->顺序查找
 * j+1:i-1  数据移动
 *
 */
void InsertSort(Item *data, int len) {
    int i, j;
    for (i = 2; i < len + 1; i++) {
        data[0] = data[i];
        for (j = i - 1; data[0].value < data[j].value; j--) {
            data[j + 1] = data[j];
        }
        data[j + 1] = data[0];
    }
}

稳定
时间复杂度:O(n2)
空间复杂度:O(1)

最好:有序 O(n)           无查找,移动
最坏:逆序 O(n2)

折半插入排序

/**
 * 折半插入排序  顺序存储
 * 0:i-1    查找位置 j+1--->顺序查找
 * j+1:i-1  数据移动
 *
 */
void BinaryInsertSort(Item *data, int len) {
    int low, high, mid;
    int i, j;
    for (i = 2; i < len + 1; i++) {
        data[0] = data[i];
        low = 1;
        high = i - 1;
        while (low <= high) {
            mid = (low + high) / 2;
            if (data[mid].value > data[0].value)
                high = mid - 1;
            else
                low = mid + 1;
        }


        for (j = i - 1; j >= low; j--)
            data[j + 1] = data[j];

        data[low] = data[0];
        /* data[high + 1] = data[0];*/
    }
}

稳定
时间复杂度:O(n2)
空间复杂度:O(1)

希尔排序

/**
 * 希尔排序(不稳定)  顺序存储
 * 步长选取 d:
 *
 *      d1=n/2    d2=d1/2
 *
 * 最坏O(n^2)   一般O(n^1.3)
 *
 */
void ShellSort(Item *data, int len) {
    int j;
    for (int d = len / 2; d >= 1; d = d / 2) {
        for (int i = d + 1; i <= len; ++i) {
            if (data[i].value < data[i - d].value) {
                data[0] = data[i];//暂存作用,非哨兵

                for (j = i - d; j > 0 && data[0].value < data[j].value; j -= d)
                    data[j + d] = data[j];

                data[j + d] = data[0];
            }
        }
    }
}

不稳定
时间复杂度:O(n2)   O(n1.3
空间复杂度:O(1)

交换排序

一趟排序过程通过不断的交换,确定其中一个元素的位置

冒泡排序

/**
 * 冒泡排序 顺序存储,链式存储
 * 一趟冒泡一个数据到达最终位置(n-1次冒泡)
 *
 */
void BubbleSort(Item *data, int len) {
    for (int i = 1; i < len; i++) {
        _Bool flag = FALSE;
        for (int j = len; j > i; j--) {
            //查找逆序进行交换
            if (data[j - 1].value > data[j].value) {
                data[0] = data[j];
                data[j] = data[j - 1];
                data[j - 1] = data[0];
                flag = TRUE;
            }
        }

        if (flag == FALSE)
            return;
    }
}

稳定
时间复杂度:O(n2)
空间复杂度:O(1)

最好:有序 O(n)           无查找,移动
最坏:逆序 O(n2)

快速排序

依赖划分,构建二叉树

/**
 * 快速排序 顺序存储,链式存储   不稳定
 * 一次划分过程,一个元素到位
 * Partition 划分
 * QuickSortImpl
 */
void QuickSort(Item *data, int len) {
    QuickSortImpl(data, 1, len);
}

void QuickSortImpl(Item *data, int low, int high) {
    if (low < high) {
        int mid = Partition(data, low, high);
        QuickSortImpl(data, low, mid - 1);
        QuickSortImpl(data, mid + 1, high);
    }
}

int Partition(Item *data, int low, int high) {
    data[0] = data[low];
    while (low < high) {
        while (low < high && data[high].value >= data[0].value)
            high--;
        data[low] = data[high];
        while (low < high && data[low].value <= data[0].value)
            low++;
        data[high] = data[low];
    }
    data[low] = data[0];
    return low;
}

不稳定
时间复杂度:O(nlog2n)
空间复杂度:O(log2n)

最坏:有序,逆序

树的高度max 时间复杂度–> O(n2) 空间复杂度—> O(n)

选择排序

每次选择出当前未排序状态下的最小OR最大

直接选择排序

/**
 * 直接排序 顺序存储,链式存储   不稳定
 * 每次选择一个最小的数据
 */
void SelectSort(Item *data, int len) {
    for (int i = 1; i < len; ++i) {
        int min = i;
        for (int j = i + 1; j <= len; j++) {
            if (data[j].value < data[min].value)
                min = j;
        }

        if (min != i) {
            data[0] = data[i];
            data[i] = data[min];
            data[min] = data[0];
        }
    }
}

不稳定
时间复杂度:O(n2)
空间复杂度:O(1)

堆排序

完全二叉树的顺序存储

/**
 * 堆排序  顺序存储,链式存储 不稳定
 *
 * @param data
 * @param len
 */
void HeapSort(Item *data, int len) {
    BuildMaxHeap(data, len);
    for (int i = len; i > 1; i--) {
        data[0] = data[i];
        data[i] = data[1];
        data[1] = data[0];

        AdjustDown(data, 1, i - 1);
    }
}

void BuildMaxHeap(Item *data, int len) {
    for (int i = len / 2; i > 0; i--) {
        AdjustDown(data, i, len);
    }
}

void AdjustDown(Item *data, int parent, int len) {
    data[0] = data[parent];

    int i;
    for (i = parent * 2; i <= len; i = i * 2) {
        if (i < len && data[i].value < data[i + 1].value)
            i++;
        if (data[0].value >= data[i].value)
            break;
        else {
            data[parent] = data[i];
            parent = i;
        }
    }
    data[parent] = data[0];
}

不稳定
时间复杂度:O(nlog2n)
空间复杂度:O(1)

归并排序

/**
 * 归并排序
 */
void MergeSort(Item *data, int len) {
    MergeSortImpl(data, 1, len);
}

void MergeSortImpl(Item *data, int low, int high) {
    if (low < high) {
        int mid = (low + high) / 2;
        MergeSortImpl(data, low, mid);
        MergeSortImpl(data, mid + 1, high);
        Merge(data, low, mid, high);
    }
}

/**
 * 合并 i:mid     mid+1:high
 */
void Merge(Item *data, int low, int mid, int high) {
    Item *buffer = (Item *) malloc((high + 1) * sizeof(Item));
//    memset(buffer, 0, (high + 1) * sizeof(Item));
    for (int i = low; i <= high; ++i) {
        buffer[i] = data[i];
    }

    int i = low, j = mid + 1;
    int k = i;
    for (; i <= mid && j <= high; k++) {
        if (buffer[i].value <= buffer[j].value)
            data[k] = buffer[i++];
        else
            data[k] = buffer[j++];
    }

    while (i <= mid)
        data[k++] = buffer[i++];
    while (j <= high)
        data[k++] = buffer[j++];

    free(buffer);
    buffer=NULL;
}

稳定
k时间复杂度:O(nlog2n)
空间复杂度:O(n)

基数排序

数字有d位 ,每位采用r进制

一趟排序需要分配r个队列
进行d趟分配和收集,一趟分配需要O(n),一趟收集需要O®

稳定
时间复杂度:O(d(n+r))
空间复杂度:O( r )

外部排序

处理大批量数据

首先,根据内存缓冲区大小,将外存上含n个记录的文件分成若干长度为h的子文件,依次读入内存,并利用内部排序算法进行排序,使得初始归并段有序,并将有序的初始归并段写回外存

然后,对初始归并段进行逐趟归并,使归并段逐渐由小到大,直至得到整个有序文件为止。

外部排序时间内部排序时间+外村读写时间+内部归并时间


文件记录:n     归并段长:h

归并段数:r= &lceil;n/h&rceil;


采用m路归并算法:

归并趟数: ⌈logmr
整体文件I/O:⌈logmr⌉ +1

减少IO时间

  1. 增加归并路数
  2. 减少归并段数

失败树

目的:增加归并路数

前因

S趟归并需要比较的次数:

S(n-1)(m-1)=⌈logmr⌉ (n-1)(m-1)

因此不能盲目增加归并路数m!!!

失败树

增加m并且使比较时间不会因此增加

失败树

比较次数:
⌈logmr⌉ (n-1) ⌈log2m⌉ = (n-1) ⌈log2r

与m无关,故此时增加归并路数,对比较次数无影响

过程

but: 怎加m 会对内存缓冲区的个数有要求。

置换选择排序

减小r的数目 获取长度不等的归并段

待排序文件:17,23,46,53,11,9,78,8,56,36,21,96

输出文件FO工作区WA输入文件FI
17,23,4653,11,9,78,8,56,36,21,96
1723,46,5311,9,78,8,56,36,21,96
17,2346,53,119,78,8,56,36,21,96
17,23,4653,11,978,8,56,36,21,96
17,23,46,5311,9,788,56,36,21,96
17,23,46,53,7811,9,856,36,21,96
17,23,46,53,78 #11,9,856,36,21,96
811,9,5636,21,96
8,911,56,3621,96
8,9,1156,36,2196
8,9,11,2156,36,96
8,9,11,21,36,56,96#

两个初始归并段:

17,23,46,53,78
8,9,11,21,36,56,96

最佳归并树

结合哈夫曼树 (m阶哈夫曼树:树中节点的度数为m或0)

a. 通过置换选择排序获取了初始归并段
b. 构造成m阶哈夫曼树

n0+nm -1=度数=nm * m

(m-1)nm =n0-1

nm=(n0-1)/(m-1)

nm整数

补充段数目:(n0-1)%(m-1)
n0=初始归并段数目+ 补充段数目

例. 三叉树T中有6个叶节点的权分别为2,3,4,5,6,7,T的带权外部路径长度最小为: 46 (构造3阶哈夫曼树)

补充:5%2=1 补充1个段

13+18+15=46

最小:

6,7
4,5
0,2,3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值