数据结构精讲——排序(二)

文章介绍了编程中常见的两种排序算法:冒泡排序和快速排序。冒泡排序是基础的排序方法,时间复杂度为O(n^2),适合初学者。快速排序是一种效率较高的排序算法,由Hoare在1962年提出,基于分治策略,平均时间复杂度为O(nlogn)。文中提供了不同版本的快速排序实现,包括hoare版本、挖坑法和前后指针版本。
摘要由CSDN通过智能技术生成

数据结构精讲——排序(二)

排序的分类

在这里插入图片描述
上节课我们已经了解过插入排序选择排序,这节课主要讲的是快排排序,冒泡排序应该都很熟悉,少提一下吧。

冒泡排序

冒泡排序是我们刚接触编程时就学习的排序方法(还有选择排序)。
其原理就是将相邻的两个数据节点节进行比较然后慢慢完后转移比较,很像水中的泡泡。
在这里插入图片描述
一共七趟(n-1趟)。

程序实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;

int main()
{
	int a[] = { 1,8,7,3,5,4,6,2 };
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < n; i++)
	{
		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
			{
				swap(a[j - 1], a[j]);
			}
		}
	}
	for (int i = 0; i < n; i++)
	{
		cout << a[i] <<' ';
	}
	return 0;
}

时间复杂度为O(n^2),所以效率并不高,但是好理解所以新手好学一下。
只是简单说一下,并不做重点。

快排

在学C语言时我们了解过qsort这个函数,其内核就是快排。
在这里插入图片描述
base是数组的第一个节点的指针,num是排序数组的成员个数(要排序几个成员),size是每一个成员的类型大小(char=1;double=8;int=4),compar是比较函数(需要在运算后将void*改成int返回出来)。

compare函数:

在这里插入图片描述
如果小于返回-1,如果大于返回1,等于返回0。

快排原理

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

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
 if(right - left <= 1)
 return;
 
 // 按照基准值对array数组的 [left, right)区间中的元素进行划分
 int div = partion(array, left, right);
 
 // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
 // 递归排[left, div),左闭右开
 QuickSort(array, left, div);
 
 // 递归排[div+1, right)
 QuickSort(array, div+1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉
树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。
将区间按照基准值划分为左右两半部分的常见方式有:

hoare版本

在这里插入图片描述
我们可以把L与R相遇的地方变成新keyi,新keyi左边都比keyi小,右边都比keyi大。
然后再进行递归。
在这里插入图片描述
再进行分批排序。

void QSort1(int* a, int begin, int end)//hoare
{
	int keyi = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])//右边找小于a[keyi]的数
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])//左边找大于a[keyi]的数
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	//将a[keyi]与相遇点交换(要保证相遇点比a[keyi]小,需要让right先走)
	Swap(&a[keyi], &a[right]);
	QSort1(a, begin, keyi-1);
	QSort1(a, keyi+1,end);

}
挖坑法

先保存a[keyi]的值到int temp上,然后将keyi先作坑,int left=0、int right=n-1

先让right走,找到小于a[keyi]的数,就将它放在a[hole]处,更新hole=right。然后left再走,找到大于a[keyi]的数,然后将它放在a[hole]处,再次更新hole=left,再让right移动,left移动,直到left等于right。此时相遇点必然是一个坑,最后将temp放在a[hole]处。
在这里插入图片描述

int Q_PartSort2(int* a, int begin, int end)//挖坑法
{
	int key = a[begin];
	int hole = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}
void _QuickSort(int* a, int begin, int end)//递归
{
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort2(a, begin, end);

	_QuickSort(a, begin, keyi - 1);
	_QuickSort(a, keyi + 1, end);
}
前后指针版本

取最左边的下标左keyi,prev=begin、next=begin+1,
next找小,如果找到小于a[ keyi ]的数,就让prev++,
然后将a[prev]和a[next]交换。直到next大于n,结束。
最后再让a[keyi]和a[prev]交换。
在这里插入图片描述

int Q_PartSort3(int* a, int begin, int end)//前后指针法
{
	int keyi = begin;
	int prev = begin;
	int next = begin + 1;
	while (next <= end)
	{
		if (a[next] < a[keyi] && ++prev != next)
		{
			Swap(&a[prev], &a[next]);
		}
		next++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}
void _QuickSort(int* a, int begin, int end)//递归
{
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort3(a, begin, end);

	_QuickSort(a, begin, keyi - 1);
	_QuickSort(a, keyi + 1, end);
}

快速排序优化

  1. 三数取中法选key
  2. 递归到小的子区间时,可以考虑使用插入排序
    在这里插入图片描述

快速排序非递归

void QuickSortNonR(int* a, int left, int right)
{
Stack st;
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
while (StackEmpty(&st) != 0)
{
 right = StackTop(&st);
 StackPop(&st);
 left = StackTop(&st);
 StackPop(&st);
 
 if(right - left <= 1)
 continue;
 int div = PartSort1(a, left, right);
 // 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)
 StackPush(&st, div+1);
 StackPush(&st, right);
 
 StackPush(&st, left);
 StackPush(&st, div);
}
 
 StackDestroy(&s);
}

排序的代码

我的全部代码,拿走不谢!

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

#define MAX 10

void display(int arr[], int num) {//遍历数组
    int i;
    for (i = 0; i < num; i++) {
        printf("%-5d", arr[i]);
    }
    printf("\n");
    return;
}

int Q_PartSort1(int* a, int begin, int end)//hoare
{
	int keyi = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])//右边找小于a[keyi]的数
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])//左边找大于a[keyi]的数
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	//将a[keyi]与相遇点交换(要保证相遇点比a[keyi]小,需要让right先走)
	Swap(&a[keyi], &a[right]);
	/*Q_PartSort1(a, begin, keyi-1);
	Q_PartSort1(a, keyi+1,end);*/
	return right;
}
int Q_PartSort2(int* a, int begin, int end)//挖坑法
{
	int key = a[begin];
	int hole = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}
int Q_PartSort3(int* a, int begin, int end)//前后指针法
{
	int keyi = begin;
	int prev = begin;
	int next = begin + 1;
	while (next <= end)
	{
		if (a[next] < a[keyi] && ++prev != next)
		{
			Swap(&a[prev], &a[next]);
		}
		next++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}
void _QuickSort(int* a, int begin, int end)//递归
{
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort3(a, begin, end);

	_QuickSort(a, begin, keyi - 1);
	_QuickSort(a, keyi + 1, end);
}
int main()
{

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tom王要coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值