6.快速排序
1.方法
属于分治法,主要思想是选择一个基准,把小于等于基准的放置在左边,把大于等于基准的放置在右边,然后再对左边和右边递归排序,则完成排序。
2.步骤
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
3.动图演示:
4.代码实现
void QuickSort(int arr[], int left,int right) {
if (left >= right) {
return;
}
int pivot = arr[left];//选最左侧为中枢
int i = left, j = right;
while (i < j) {
//选数组第一个作为基元,那么应该从数组尾端开始进行比较,
//否则下面程序行不通,因为从头部开始会丢失基元。
while (i < j&&arr[j] >= pivot)//从右向左找<中枢的B
j--;
while (i < j&&arr[i] <= pivot) //从左向右找>中枢的A
i++;
if (i < j) {//如果A在B的左侧,则交换
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
//让中枢和最后一个<=换位
arr[left] = arr[i];
arr[i] = pivot;
//递归处理中枢左右两侧
QuickSort(arr, left, i - 1);
QuickSort(arr, i + 1, right);
}
7.堆排序
1. 方法
主要利用堆的数据结构,大顶堆,即每个节点的值都大于或等于其子节点的值,小顶堆则相反。将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
对堆中的结点按层进行编号,将这种逻辑结构映射到数组中 :
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] ;小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
构造初始堆
2. 步骤
- 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
- 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
- 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
4.代码实现
void adjustMaxHead(int arr[], int i, int n) {
int maxIndex = i;//以i为父节点调整大顶堆
if (i * 2 <= n && arr[i * 2] > arr[maxIndex]) {//如果存在左子树 且比当前节点大
maxIndex = i * 2;
}
if (i * 2 + 1 <= n && arr[i * 2 + 1] > arr[maxIndex]) {//如果存在右子树 且比当前节点大
maxIndex = i * 2 + 1;
}
if (maxIndex != i) {//如果当前父节点不是最大值,则父节点和子节点交换
int tmp = arr[maxIndex];
arr[maxIndex] = arr[i];
arr[i] = tmp;
adjustMaxHead(arr, maxIndex, n);//交换后,递归处理新的子节点,保证新的子节点是大顶堆
}
}
void HeadSort(int arr[], int n) {
//从最后一个非叶子节点开始,arr.size()/2-1,往前遍历,构造大顶堆
for (int i = n / 2 - 1; i >= 0; i--) {
adjustMaxHead(arr, i, n);
}
while (n > 0) {//把大顶堆的顶(最大值)放在数组最后
int tmp = arr[0];
arr[0] = arr[n];
arr[n] = tmp;
n--;
adjustMaxHead(arr, 0, n);//对剩下的n-1个元素做同样操作
}
}
8.计数排序
1. 方法
为每个元素初始化一个计数器,按出现的次数累加,最后按计数结果保存在数组位置。
2. 步骤
- 根据待排序集合中最大元素和最小元素的差值范围,申请额外空间;
- 遍历待排序集合,将每一个元素出现的次数记录到元素值对应的额外空间内;
- 对额外空间内数据进行计算,得出每一个元素的正确位置;
- 将待排序集合每一个元素移动到计算得出的正确位置上
3. 图解示例:
4. 代码实现:
void CountingSort(int arr[], int n) {
if (n == 0) return;
//1. 找数组的最大最小值
int max = arr[0], min = arr[0];
for (int i = 0; i < n; i++) {
max = max > arr[i] ? max : arr[i];
min = min < arr[i] ? min : arr[i];
}
//2.为每一个可能元素初始化一个计数器,按出现次数累加
vector<int> res(max - min + 1, 0);
for (int i = 0; i < n; i++) {
res[arr[i] - min]++;
}
//3.根据计数器的顺序和元素出现的次数,写回原数组
int counter = 0;
for (int j = 0; j < res.size(); j++) {
for (int k = 0; k < res[j]; k++) {
arr[counter++] = j + min;
}
}
}
9.桶排序
1. 方法
计数排序的升级版,计数排序相当于每个元素一个桶,但是桶排序是若干个元素一个桶。将元素分桶后,每个桶再自行排序(有可能使用别的排序算法或是以递归方式继续使用桶排序),最后将排序好的结果结合到一起。
2. 步骤
- 设置一个定量的数组当作空桶;
- 遍历输入数据,并且把数据一个一个放到对应的桶里去;
- 对每个不是空的桶进行排序;
- 从不是空的桶里把排好序的数据拼接起来。
3. 动图演示
4.代码实现
void BucketSort(vector<int>& v, int bucketsize) {
if (v.size() <= 1) return;
int n = v.size();
// 找出最大值和最小值
int min = v[0], max = v[0];
for (int i = 0; i < n; i++) {
max = max >= v[i] ? max : v[i];
min = min <= v[i] ? min : v[i];
}
//根据桶的尺寸,初始化桶的数量
int bucketCount = (max - min) / bucketsize + 1;
vector<vector<int>> bucket(bucketCount, vector<int>());
for (int i = 0; i < n; i++) { 按照元素的值装入各自的桶中
int bucketIndex = (v[i] - min) / bucketsize;
bucket[bucketIndex].push_back(v[i]);
}
int index = 0;
for (int i = 0; i < bucketCount; i++) {
//此处可以用其他排序算法,这里用递归实现
if (bucketsize > 1) {//如果桶的尺寸大于1,正民府可以递归再次排序,直到桶的尺寸为1;
if (bucketCount == 1) {
bucketsize = 1;//当前只有一个桶了,把桶尺寸收缩为1, 进行最后一次排序
}
BucketSort(bucket[i], bucketsize);
}
//把已经排好序的桶写回数组
for (int j = 0; j < bucket[i].size(); j++) {
v[index++] = bucket[i][j];
}
}
}
10.基数排序
1. 方法
先低位后高位:先以个位数的大小来对数据进行排序,接着以十位数的大小来对数据进行排序,接着以百位数的大小……由于某位数(个位/十位….,不是一整个数)的大小范围为0-9,所以我们需要10个桶,然后把具有相同数值的数放进同一个桶里,之后再把桶里的数按照0号桶到9号桶的顺序取出来,这样一趟下来,按照某位数的排序就完成了
2. 步骤
- 取得数组中的最大数,并取得位数;
- arr为原始数组,从最低位开始取每个位组成radix数组;
- 对radix进行计数排序(利用计数排序适用于小范围数的特点);
3. 动图演示:
4. 代码实现
int maxdigit(int data[], int n)//求数据的最大位数
{
int d = 1;//保存最大的位数
int p = 10;
for (int i = 0; i < n; ++i)
{
while (data[i] >= p)
{
p *= 10;//100?1000?三位,四位
++d;
}
}
return d;
}
void Radixsort(int data[], int n)//基数排序
{
list<int> list[10];
int digit = maxdigit(data, n);
int i, factor, j, k;
for (i = 1, factor = 1; i <= digit; factor *= 10, ++i)
{
for (j = 0; j < n; ++j)
{
list[(data[j] / factor) % 10].push_back(data[j]);
}
for (j = k = 0; j < 10; ++j)
{
while (!list[j].empty())//链表非空
{
data[k++] = list[j].front();//复制回数组
list[j].pop_front();//取一个删一个
}
}
//输出每次排序的中间结果
for (int m = 0; m < n; ++m)
cout << data[m] << " ";
cout << endl;
}
}