首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【数据结构】常见的排序算法 -- 交换排序

【数据结构】常见的排序算法 -- 交换排序

作者头像
小年糕是糕手
发布2026-01-14 17:23:23
发布2026-01-14 17:23:23
860
举报
文章被收录于专栏:C++学习C++学习

交换排序基本思想: 所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置 交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动

一、冒泡排序
1.1、算法思想

前面在C语言博客专栏中我们已经接触过冒泡排序的思路了,冒泡排序是一种最基础的交换排序。之所以叫做冒泡排序,因为每一个元素都可以像小气泡一样,根据自身大小一点一点向数组的一侧移动。

  1. 从序列的第一个元素开始,依次比较相邻的两个元素。
  2. 若前一个元素大于后一个元素,则交换它们的位置。
  3. 继续向后比较,直到完成对序列中最后一个未排序元素的比较,此时本轮最大的元素会 “冒泡” 到序列末尾(即已排序部分的起始位置)。
  4. 忽略已排好序的末尾元素,对剩余未排序的元素重复上述过程。
  5. 当某一轮遍历中没有发生任何元素交换时,说明序列已完全有序,排序结束
1.2、代码实现
代码语言:javascript
复制
//1)冒泡排序
void BubbleSort(int* arr, int n)
{
	int exchange = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				exchange = 1;
				Swap(&arr[j], &arr[j + 1]);
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}

冒泡排序的特性总结

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)
二、快速排序

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

2.1、快速排序实现主框架
代码语言:javascript
复制
//2)快速排序
void QuickSort(int* arr, int left, int right)
{

	if (left >= right)
	{
		return;
	}
	//找基准值keyi
	int keyi = _QuickSort(arr, left, right);
	//左序列[left,keyi - 1]  右序列[keyi + 1,right]
	QuickSort(arr, left, keyi - 1);
	QuickSort(arr, keyi + 1, right);
}

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

2.2、找基准值的三种方法
2.2.1、hoare版本
2.2.1.1、算法思路

算法思路: 1)创建左右指针,确定基准值 2)从右向左找出比基准值小的数据,从左向右找出比基准值大的数据,左右指针数据交换,进入下次循环

2.2.1.2、代码实现
代码语言:javascript
复制
//找基准值 -- hoare版本
int _QuickSort1(int* arr, int left, int right)
{
	int keyi = left;
	left++;
	while (left <= right)
	{
		//right:从右往左找比基准值要小的
		while (left <= right && arr[right] > arr[keyi])
		{
			--right;
		}
		//left:从左往右找比基准值要大的
		while (left <= right && arr[left] < arr[keyi])
		{
			++left;
		}
		//left和right交换
		if (left <= right)
		{
			Swap(&arr[left], &arr[right]);
			left++;
			right--;
		}
	}
	//right的位置就是基准值的位置
	Swap(&arr[keyi], &arr[right]);//交换值
	return right;
}
2.2.2、挖坑法
2.2.2.1、算法思路

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

2.2.2.2、代码实现
代码语言:javascript
复制
int _QuickSort2(int* arr, int left, int right)
{
	int hole = left;
	int key = arr[hole];
	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;
}
2.2.3、lomuto前后指针
2.2.3.1、算法思路

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

cur从左往右找比基准值要小的数据 1)cur指向的数据比基准值要小,++prev,cur和prev交换值,++cur 2)cur指向的数据不比基准值要小,++cur

2.2.3.2、代码实现
代码语言:javascript
复制
//找基准值 -- lomuto前后指针
int _QuickSort3(int* arr, int left, int right)
{
	int prev = left, cur = left + 1;
	int keyi = left;
	while (cur <= right)
	{
		//cur数据和基准值比较
		if (arr[cur] < arr[keyi] && ++prev != cur)
		{
			Swap(&arr[cur], &arr[prev]);
		}
		++cur;
	}
	Swap(&arr[keyi], &arr[prev]);

	return prev;

}

快速排序特性总结:

  1. 时间复杂度:O(n*logn)
  2. 空间复杂度:O(logn)
2.3、非递归版本

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

代码语言:javascript
复制
//非递归版本快速排序
// 交换两个元素
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 分区操作:返回基准值最终位置
int partition(int arr[], int left, int right) {
    // 选择最右侧元素作为基准值
    int pivot = arr[right];
    // i 指向小于基准值区域的最后一个元素
    int i = left - 1;
    
    for (int j = left; j < right; j++) {
        // 若当前元素小于等于基准值,加入左侧区域
        if (arr[j] <= pivot) {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    // 将基准值放到正确位置(i+1 是基准值的最终索引)
    swap(&arr[i + 1], &arr[right]);
    return i + 1;
}

// 非递归快速排序
void quickSort(int arr[], int n) {
    if (n <= 1) return;
    
    // 用数组模拟栈,存储区间的左右边界(每个区间占两个位置)
    int* stack = (int*)malloc(2 * n * sizeof(int));
    int top = -1;  // 栈顶指针
    
    // 初始区间 [0, n-1] 入栈
    stack[++top] = 0;
    stack[++top] = n - 1;
    
    // 栈不为空时循环处理
    while (top >= 0) {
        // 弹出右边界和左边界(注意栈的先入后出特性)
        int right = stack[top--];
        int left = stack[top--];
        
        // 分区操作,获取基准值位置
        int pivotIndex = partition(arr, left, right);
        
        // 左子区间 [left, pivotIndex-1] 入栈(若有效)
        if (pivotIndex - 1 > left) {
            stack[++top] = left;
            stack[++top] = pivotIndex - 1;
        }
        
        // 右子区间 [pivotIndex+1, right] 入栈(若有效)
        if (pivotIndex + 1 < right) {
            stack[++top] = pivotIndex + 1;
            stack[++top] = right;
        }
    }
    
    free(stack);  // 释放栈内存
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、冒泡排序
    • 1.1、算法思想
    • 1.2、代码实现
  • 二、快速排序
    • 2.1、快速排序实现主框架
    • 2.2、找基准值的三种方法
      • 2.2.1、hoare版本
      • 2.2.1.1、算法思路
      • 2.2.1.2、代码实现
      • 2.2.2、挖坑法
      • 2.2.2.1、算法思路
      • 2.2.2.2、代码实现
      • 2.2.3、lomuto前后指针
      • 2.2.3.1、算法思路
      • 2.2.3.2、代码实现
    • 2.3、非递归版本
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档