几种常见的排序算法

C++实现常见的排序算法:

选择排序、插入排序、归并排序、快速排序、冒泡排序
1、选择排序

首先找到数组中最小的元素,其次,将它和数组中第一个元素交换位置(如果第一个元素就是最小元素那么就和自己交换)。然后在剩下的元素中找到最小的元素,将它和数组的第二个元素交换位置。如此往复,直到将整个数组排序,由于它在不断的选择剩余元素中的最小者,所以将它称为选择排序

特点:

1)运行时间和输入无关,这指的是为了找出最小的元素扫描一次不会为下一次扫描提供什么信息,就算是有序的数组也和无序的数组排序时间一样长。其他算法更善于利用输入的初始状态。

2)数据移动最少,用了N次交换,交换次数和数组的大小是线性关系。其他算法并不具备此特征。(线性对数或者平方级别)

时间效率取决于比较的次数 对于长度为N的数组,大约需要(N^2/2)次比较和N次交换

C++代码:
void selection_sort(vector<int>& nums){
    // 将数组升序排列
    int min, n = nums.size();
    // 每次循环会排列一个位置的最小值 最后一个位置的值不用再循环 已经确定
    // 第一层循环都是确定即将要排好的元素  例如第一层的第一次循环就是确定要排好序数组的第一个位置的元素 以此类推,循环n次,整个数组也就全部排好序了
    for(int i = 0; i < n - 1; i++){
        min = i;
        // 将nums[i]和nums[i+1...n-1]中的最小的元素交换
        // 第二层循环是针对我们要找的元素的具体操作 具体算法实现
        for(int j = i + 1; j < n; j++){
            // 更新本次循环最小元素的下标
            if(nums[j] < nums[min]){
                min = j;
            }
        }
        // 找到此次循环的最小值并 交换顺序
        swap(nums[min],nums[i]);
	}
}
2、插入排序

与选择排序一样,当前索引左边元素都是有序的,但最终位置还不确定,但索引到达数组最右端时,数组排序就完成了。

插入排序时间效率取决于输入元素中的初始顺序,例如对一个很大的且其中的元素已经有序的数组比随机顺序或者逆序数组快得多。

插入排序对于部分有序数组十分高效,也很适合小规模数组

C++代码:
void insertion_sort(vector<int>& nums){
    //将数组nums升序排列
    int n = nums.size();
    for(int i = 0;i < n; ++i){
        // 将nums[i]插入到nums[i]之前的所有元素之间  以保证索引左侧所有元素是有序的 只有当j>0并且当前元素小于前面的任一元素时才进行交换
        for(int j = i;j > 0 && nums[j] < nums[j - 1]; --j){
            swap(nums[j],nums[j-1]);
        }
    }
}
3、归并排序

将两个有序的数组归并成一个更大的有序数组,递归的将一个数组分成两本分别排序,然后将结果归并起来。分治思想

归并排序将任意长度为N的数组排序所需时间和NlogN成正比,主要缺点是需要额外空间和N成正比

C++代码:
//归并所需要的辅助数组temp  这里的l代表数组最左边的元素索引如0  r代表数组的大小 如:nums.size() 
// 这里需要注意的是传入的r代表数组元素的size 如果传入数组最右边元素的索引也可以 代码需要微调,主要是判断条件越界这里 > = <= 
void merge_sort(vector<int>& nums,int l, int r,vector<int>& temp){
    if(l + 1 >= r){
        return;
    }
    // divide 分  分别对数组进行排序
    int m = l + (r - l)/2;
    merge_sort(nums, l, m, temp);
    merge_sort(nums, m, r, temp);
    // conquer  治 对排好序的数组进行合并
    int p = l, q = m, i = l;
    // 当合并的两个数组索引不全越界时
    while(p < m || q < r){
        // 右边元素用尽 (q=r已经越界)  或者 左边元素还有并且右边元素大于左边元素时 取用左边元素
        if(q >= r || (p < m && nums[p] <= nums[q])){
            temp[i++] = nums[p++];
        }else{
            // 否则取用右边元素放入临时数组
            temp[i++] = nums[q++];
        }
    }
    //遍历临时数组  将所有元素全部放入原来数组中
    for(i = l; i < r; ++i){
        nums[i] = temp[i];
    }
}
4、快速排序

应用最广泛的排序算法,实现简单,适用于各种不同的输入数据且在一般应用中比其他算法快的多。

特点一:原地排序,只需要一个很小的辅助栈,特点二:将任意长度为N的数组排序所需时间和NlogN成正比,其他的算法无法达到这两个特点。

对比归并和快速排序: 归并和快速排序都使用了分治思想。归并排序是将两个子数组分别排序,并将有序的子数组归并以实现整个数组排序,递归调用发生在处理整个数组之前,一个数组被等分为两半。快速排序是当两个子数组都有序时,整个数组也就有序了,递归调用发生在处理整个数组之后,切分位置(partition)位置取决于数组内容。

C++实现代码:
// 这里的参数参考归并排序
void quick_sort(vector<int>& nums, int l, int r){
    // 越界
    if(l + 1 >= r){
        return;
    }
    // 首先将nums[first] 也就是第一个元素作为切分元素 key  将切分元素事先保存在key中
    int first = l, last = r - 1, key = nums[first];
    // 左右两个指针没有相遇时
    while(first < last){
        // last从右向左移动直到一个元素值小于key 
        while(first < last && nums[last] >= key){
            --last;
        }
        // 更新first指针
        nums[first] = nums[last];
        // first从左向右移动直到遇到一个元素值大于key
        while(first < last && nums[first] <= key){
            ++first;
        }
        // 更新last指针
        // 这里first和last指针交换可能会进行数次,交换的目的是给key找到一个恰当的位置使得它左边的元素全部小于它,右边的元素全部大于等于它,它们第一次交换后是将较小的一个元素移到前面,较大的移到后面,如此往复循环直到两个指针相遇退出循环
        nums[last] = nums[first];
    }
    // 此时找到了第一个切分元素的位置
    nums[first] = key;
    // 将key前面 和后面的元素分别排序  给每个元素找到位置  整个数组也就排好序了
    quick_sort(nums, l, first);
    quick_sort(nums, first+1, r);
}
5、冒泡排序
C++实现代码:
void bubble_sort(vector<int>& nums){
    int n = nums.size();
    // 设置交换标志位  可以当数组有序时直接跳出循环  减少时间
    bool swapped;
    for(int i = 1; i < n ;++i){
        // 每次寻找下一个最大值时都把swapped 初始值设置为false
        swapped = false;
        // 每一次循环都能找到最大的一个值放在最后面 
        for(int j = 1;j < n - i + 1;++j){
            //在这次循环中只要有元素交换 就把swapped设置为true
            if(nums[j] < nums[j - 1]){
                swap(nums[j],nums[j - 1]);
                swapped = true;
            }
        }
        // 在上一次循环中没有交换过元素说明已经是有序的了 不用继续循环了  直接break
        if(!swapped){
            break;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值