首页 > 其他分享 >数据结构 ——— 快速排序的时间复杂度以及规避最坏情况的方法

数据结构 ——— 快速排序的时间复杂度以及规避最坏情况的方法

时间:2024-12-01 17:34:10浏览次数:10  
标签:right midi int 复杂度 排序 key 数据结构 left

目录

前言

快速排序的时间复杂度

快速排序时间效率最坏的情况

规避快速排序最坏情况的方法

三数取中法

将三数取中放在快速排序算法中(以前后指针版本举例)


前言

在前几章学习了快速排序不同版本的实现,他们的时间效率都是差不多一样的
数据结构 ——— 快速排序算法的实现(前后指针法版本)-CSDN博客
数据结构 ——— 快速排序算法的实现(挖坑法版本)-CSDN博客
数据结构 ——— 快速排序算法的实现(hoare版本)-CSDN博客

接下来要学习的是快速排序的时间复杂度
以及如何规避快速排序中最坏情况(也就是会导致时间效率最低情况)的方法


快速排序的时间复杂度

通过递归分治的思想,每次把 n 都一分为二,分到最后都为 1 时,排序也就完成了
得出快速排序的时间复杂度为(大O渐进表示法):N * longN


快速排序时间效率最坏的情况

当数组有序或者接近有序时,快速排序的时间效率最低
低到时间复杂度为:O(N^2) 的程度

因为每次选择的 key 是最左边的值,那么就拿升序来说
因为升序,所以 key 每次都是最小的值,那么此区间的左边几乎不存在,也就是又要在 n-1 个值中选出 key ,同样是递归分治的思想,但是因为每次排序只能让 key 停留在最终位置
所以要排完整个数组,那么就是一个等差数列,时间复杂度也就是:O(N^2) 


规避快速排序最坏情况的方法

三数取中法

方法思维:

选取当前区间中左中右三个值,把它们比较出来,选中间值作为 key
但是不能改变快速排序的整体逻辑,所以选出中间值后,和最左边的值交换,这样既没有改变快速排序本身的逻辑,也规避了数组是有序或者接近有序的情况

方法代码:

int GetMidIndex(int* a, int left, int right)
{
	int midi = (left + right) / 2;

	// 找出中间值
	if (a[midi] < a[left])
	{
		if (a[midi] > a[right]) //[right midi left]
		{
			return midi;
		}
		else if (a[right] > a[left]) //[midi left right]
		{
			return left;
		}
		else //[midi right left]
		{
			return right;
		}
	}
	else
	{
		if (a[midi] < a[right]) //[left midi right]
		{
			return midi;
		}
		else if (a[right] < a[left]) //[right left midi]
		{
			return left;
		}
		else //[left right midi]
		{
			return right;
		}
	}
}

注意:是要返回中间值的下标,才能方便和最左边的值交换


将三数取中放在快速排序算法中(以前后指针版本举例)

代码演示:

int PartSort_Pointer(int* a, int left, int right)
{
	// 三数取中
	int midi = GetMidIndex(a, left, right);
	Swap(&a[midi], &a[left]);

	int prev = left;
	int cur = prev + 1;
	int keyi = left;

	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}

		cur++;
	}

	// 把 key 放在最终位置
	Swap(&a[prev], &a[keyi]);

	return prev;
}

void QuickSort(int* a, int begin, int end)
{
	// 前后指针法
	if (begin >= end)	
	{
		return;
	}

	int key = PartSort_Pointer(a, begin, end);

	// [begin,key-1] key [key+1,end]

	QuickSort(a, begin, key - 1);
	QuickSort(a, key + 1, end);
}

得到中间值的下标后,直接和最左边的值进行交换,然后其他的逻辑都不变

代码验证:

标签:right,midi,int,复杂度,排序,key,数据结构,left
From: https://blog.csdn.net/weixin_55341642/article/details/144043005

相关文章

  • 数据结构第一弹-二叉树
    大家好,今天和大家一起分享一下数据结构中的二叉树~二叉树是一种非常基础且重要的非线性数据结构,广泛应用于各种算法和系统设计中。今天详细介绍二叉树的基本概念、性质以及操作方法,并特别展开讨论一种特殊的二叉树——二叉搜索树(BinarySearchTree,BST)一、二叉树概述二......
  • 数据结构第一弹-队列
    大家好,今天和大家一起分享一下数据结构中的队列相关内容~队列是一种非常重要的线性数据结构,遵循先进先出(FIFO,FirstInFirstOut)的原则。一、队列概述队列是一种特殊的线性表,它只允许在一端进行插入操作,在另一端进行删除操作。队列的入口称为队尾(rear),出口称为队头(front)。......
  • MySQL底层概述—8.JOIN排序索引优化
    大纲1.Join算法原理2.IN和EXISTS函数3.MySQL排序之索引排序(Usingindex)4.MySQL排序之额外排序(Usingfilesort)5.排序优化之尽量使用索引排序6.索引单表优化7.索引多表优化 1.Join算法原理(1)Join简介(2)驱动表的定义(3)三种Join算法(4)总结 (1)Join简介Join......
  • 【数据结构与算法】链表之美-复杂链表的复制与链表的插入排序
    主页:HABUO......
  • 一点点排序
    排序归并排序归并排序介绍与代码大体思路:归并排序总体思路是,先把一串待排序数列分为前后两组,把这两组分别排为顺序数组,再将两组顺序数组合为一整个大的顺序数组。objection1:分组后分别排好序?用选择排序吗?递归的思路是什么?并非选择排序,而是递归的方式。可以看到,第一次“将一......
  • 数据结构之线性表实验(二)
    【实验目的】1.理解和掌握线性关系数据的链接结构组织;2.设计、分析算法。3.理解封装的思想和意义。【实验内容】一、约瑟夫问题的求解。1.分别用带附加头结点和不带附加头结点的单链表实现,写出实现的代码,可以使用单链表的基本操作函数。2.在1基础上,计算报数间隔多......
  • 数据结构排序
    语雀链接:https://www.yuque.com/g/wushi-ls7km/ga9rkw/gsehtu150dmco6bf/collaborator/join?token=sUtrjJPYLJ0W7OGx&source=doc_collaborator#《数据结构排序》......
  • 归并排序的学习
    归并排序思想:归并的本质也是分治,不过不同于快速排序,它在将大问题分成小问题之后最后需要将小问题合并成最终的排序结果。#include<iostream>usingnamespacestd;constintN=1e6+10;intn;intq[N],tmp[N];voidmerge_sort(intq[],intl,intr){if(l>=r......
  • 堆排序(下标0/1)
    0  void SiftHeap(int r[], int k, int n) {    int i = k;     int j = 2 * i + 1;  // 置i为要筛的结点,j为i的左孩子    while (j < n) {  // 筛选还没有进行到叶子    if (j + 1 < n && r[j] < r[j + 1]) j++......
  • 【机器学习】L1与L2正则化的深度解读:如何平衡模型复杂度与性能
    L1与L2正则化的深度解读:如何平衡模型复杂度与性能1.引言:过拟合问题1.1什么是过拟合?1.2为什么会出现过拟合?主要原因:1.3正则化的基本思想2.L1正则化深度解析2.1数学表达式2.2L1正则化的特性1.稀疏解的产生2.优化特性3.权重更新规则3.L2正则化深度解析3.1数......