排序算法
2020-05-08 本文已影响0人
浪淘沙008
插入排序.gif
选择排序.gif
归并排序.gif
快排.gif
双下标快排.png
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
// 冒泡排序
vector<int> bubbleSort(vector<int>& nums) {
for (int i = 0; i < nums.size(); ++i) {
for (int j = 0; j < nums.size() - i -1; ++j) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
return nums;
}
/**
插入排序
以i为分界线,i左边的为已排区域,i以及i右边的为未排区域,每次拿i的值与前面的值相比较,以j定位与nums[i]对比的值,当未找到合适时则将nums[j]右移一位,直到查到比nums[i]小的值或者当j为-1时插入nums[i],
*/
vector<int> insertionSort(vector<int>& nums) {
if (nums.size() <= 1) return nums;
for (int i = 0; i < nums.size(); i++) {
int value = nums[i];
int j = i - 1;
for (; j>= 0; j--) { //逆查找适合位置
if (nums[j] > value) {
nums[j + 1] = nums[j]; // 当未找到合适时则将nums[j]右移一位
}else {
break; //找到合适位置时暂停循环
}
}
nums[j + 1] = value; //找到合适位置进行插入操作
}
return nums;
}
/**
选择排序
先遍历出未排序数据找出最小值与已排序末尾的数据进行替换,i左侧为已排序数据,右侧为未排序数据,通过遍历未排序段进行最小数据查找并与nums[i]进行替换从而得到有序的数列
*/
vector<int> selectSort(vector<int>& nums) {
if (nums.size() <= 1) return nums;
for (int i = 0; i < nums.size(); i++) {
int val = nums[i];
int index = i;
for (int j = i; j < nums.size(); j++) {
if (nums[j] < val) {
val = nums[j];
index = j;
}
}
nums[index] = nums[i];
nums[i] = val;
/**
//通过位移插入数据
for (int a = index; a > i; a--) {
nums[a] = nums[a - 1];
}
nums[i] = val;
*/
}
return nums;
}
/**
归并排序
生成一个与原数组相同大小的数组便于存储正在进行排序的数据, 递归时将数组进行分割排序并对排序好的数据进行合并调用;合并方法中通过i、j左右指针指向左右数组中对应的排序数据下标,当当前数组排序结束后再将值copy到原数组中,其时间复杂度为O(nlogn),空间复杂度为O(n).
参考:https://www.jianshu.com/p/33cffa1ce613
*/
void sort(vector<int> &nums) {
vector<int> temp = nums; //在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
sort(nums, 0, (int)(nums.size() - 1), temp);
}
void sort(vector<int> &nums, int left, int right, vector<int>temp) {
if (left < right) {
long mid = (left + right) / 2;
sort(nums, left, int(mid), temp); //左边归并排序,使得左子序列有序
sort(nums, int(mid + 1), right, temp);//右边归并排序,使得右子序列有序
// 递归调用上面的代码调用结束直至left>right才会执行递归中的如下代码
// left为左侧下标值, right为右侧下标值
merge(nums, left, int(mid), right, temp);//将两个有序子数组合并操作
}
}
void merge(vector<int> &nums, int left, int mid, int right, vector<int> temp) {
int i = left;//左序列指针
int j = mid + 1;//右序列指针
int t = 0;//临时数组指针
while (i <= mid && j <= right) {// 对左右数组的下标进行大小限制
if (nums[i] <= nums[j]) { // 对左右数组对应下标下的值进行比较,并取出教小的值放到temp中
temp[t++] = nums[i++];
}else {
temp[t++] = nums[j++];
}
}
while (i <= mid) { //将左边剩余元素填充进temp中
temp[t++] = nums[i++];
}
while (j <= right) {//将右序列剩余元素填充进temp中
temp[t++] = nums[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while (left <= right) {
nums[left++] = temp[t++];
}
}
/**
快速排序双下标实现
参考:https://blog.csdn.net/starzyh/article/details/90272347
*/
void quickSort(vector<int> &nums, int left, int right)
{
if(left < right)
{
// 存储当前第一个值并取出该值以进行后期的对比
int pivot = nums[left];
// 设置双标,low记录当前将要被插入的下标(也是当前获取的privot的下标)
int low = left, high = right;
while(low < high) // 分为两个下标向中间移动,当两下标相遇时将pivot的值插入
{
while(nums[high] >= pivot && low < high){ //如果高位值大于pivot则获取前一个值再进行比较
high--;
}
// 获取到的nums[high]比pivot则将该值赋值给nums[low],
// 拟定当前的high为 high1,以便后面理解,在执行该循环下面的代码时high将不会再发生改变
nums[low] = nums[high];
while(nums[low] <= pivot && low < high){ //如果获取的低位值小于pivot则进行++进入下个循环
low++;
}
// 获取到的nums[low]比pivot大,此时将nums[low]的数值赋值给nums[high],即nums[high1],此时的high1==high
nums[high] = nums[low];
}
// 遍历结束将当前pivot插入到nums[low]的位置,此时nums[low]左边的都小于pivot,右边的都大于pivot
nums[low] = pivot;
for (int i = 0; i < nums.size(); i++) {
cout << nums[i] << "->";
}
cout << " " << endl;
//递归对两边的数据继续进行排序
quickSort(nums, left, low - 1);
quickSort(nums, low + 1, right);
}
}
};
int main(int argc, const char * argv[]) {
/**
对于排序算法执行效率的分析,我们一般会从这几个方面来衡量:
1.最好情况、最坏情况、平均情况时间复杂度
我们在分析排序算法的时间复杂度时,要分别给出最好情况、最坏情况、平均情况下的时间复杂度。除此之外,你还要说出最好、最坏时间复杂度对应的要排序
的原始数据是什么样的。
为什么要区分这三种时间复杂度呢?第一,有些排序算法会区分,为了好对比,所以我们最好都做一下区分。第二,对于要排序的数据,有的接近有序,有的完
全无序。有序度不同的数据,对于排序的执行时间肯定是有影响的,我们要知道排序算法在不同数据下的性能表现。
2.时间复杂度的系数、常数 、低阶
我们知道,时间复杂度反应的是数据规模n很大的时候的一个增长趋势,所以它表示的时候会忽略系数、常数、低阶。但是实际的软件开发中,我们排序的可能
是10个、100个、1000个这样规模很小的数据,所以,在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来。
3.比较次数和交换(或移动)次数
这一节和下一节讲的都是基于比较的排序算法。基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。所以,如
果我们在分析排序算法的执行效率的时候,应该把比较次数和交换(或移动)次数也考虑进去。
*/
vector<int> list = {4, 3, 6, 2, 7 , 8, 1, 5};
Solution solution;
solution.quickSort(list, 0, int(list.size() - 1));
for (int i = 0; i < list.size(); i++) {
printf("%d-", list[i]);
}
return 0;
}
参考:https://www.jianshu.com/p/33cffa1ce613
https://blog.csdn.net/starzyh/article/details/90272347
以及王铮老师的《数据结构与算法之美》