【数据结构】十一种排序算法C++实现

   练习了十一种排序算法的C++实现:以下依次为,冒泡、选择、希尔、插入、二路归并、快排、堆排序、计数排序、基数排序、桶排序,可建立sort.h和main.cpp将代码放入即可运行。如有错误,请指出更正,谢谢交流。

   Python版本实现:王培军https://blog.csdn.net/qq_33287645/article/details/81702993

// sort.h
# include <iostream>
# include "chain.h"
using namespace std;

typedef struct ChainNode {
    int value;
    struct ChainNode *next;
    //ChainNode() {}
    ChainNode(int value, ChainNode * next) {
        this->value = value;
        this->next = next;
    }
} CNode, *Chain;

void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

void blob(int arr[], int len) {
    for (int i = len - 1; i > 0; i--)
        for (int j = 0; j < i; j++)
            if (arr[j] > arr[j + 1])
                swap(arr[j], arr[j + 1]);
}

void select(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        int min = i;
        for (int j = i; j < len; j++)
            if (arr[j] < arr[min])
                min = j;
        swap(arr[i], arr[min]);
    }
}

void shell(int arr[], int len) {
    // https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F
    int i, j, step;
    for (step = len >> 1; step > 0; step >>= 1) // 初始步长为n/2,每次对步长取半直到步长达到1
        for (i = step; i < len; i++) { // 从第step个数开始,依次将后面的数与前面的step步长数进行比较
            int temp = arr[i];  // 将大于当前值的希尔序列中的值,不断地进行后移,最终将当前值放到希尔序列的合适位置
            for (j = i - step; j >= 0 && arr[j] > temp; j -= step)  // 初始为当前位置的第前step个数,比较arr[j + step] 与 arr[j]
                arr[j + step] = arr[j];  // 希尔序列中前面的值,往后移(第一次会冲掉初始值temp)
            arr[j + step] = temp;
        }
}

void insert(int arr[], int len) {
    for (int i = 1; i < len; i++) {
        int temp = arr[i];
        int j;
        for (j = i - 1; j >= 0 && temp < arr[j]; j--)
            arr[j + 1] = arr[j];
        arr[j + 1] = temp;
    }
}

void merge(int arr[], int temp[], int start, int end) {
    if (start >= end)
        return;

    int mid = (start + end) / 2;
    int start1 = start, end1 = mid;
    int start2 = mid + 1, end2 = end;
    merge(arr, temp, start1, end1);
    merge(arr, temp, start2, end2);

    int k = start;  // 从start起始位置开始合并进temp中
    while (start1 <= end1 && start2 <= end2) { // 对两个数组同时处理,小的放到temp中
        temp[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
    }
    while (start1 <= end1)  // 归并的两个数组不一定长,如果start1剩下,则合并进temp
        temp[k++] = arr[start1++];
    while (start2 <= end2)  // 如果第二个数组剩下,将其合并进temp
        temp[k++] = arr[start2++];

    for (int k = start; k <= end; k++)  // 拷贝回原数组
        arr[k] = temp[k];
}

void quick(int arr[], int start, int end) {
    if (start > end)
        return;

    int left = start, right = end;
    int mid = arr[left];  // 先在左边第一个数挖个坑

    while (left < right) {
        while (left < right && arr[right] >= mid)  // 从右边开始找小于中间值得第一个数
            right--;
        if (left < right) {
            arr[left] = arr[right]; // 将‘右边的小于中间值的那个数’ 填入到 ‘左边的坑(初始为第一个)’
            left++;  // 左边坑被填好之后,处理下一个,往右挪一下
        }

        while (left < right && arr[left] <= mid)
            left++;
        if (left < right) {
            arr[right] = arr[left];  // 将‘左边大于中间中的数’ 填入到 ‘右边被挖出来的坑’
            right--; // 右边坑被填好之后,处理下一个,往做挪一下
        }
    }

    arr[left] = mid; // //退出时,left等于right。将x填到这个坑中。这个坑也是中间数。

    quick(arr, start, left - 1);  // 中间的数不需要再排序
    quick(arr, left + 1, end);
}

void max_heapify(int arr[], int start, int end) {
    // 初始化dad和son
    int dad = start;
    int son = dad * 2 + 1;

    while (son <= end) { // 在不停地向下调整过程中,son不越过堆尾的前提下
        if (son + 1 <= end && arr[son + 1] > arr[son])  // 在不越过堆尾的前提下,找出最大的孩子节点的位置
            son++;
        if (arr[dad] > arr[son])  // 如果父节点值大于最大孩子节点值,则返回
            return;
        else {
            swap(arr[dad], arr[son]); // 交换父子节点值
            dad = son;  // 重新赋值dad和son,进一步的向下调整
            son = dad * 2 + 1;
        }
    }
}

void heap(int arr[], int len) {
    // https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F
    // 建立初始大根堆,只有这里考虑的是数量号len,其余地方全部考虑索引号
    for (int i = len / 2 - 1; i >= 0; i--)  // 从最后一个父节点,依次向前开调整,len/2 - 1 可以确定最后一个父节点的索引位置。这里使用节点数量len确定索引位置i,而不是索引数len-1(67确定索引2,(位置Wie3))
        max_heapify(arr, i, len - 1);

    // 逆序生成堆排序结果。依次取出堆顶元素,与最近一个尾部交换,重新调整大根堆,此时只需要调整堆顶一个元素即可
    for (int i = len - 1; i > 0; i--) {
        swap(arr[0], arr[i]); // 拿下堆顶元素,与堆尾元素交换
        max_heapify(arr, 0, i - 1); // 调整当前堆尾之前的所有元素为大根堆,只需要向下调整0号位置
    }
}

void count(int arr[], int len) {
    // https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F
    // 找到数组中的最大值最小值
    int min, max;
    min = max = arr[0];
    for (int i = 0; i < len; i++) {
        if (arr[i] > max)
            max = arr[i];
        else if (arr[i] < min)
            min = arr[i];
    }

    // 确定最大值,最小值之间的范围差,并初始化相应大小的数组
    int lenC = max - min + 1;
    int *arrC = new int[lenC];
    for (int k = 0; k < lenC; k++)
        arrC[k] = 0;

    // 遍历原始数组,将对应的新计数数组上的值+1,每个值可以看作是一个桶
    for (int i = 0; i < len; i++) {
        int index = arr[i] - min;
        arrC[index] += 1;
    }

    // 遍历计数数组,输出上面关联的数值(桶里的所有个数的数值)
    for (int k = 0, i = 0; k < lenC; k++) {
        if (arrC[k] != 0) {
            for (int j = 0; j < arrC[k]; j++)
                arr[i++] = k + min;  // 这里的k+min就是真实值
        }
    }
}

int getMaxBit(int arr[], int len) {
    int max = arr[0];
    for (int i = 0; i < len; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }        

    int maxBit = 0;
    while (max >= 10) {
        max /= 10;
        maxBit++;
    }
    return ++maxBit;
}

void radix(int arr[], int len) {
    // 求最大位数,个位为1,十位为2...
    int maxBit = getMaxBit(arr, len);
    cout << maxBit << endl;

    int *temp = new int[len];

    // 从小到达,遍历每一位进行排序
    int radix = 1;
    for (int i = 0; i < maxBit; i++) {

        // 计数器清空
        int buckets[10] = { 0 };

        // 遍历所有数据,将其放入到桶中
        for (int j = 0; j < len; j++)
        {
            int d = arr[j] / radix % 10;
            buckets[d] ++;  
        }

        // 修订bucket数组中值,索引位0的数值,代表0桶内有多少个数,索引位1上的值,代表1+0上的桶内一共有多少个数
        // 使得bucket中的数组值,变成其索引所对应基数的偏移量,注意,默认均加了0号索引上的数量。
        for (int j = 1; j < 10; j++) {
            buckets[j] = buckets[j] + buckets[j - 1];
            cout << buckets[j] << endl;
        }

        // 将bucket中的数值存储到temp数组中
        for (int j = len - 1; j >= 0; j--) {
            int d = arr[j] / radix % 10;  // 找到桶索引,即基数
            temp[buckets[d] - 1] = arr[j]; // 桶索引中的数从1开始计数的,比如索引0上1个数,其值为1,但实际temp中索引从0开始。所以减1.
            buckets[d]--; // 存储一个,则对应的基数上数值个数减一。这里如果有6、7、8三个数,先赋值给index为7的位置,然后6,然后5.
        }

        // 将temp数组赋值给原数组,此时,完成低位上的排序有序
        for (int j = 0; j < len; j++)
            arr[j] = temp[j];

        radix *= 10;
    }

    delete []temp;    
}

void bucket(int arr[], int len) {
    // 初始化及设置固定空桶数
    int bucketNum = 10;  
    CNode **bucketTable = new CNode *[bucketNum];  // 分配动态二维(指针)数组,bucketNum个CNode*数组
    // vector<vector<CNode *> > bucketTable(bucketNum,vector<CNode *>(1)?));  // vector方法
    for (int i = 0; i < bucketNum; i++)  // 为指针数组的每个元素,分配一个元素
        bucketTable[i] = new CNode(0, NULL);  // 初始化桶内元素计数,用第一个CNode?

    for (int i = 0; i < len; i++) {
        // 计算当前元素映射后的桶号
        int index = arr[i] / bucketNum;  
        CNode *p = bucketTable[index];

        // 将数据放到对应的空桶中, 并进行排序
        CNode *pNode = new CNode(arr[i], NULL);  // 添加数组中的元素到桶中
        if (p->value == 0) {  // 该桶中还没有数据  
            bucketTable[index]->next = pNode;
            (bucketTable[index]->value)++;  // 桶中数量加1
        }
        else {
            // 链表结构的插入排序
            while (p->next != NULL && p->next->value <= pNode->value)
                p = p->next;
            pNode->next = p->next;
            p->next = pNode;
            (bucketTable[index]->value)++;
        }
    }

    // 拼接不为空的桶中的数据,得到结果
    for (int i = 0; i < bucketNum; i++) {
        CNode *p = bucketTable[i]->next;  // bucketTable[i]这个CNode中包含了计数,其第一个next为第一个元素
        while (p) {
            cout << p->value << ",";
            p = p->next;
        }
    }

    for (int i = 0; i < bucketNum; i++)
    {
        delete[bucketNum]bucketTable[i];  // 先释放指针数组的每个元素指向的数组
        bucketTable[i] = NULL;
    }
    delete[bucketNum]bucketTable;  // 再释放该指针数组
    bucketTable = NULL;

    // 创建二维动态数组
    // https://blog.csdn.net/samuelcoulee/article/details/8674388
}

// main.cpp
# include <iostream>
# include "sort.h"
using namespace std;

void testSort() {
    int temp[8];
    int arr[] = { 7, 9, 1, 2, 6, 0, 5 , 8 };
    int len = sizeof(arr) / sizeof(int);
    for (int i = 0; i < len; i++)
        cout << arr[i] << ", ";

    // blob(arr, len);
    // select(arr, len);
    // insert(arr, len);
    // merge(arr, temp, 0, len-1);
    // quick(arr, 0, len - 1);
    // heap(arr, len);
    // shell(arr, len);
    // count(arr, len);
    // radix(arr, len);
    bucket(arr, len);

    for (int i = 0; i < len; i++)
        cout << arr[i] << ", ";
}


int main() {
    testSort();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值