首页 > 其他分享 >交换排序(冒泡/快排)

交换排序(冒泡/快排)

时间:2024-11-03 21:45:20浏览次数:3  
标签:arr right int st 快排 冒泡 排序 left

一 . 交换排序

交换排序基本思想 : 

所谓交换 , 就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置 。

交换序列的特点是 : 将键值较大的记录向序列的尾部移动 , 键值较小的记录向序列的前部移动

 

 1.1 冒泡排序

在前面中 , 介绍了冒泡排序的思路了 冒泡排序是一种最基础的交换排序 。 之所以叫做冒泡排序 , 因为每个元素都可以像小气泡一样 , 根据自身大小一点一点移动到对应的位置 。其本质就是两两数值进行比较 。 

//冒泡排序
void BubbleSort(int* arr, int n)
{
	//比较的趟数
	for (int i = 0; i < n-1; i++)
	{
		int exchange = 0;
		//一趟中比较的次数
		for (int j = 0; j < n - i - 1; j++) {
			if (arr[j] > arr[j + 1])
			{
				Swap(&arr[j], &arr[j + 1]);
				exchange = 1;
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}

时间复杂度 : O( n ^ 2 )

空间复杂度 : O ( 1 ) 

1.2 快速排序

快速排序是Hoare 于1962 年提出的一种  二叉树  的交换排序方法 ,  其基本思想为 : 任取待排序元素中的某元素作为基准值 , 按照该排序码 将待排序集合分割成  两个子序列 , 左子序列中所有元素均小于基准值 , 右子序列中 所有元素均大于基准值 , 然后左右子序列重复该过程 , 直到所有元素都排列到相应的位置上 。

 快速排序实现的主框架 : 

//快速排序
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	//找基准值
	int keyi = _QuickSort(arr, left, right);

	//二分查找
	// [0 , keyi -1 ]      keyi     [ keyi + 1 , right]
	QuickSort(arr, 0, keyi - 1);
	QuickSort(arr, keyi + 1 ,right);
}

将区域中的元素进行划分的   _QuickSort方法  主要有以下几种实现方式 :  

 

1.2.1 Hoare 版本

基本思路 :

1 ) 创建左右指针 , 以确定基准值

2 ) 左指针从左往右找比 基准值要大的数据, 右指针从右往左找比基准值要小的数据 , 然后左右指针数据交换 , 继续循环 , 直到 左指针的下标值比右指针的 大

3 )当 左指针的下标值比右指针的大时 , 基准值 与 右指针的数值交换

例如 对数组 a [ ] = { 6 , 1 , 2 , 7 , 9 , 3 } 进行快速排序  : 

 

  •  问题 一 : 为什么跳出循环后right 位置的值一定不大于 key ?

因为在 left > right 时 , right 走到了 left 的左侧 , 而再次之前 left 已经把最大的值找到并且与 right 交换 ( left 扫描过的数据均不大于key ) ;

相遇点的值有三种情况 : 

 

  •  问题 二 : 为什么 left 和 right 指定的数据和 key 值相等时候也交换 ? (也就是以下代码加个 "  =  " 为啥不行 )

相等的值参与交换确实会有一定额外的消耗 。 实际还有各种复杂的场景 ,假设数组中的数据大量重复的时候 无法进行有效的分割排序 !时间复杂度会变高 !

 时间复杂度分析 :

递归算法的时间复杂度的计算 : 递归次数 * 单次递归的时间复杂度

                                                      \log_{2}n    *       n

快速排序的时间复杂度为 : O(n*logn )

如果数组为有序 : 快排的时间复杂度为 O(n^2)

1.2.2 挖坑法

思路 : 

创建左右指针 。 首先从右向左找出比基准小的数据 , 找到后立即放入到左边坑中 , 当前位置变为新  “坑” , 然后从左向右找出比基准大的数据 , 找到后立即放入到右边坑中 , 当前位置变为新的 “坑” , 结束循环后将最开始存储的分界值放入到当前的 "坑“ 中 , 返回当前 ” 坑“ 下标(即分界值的下标)

//挖坑法
int _QuickSort1(int* arr, int left, int right)
{
	int key = arr[left];
	int hole = left;
	while (left < right)
	{
		while (left < right && arr[right] > key)
		{
			right--;
		}
		arr[hole] = arr[right];
		hole = right;
		while (left < right && arr[left] < key)
		{
			left++;
		}
		arr[hole] = arr[left];
		hole = left;
	}
	arr[hole] = key;
	return hole;
}

 

1.2.3 lomuto指针

创建前后指针 , 从左往右找比  基准值小  的进行交换 , 使得小的都排在基准值的左边 。 

//双指针法
int _QuickSort2(int* arr, int left, int right)
{
	int prev = left, cur = prev + 1;
	int key = left;
	while (cur <= right)
	{
		if (arr[cur] < arr[key] && ++prev !=cur)
		{
			Swap(&arr[prev], &arr[cur]);
		}
		cur++;
	}
	Swap(&arr[prev], &arr[key]);
	return prev;
}

快速排序特性总结 :

1 . 时间复杂度 : O(n * logn)

2 . 空间复杂度 :O(logn) 

 1.2.4 非递归版本

非递归版本的快速排序需要借助数据结构 : 栈

void QuickSortNonR(int* arr, int left, int right)
{
	ST st;
	STInit(&st);
	StackPush(&st, right);
	StackPush(&st, left);
	while (!StackEmpty(&st))
	{
		//取栈顶元素——两次
		int begin = STTop(&st);
		StackPop(&st);

		int end = STTop(&st);
		StackPop(&st);

		//对区间[begin,end]找基准值---双指针法
		int prev = begin, cur = prev + 1;
		int keyi = begin;
		while (cur <= end)
		{
			if (arr[cur] < arr[keyi] && ++prev != cur)
			{
				Swap(&arr[prev], &arr[cur]);
			}
			++cur;
		}
		Swap(&arr[keyi], &arr[prev]);
		keyi = prev;
		//此时基准值的下标:keyi
		//划分左右序列:[begin,keyi-1] [keyi+1,end]
		if (keyi + 1 < end)
		{
			StackPush(&st, end);
			StackPush(&st, keyi + 1);
		}

		if (keyi - 1 > begin)
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, begin);
		}
	}

	STDestory(&st);
}

标签:arr,right,int,st,快排,冒泡,排序,left
From: https://blog.csdn.net/khjjjgd/article/details/143462108

相关文章

  • 排序 (插入/选择排序)
    目录一.排序概念及运用1.1排序的概念1.2排序的应用1.3常见的排序算法二.插入排序2.1直接插入排序2.1复杂度分析 2.3希尔排序 2.4希尔排序时间复杂度分析三.选择排序3.1直接选择排序3.2堆排序一.排序概念及运用1.1排序的概念排序:所谓......
  • 【算法-选择排序】挑挑拣拣,排出顺序——选择排序入门
    什么是选择排序?选择排序是一种比较简单直接的排序方式。想象你在打散一副牌,想按照大小顺序从小到大排列这些牌。你会怎么做?可能会先找出最小的那张,放在最前面,然后在剩下的牌里找第二小的,依次类推,这就是选择排序的基本思路!在程序中,选择排序的操作流程也类似:它逐步将未排序......
  • 算法-图论-拓扑排序
    1.拓扑排序(卡码网117)fromcollectionsimportdeque,defaultdictdefmain():num_node,num_edge=map(int,input().split())inDegrees=[0for_inrange(num_node)]edges=defaultdict(list)for_inrange(num_edge):source,target=......
  • 【LeetCode:153. 寻找旋转排序数组中的最小值 + 二分】
    在这里插入代码片......
  • C++优选算法 分治-快排
    一、基本思想快速排序采用分治法策略,将一个大数组(或子数组)分为两个小数组,然后递归地对这两个小数组进行排序。其基本思想可以概括为“分解、解决、合并”三个步骤:分解:将原问题(即待排序的数组)分解为若干个规模较小、相互独立且与原问题形式相同的子问题(即子数组)。解决:若子问题......
  • 34. 在排序数组中查找元素的第一个位置和最后一个位置
    题目参考了y总讲的这题789.数的范围自己是这样写的;classSolution{public:vector<int>searchRange(vector<int>&nums,inttarget){vector<int>result(2,-1);intl=0,r=nums.size()-1;while(l<r){......
  • 手撕快排的三种方法:让面试官对你刮目相看
    快来参与讨论......
  • 【排序算法】堆排序
    ......