选择排序、快速排序、归并排序、堆排序、快速排序实现及Sort()函数使用
1.问题来源
在刷题是遇到字符串相关问题中使用 strcmp()函数。
在函数比较过程中有使用 排序函数 Sort(beg,end,comp),其中comp这一项理解不是很彻底。
#include <vector>
#include <cstring>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<const char*> cats {"Heathcliff", "Snagglepuss", "Hobbes", "Garfield"};
std::sort(cats.begin(), cats.end(), [](const char *strA, const char *strB) { return std::strcmp(strA, strB) < 0; }); // 注意这个comp的写法
for (const char *cat : cats) {
std::cout << cat << '\n';
}
}
/*
*输出:
*Garfield
Heathcliff
Hobbes
Snagglepuss
*/
/*
* [](const char *strA, const char *strB) { return std::strcmp(strA, strB) < 0; } 不是很理解 ,为什么在前面还需要 [] 来实现?
*/
当然也可以定义一个comp变量函数
std::sort(begin(),end(),compare);
bool compare (const char* strA,const char* strB )
{
return std::strcmp(strA,strB)<0;
}
2.comp的定义
Elements are compared using the given binary comparison function comp.
这一块的内容还是需要继续学习
3.各种排序算法的实现
排序算法列表:
0.BubbleSort
- 冒泡排序实现,两两比较关键字
// 基础实现
void BubbleSort(vector<int>& nums){
int i,j;
int len=nums.size();
for(i=0;i<len;++i){
// 后面向前进行循环
// 最小值冒泡到顶端
for(j=len-2;j>=i;--j){
if(nums[j]>nums[j+1])
{
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
}
// 改进版本
void BubbleSort2(vector<int>& nums){
int i,j;
int len=nums.size();
bool flag=true;
for(i=0;i<len &&flag;i++){
flag=false;
for(j=len-2;j>=i;--j){
if(nums[j]>nums[j+1]){
// 有数据交换
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
flag=true;
}
}
}
}
1.Selection Sort
- 选择排序的基本思路:
在未排序的数组中找到最小的元素与未排序数组的第一个元素进行交换
void SelectionSort(vector<int>& nums)
{
for(int i=0;i<nums.size();++i)
{
int min_index=i;
for(int j=i+1;j<nums.size();++j) // 遍历维排序数组
{
if(nums[j]<nums[min_index])
min_index=j; // 找到最小的元素的下标
}
int temp=nums[i]; // 交换
nums[i]=nums[min_index];
nums[min_index]=temp;
}
}
/*
* 时间复杂度: O(n^2) 空间复杂度 : O(1)
* in_palce order ?
*/
2.InsertionSort
- 插入排序的基本思路:
与要插入的元素之前的元素进行对比,元素要不断的向右进行移动
void InsertionSort(vector<int>& nums)
{
for(int i=1;i<nums.size();++i)
{
int temp=a[i]; // 要插入的元素
int j=i-1;
while(j>=0 && temp<a[j]) // 之前的元素相对比,向右移动
{
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
/*
* 时间复杂度 O(n^2)
*/
同样的解题思路,java代码看着更舒服
public class Insertion
{
public static void sort(Comparable[] a)
{
// ascending
int N=a.length;
for(int i=1;i<N;i++)
{
// 将 a[i]插入到 a[i-1],a[i-2]中
for(int j=i;j>0 && less(a[j],a[j-1]);j--)
{
exch(a,j,j-1);
}
}
}
}
3.ShellSort
希尔排序的主要思路:
希尔排序是基于插入排序的快速排序
1.交换不相邻的元素对数组的局部进行排序(最重要的就是间隔的选择)
2.最终使用插入排序将局部有序的数组排序
/* 至今无法解释其算法性能
* 这个算法熟悉知道一下就好
*/
public class Shell
{
public static void sort(Comparable[] a)
{
int N=a.length();
int h=1; // 定义这个间隔 gap
while(h<N/3)
h=h*3+1;
while(h>=1)
{
for(int i=h;i<N;i++)
{
for(int j=i;j>0 && less(a[j],a[j-h]);j-=h)
exch(a,j,j-h);
}
h=h/3; // 缩小gap继续进行交换
}
}
}
Cplusplus implemented
void ShellSort(int a[],int len)
{
for(int gap=len/2;gap>0;gap/=2)
{
for(int i=gap;i<len;i++)
{
int j=i-gap;
while(j>=0 && a[j]>a[j+gap])
{
swap(a[j],a[j+gap]);
j-=gap;
}
}
}
}
4.MergeSort
归并排序的主要思路:
现将数组递归的分成两半分别排序,再将结果归并起来
将数组递归的分成最小,按照有序的方式归并起来
/*
* 所需的时间复杂度O(NlogN)
*/
// 两个数组的原地归并
public class Merge
{
private static Comparable[] aux;
public static void sort(Comparable[] a)
{
aux=new Comparable[a.length]; // 分配空间
sort(a,0,a.length-1);
}
private static void sort(Comparable[] a,int lo,int hi) // a[lo,hi]进行排序
{
if(hi<lo)
return;
int mid=lo+(hi-lo)/2;
sort(a,lo,mid);
sort(a,mid+1,hi);
merge(a,lo,mid,hi);
}
public static void merge(Comparable[]a,int lo,int mid,int hi)
{
int i=lo,j=mid+1;
for(int k=lo;k<hi;++k)
aux[k]=a[k]; // 将数组完全复制到一个临时数组中
for(int k=lo;k<=hi;k++)
{
if(i>mid) a[k]=aux[j++]; // 分别对应左半数组和右半数组用尽的情况
else if (j>hi) a[k]=aux[i++];
else if (less(a[i],a[j])) a[k]=aux[i++];
else a[k]=aux[j++];
}
}
}
5.HeapSort
堆排序
- 经典而又优雅的排序算法
堆排序思路:
基于(二叉堆的数据结构)优先队列实现,优先队列是利用二叉堆来实现的。
1.根据堆的构造,现将堆构造出来
2.根据 sink 算法实现堆的下沉排序 最后实现有序
sink 算法,将堆有序的最后一个元素交换到顶点,从顶点开始下沉操作
// 堆的有序化
private void swim (int k)
{
while(k>1 && less(k/2,k))
{
exch(k/2,k);
k=k/2;
}
}
private void sink(int k)
{
while(2*k<=N) // 保证还有字节点可以下沉比较
{
int j=2*k;
if(j<N && less(j,j+1)) j++; // 找到字节点中最大的进行交换
if(!less(k,j)) break;
exch(k,j);
k=j;
}
}
public class Heap {
// This class should not be instantiated.
private Heap() { }
/**
* Rearranges the array in ascending order, using the natural order.
*/
public static void sort(Comparable[] pq) {
int n = pq.length;
for (int k = n/2; k >= 1; k--)
sink(pq, k, n); // 第一步实现堆的有序 2N项比较
while (n > 1) { // 2NlgN次比较
exch(pq, 1, n--); // 交换顶点和尾结点的元素
sink(pq, 1, n); // 进行下沉
}
}
/***************************************************************************
* Helper functions to restore the heap invariant.
***************************************************************************/
private static void sink(Comparable[] pq, int k, int n) {
while (2*k <= n) {
int j = 2*k;
if (j < n && less(pq, j, j+1)) j++;
if (!less(pq, k, j)) break;
exch(pq, k, j);
k = j;
}
}
}
6.QuickSort
快速排序的思路:
最重要的是找到切分元素,将数组分成左右两个部分 (切分元素的选取)
切分后进行双指针比较,根据与切分元素的比较,将元素进行交换位置
将左右两个部分进行排序
/*
* 快速排序是原地排序
* 分而治之的排序方法
* 时间复杂度 O(NlgN)
*/
public class Quick
{
public static void sort(Comparable[] a)
{
StdRandom.shuffle(a); // 重新打乱混淆一下数组,这样对切分元素的选择更理想
sort(a,0,a.length-1);
}
private static void sort(Comparable[]a,int lo,int hi)
{
if(hi<=lo) // 进行错误判断
return;
int j=partition(a,lo,hi); // get 切分元素
sort(a,lo,j-1); // 左右两边数组进行排序
sort(a,j+1,hi);
}
/* 切分的实现*/
private static int partition(Comparable[]a ,int lo,int hi)
{
// 将数组进行分离 a[lo,...i-1] a[i+1...hi]
int i=lo,j=hi+1;
Comparable v=a[lo]; // 确定切分元素
while(true)
{
while(less(a[++i],v)) if(i==hi) break; // 到不小于V,应该在右侧的元素
while(less(v,a[--j])) if(j==lo) break; // 小于 V,应该在左侧的元素
if(i>=j) break;
exch(a,i,j);
}
exch(a,lo,j); // 将v 放入正确的位置,切分值放到a[j]中
/* a[lo,..j-1]<=a[j]<=a[j+1..hi] */
return j;
}
}