冒泡排序和快速排序优化方法

news/2024/5/19 22:35:39 标签: 算法, 数据结构, 排序算法, 快速排序, c++

冒泡排序

参考【排序】:冒泡排序以及三种优化
在这里插入图片描述

//假设排序arr[] = { 1, 3, 4, 2, 6, 7, 8, 0 };
void BubbleSort(int arr[],int len)
{
	int i = 0;
	int tmp = 0;
	for (i = 0; i < len - 1; i++)//确定排序趟数
	{
		int j = 0;
		for (j = 0; j < len - 1 - i; j++)//确定比较次数,每次从第一个开始比
		{
			if (arr[j]>arr[j + 1])
			{
				//交换
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

优化1——设置flag

假设我们现在排序arr[]={1,2,3,4,5,6,7,8,10,9}这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,什么也没做。所以我们可以在交换的地方加一个标记,如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去。

void BubbleSort(int arr[], int len)
{
	int i = 0;
	int tmp = 0;
	for (i = 0; i < len - 1; i++)//确定排序趟数
	{
		int j = 0;
		int flag = 0;
		for (j = 0; j < len - 1 - i; j++)//确定比较次数
		{
			if (arr[j]>arr[j + 1])
			{
				//交换
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 1;//加入标记
			}
		}
		if (flag == 0)//如果没有交换过元素,则已经有序
		{
			return;
		}
	}
}


优化二——记录最后交换的位置

优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3 ,4 ,7,6,5)。但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化。既我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。

void BubbleSort(int arr[], int len)
{
	int i;
	int tmp;
	int flag ;
	int pos;//用来记录最后一次交换的位置
	int k = len - 1;
	for (i = 0; i < len - 1; i++)//确定排序趟数
	{
		pos = 0;
		int j = 0;
		flag = 0;
		for (; j < k; j++)//确定比较次数,从第一个开始比
		{
			if (arr[j]>arr[j + 1])
			{
				//交换
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 1;//加入标记
				pos = j;//交换元素,记录最后一次交换的位置
			}
		}
		if (flag == 0)//如果没有交换过元素,则已经有序
		{
			return;
		}
		k = pos;//下一次比较到记录位置即可
	}
}

优化三——鸡尾酒排序

时间复杂度

最好情况 O ( n ) O(n) O(n),最坏和平均情况 O ( n 2 ) O(n^2) O(n2)

空间复杂度

O ( 1 ) O(1) O(1)

稳定性

稳定

挖坑填数进行快速排序——固定基数

如果固定最左边的数作为基数,则要从右边开始循环。
如果固定最右边的数作为基数,则要从左边开始循环。
在这里插入图片描述

#include <iostream>
using namespace std;

int findidx(int* a, int low, int high)
{
	int temp = a[low];//拿左边的值当哨兵
	while (low < high) {
		while (low < high && a[high] >= temp) { high--; }//找到比哨兵小的放到low位置
		a[low] = a[high];
		while (low < high && a[low] <= temp) { low++; }//找到比哨兵大的放到high位置
		a[high] = a[low];		
	}
	a[high] = temp;//此时high==low,往这个位置放入哨兵值
	return high;
}

void qsort(int* array, int low, int high)
{
	if (low >= high)return;//当low>=high时,说明某个子序列就一个或没有元素了,该子序列就没必要排了
	int idx = findidx(array, low, high);
	qsort(array, low, idx - 1);
	qsort(array, idx + 1, high);
}

int main()
{
	int array[] = { 3,8,5,6,0,3,2,8,9,1,10 };
	int len = sizeof(array) / sizeof(array[0]);
	qsort(array, 0, len - 1);
	for (int i = 0; i < len; i++) {
		cout << array[i] << " ";
	}
	cout << endl;
	//0 1 2 3 3 5 6 8 8 9 10
}

时间复杂度

最差情况

一个序列完全有序(不管从小到大或是从大到小),每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)。时间复杂度为 O ( n 2 ) O(n^2) O(n2).

最优情况

快速排序最优的情况就是每一次取到的元素都刚好平分整个数组。递归算法公式为 T [ n ] = a T [ n / b ] + f ( n ) T[n]=aT[n/b]+f(n) T[n]=aT[n/b]+f(n),即把一个规模为n的问题分为a个规模为n/b的子问题。
这里复杂度为 T [ n ] = 2 T [ n / 2 ] + f ( n ) T[n]=2T[n/2]+f(n) T[n]=2T[n/2]+f(n), f ( n ) f(n) f(n)为平分这个数组所花的时间 O ( n ) O(n) O(n)(即函数findidx)
T [ n ] = 2 T [ n / 2 ] + n T[n]=2{T[n/2]}+n T[n]=2T[n/2]+n——第一次递归
T [ n ] = 2 ( 2 T [ n / 4 ] + n / 2 ) + n = 2 2 T ( n / 2 2 ) + 2 n T[n]=2(2T[n/4]+n/2)+n=2^2T(n/2^2)+2n T[n]=2(2T[n/4]+n/2)+n=22T(n/22)+2n——第二次递归
T [ n ] = 2 2 T ( n / 2 2 ) + 2 n = 2 2 ( 2 T ( n / 2 3 ) + n / 2 2 ) + 2 n = 2 3 T ( n / 2 3 ) + 3 n T[n]=2^2T(n/2^2)+2n=2^2(2T(n/2^3)+n/2^2)+2n=2^3T(n/2^3)+3n T[n]=22T(n/22)+2n=22(2T(n/23)+n/22)+2n=23T(n/23)+3n——第三次递归
。。。
T [ n ] = = 2 M T ( n / 2 M ) + M n T[n]==2^MT(n/2^M)+Mn T[n]==2MT(n/2M)+Mn——第M次递归
此时平分的不能再平分, n / 2 M = 1 n/2^M=1 n/2M=1, M = l o g 2 n M=log_2n M=log2n,即进行了 l o g 2 n log_2n log2n次递归。
T [ n ] = = 2 M T ( n / 2 M ) + M n = n T ( 1 ) + n l o g 2 n = n + n l o g 2 n = O ( n l o g n ) T[n]==2^MT(n/2^M)+Mn=nT(1)+nlog_2n=n+nlog_2n=O(nlogn) T[n]==2MT(n/2M)+Mn=nT(1)+nlog2n=n+nlog2n=O(nlogn)

平均情况

O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度

首先就地快速排序使用的空间是O(1)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据;

最优情况

进行 l o g 2 n log_2n log2n次递归,故为 O ( l o g n ) O(logn) O(logn)

最差情况

退化为冒泡排序的情况, O ( n ) O(n) O(n)

平均情况

O ( l o g n ) O(logn) O(logn)

稳定性

在这里插入图片描述

优化方法

1.三数取中法

解决基本有序的序列(就是找到数组中最小下标,最大下标,中间下标的数字,进行比较,把中间大的数组放在最左边)

#include <iostream>
using namespace std;

void swap(int *array, int low, int high)
{
    int temp = array[low];
    array[low] = array[high];
    array[high] =temp;
}
int findidx(int *a, int low, int high)
{
    //最左边,最右边以及中间这3个数的中位数放在最左边
    int mid = (low + high) / 2;
    if(a[low]>a[high])//最左大于最右的时候,交换左右
	{
		swap(a,low,high);
	}
	if(a[mid]>a[high])//如果中间的>high ,交换
	{
		swap(a,mid,high);
	}
	if(a[mid]>a[low])//如果中间的>low,交换
	{
		swap(a,mid,high);
	}

    int temp = a[low]; //拿左边的值当哨兵
    while (low < high)
    {
        while (low < high && a[high] >= temp)
        {
            high--;
        } //找到比哨兵小的放到low位置
        a[low] = a[high];
        while (low < high && a[low] <= temp)
        {
            low++;
        } //找到比哨兵大的放到high位置
        a[high] = a[low];
    }
    a[high] = temp; //此时high==low,往这个位置放入哨兵值
    return high;
}

void qsort(int *array, int low, int high)
{
    if (low >= high)
        return; //当low>=high时,说明某个子序列就一个或没有元素了,该子序列就没必要排了
    int idx = findidx(array, low, high);
    qsort(array, low, idx - 1);
    qsort(array, idx + 1, high);
}

int main()
{
    int array[] = {3, 8, 5, 6, 0, 3, 2, 8, 9, 1, 10};
    int len = sizeof(array) / sizeof(array[0]);
    qsort(array, 0, len - 1);
    for (int i = 0; i < len; i++)
    {
        cout << array[i] << " ";
    }
    cout<<"s"<<endl;
    cout << endl;
    //0 1 2 3 3 5 6 8 8 9 10
}

2.随机选取基准

#include <iostream>
#include <ctime>
using namespace std;

void swap(int *array, int low, int high)
{
    int temp = array[low];
    array[low] = array[high];
    array[high] = temp;
}
int findidx(int *a, int low, int high)
{
    srand((unsigned)time(nullptr)); //设置随系统时间变化的种子
    int pivotPos = rand() % (high - low + 1) + low ;//哨兵为from low to high中的一个 
    swap(a, low, pivotPos);//把哨兵放到最左边
    int temp = a[low]; //拿左边的值当哨兵
    while (low < high)
    {
        while (low < high && a[high] >= temp)
        {
            high--;
        } //找到比哨兵小的放到low位置
        a[low] = a[high];
        while (low < high && a[low] <= temp)
        {
            low++;
        } //找到比哨兵大的放到high位置
        a[high] = a[low];
    }
    a[high] = temp; //此时high==low,往这个位置放入哨兵值
    return high;
}

void qsort(int *array, int low, int high)
{
    if (low >= high)
        return; //当low>=high时,说明某个子序列就一个或没有元素了,该子序列就没必要排了
    int idx = findidx(array, low, high);
    qsort(array, low, idx - 1);
    qsort(array, idx + 1, high);
}

int main()
{
    int array[] = {3, 8, 5, 6, 0, 3, 2, 8, 9, 1, 10};
    int len = sizeof(array) / sizeof(array[0]);
    qsort(array, 0, len - 1);
    for (int i = 0; i < len; i++)
    {
        cout << array[i] << " ";
    }
    cout << endl;
    //0 1 2 3 3 5 6 8 8 9 10
}


http://www.niftyadmin.cn/n/1530961.html

相关文章

leetcode回溯专题

22.括号生成 class Solution { public:vector<string> res;vector<string> generateParenthesis(int n){if (n 0)return {};string track;//可用的左括号和右括号数量backtrack(n, n, track, res);return res;}void backtrack(int left, int right, string &t…

leetcode字符串专题

151.翻转字符串里的单词 class Solution { public:string reverseWords(string s){reverse(s.begin(), s.end());int size (int)s.size();int start 0; //每个单词开始int end 0; //每个单词末尾int index 0; //要返回的字符串下标for (; start < size; start){if (s[…

两种方法实现拓扑排序

本文转载自&#xff1a;https://blog.csdn.net/qq_35644234/article/details/60578189 1.拓扑排序的介绍 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序&#xff0c;是将G中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点u和v&#xff0c;若边(…

leetcode.第184场周赛(2020.4.12)

1.数组中字符串匹配 思路 主要是strstr函数&#xff0c;在头文件string.h里 复杂度 时间&#xff1a;O(Kn2)O(Kn^2)O(Kn2),K为字符串长度 空间&#xff1a;O(1)O(1)O(1) class Solution { public:vector<string> stringMatching(vector<string>& words) {…

leetcode数论专题

367.完全平方数 1 1; 4 1 3; 9 1 3 5; 16 1 3 5 7; N*N 1 3 5 … (2N - 1) 数学规律 时间&#xff1a;O(sqrt(num))O(sqrt(num))O(sqrt(num)) 空间&#xff1a;O(1)O(1)O(1) class Solution { public:bool isPerfectSquare(int num) {int num1 1;while(num…

leetcode字典树专题

208.实现Trie(前缀树) 思路 成员变量flag定义为单词结束的标志&#xff0c;实现search方法时要用到这个flag。字典树的每个节点看作是Tire类的一个对象&#xff0c;next数组(指针数组&#xff0c;每个指针指向一个Tire对象)初始化为nullptr。注意根节点的移动。 class Trie {…

leetcode图论专题

207.课程表 方法一&#xff1a;Kahn算法&#xff08;基于BFS&#xff09; 思路 用map记录每门课的后续课程&#xff0c;创建入度表&#xff0c;入度为0要入队。每个元素依次出队&#xff0c;最后看出队数是否等于总课程数&#xff0c;就可以判断图是否有环&#xff0c;有环证…

leetcode最短路径专题

542.01矩阵 思路 复杂度 时间复杂度&#xff1a;O(rc)O(rc)O(rc)&#xff0c;其中 r 为矩阵行数&#xff0c;c 为矩阵列数&#xff0c;即矩阵元素个数。广度优先搜索中每个位置最多只会被加入队列一次&#xff0c;因此只需要 O(rc)O(rc)O(rc) 的时间复杂度。 空间复杂度&…