冒泡、选择、插入三种比较简单

class Sort{
public:
//1.冒泡,比较相邻的元素,每次将最大的数移到后面 时间复杂度O(n^2)
void maopao(vector<int> &nums){
for(int i=0;i<nums.size()-1;i++){
for(int j=0;j<nums.size()-i-1;j++){
if(nums[j]>nums[j+1]){
int t = nums[j];
nums[j] = nums[j+1];
nums[j+1] = t;
}
}
}
}
//2.选择排序,每次找一个最大或最小的数,与冒泡不同不需要重复交换 时间复杂度O(n^2)
void xuanze(vector<int> &nums){
for(int i=0;i<nums.size()-1;i++){
int idx = i;
for(int j=i+1;j<nums.size();j++){
if(nums[j]<nums[idx]){
idx = j;
}
}
if(idx!=i){
int t = nums[i];
nums[i] = nums[idx];
nums[idx] = t;
}
}
}
//3.插入排序,每次将一个插入前面的有序序列(插入可以通过二分法)时间复杂度O(n^2)
void charu(vector<int> &nums){
for(int i=1;i<nums.size();i++){
for(int j=i-1;j>=0;j--){
if(nums[j]>nums[i]){
if(j==0){
int t = nums[i];
for(int x=i;x>0;x--){
nums[x] = nums[x-1];
}
nums[0] = t;
}
}
else{
int t = nums[i];
for(int x=i;x>j+1;x--){
nums[x] = nums[x-1];
}
nums[j+1] = t;
}
}
}
}

};

int main(){
vector<int> nums = {9,6,3,77,1,44,5,43,45,46,5,7};
Sort().charu(nums);
for(int n:nums){
cout<<n<<' ';
}
}

这里补充下上述三种排序的时间复杂度 O(n^2)的计算

algorithm: 十种排序算法_插入排序

4.希尔shell排序

希尔排序的基础是插入排序,它是以分组的形式来进行的。假如有个10个元素的数组,
第一次根据间隔5分为5个小组,各组内进行插入排序
第二次根据间隔2分为2个组,各组内插入排序
第三次根据间隔1分为1个组,组内插入排序

原理我也说不上,直觉上比较较远距离的数,使得移动时能跨过多个元素,则一次比较就可能跨越多个元素交换。
来个java版

class ShellSort{
static void shellSort(int[] nums){
//这里间隔 g 是动态生成的
for(int g=nums.length/2; g>0; g=g/2){
//从间隔大小的索引开始,各个分组同时插入排序
for(int j=g ;j<nums.length; j++){
int i = j;
int current = nums[i];
//current小于该元素,则后移,否则插入
while (i-g>=0 && current < nums[i-g]){
nums[i] = nums[i-g];
i = i-g;
}
nums[i] = current;
}
}
}
}

5.归并排序

其实也是分组的原理:

  • 将待排序的数组分为两组,分别排序,然后合并两个有序数组
  • 递归上述流程

class Al{
static void mergeSort(int[] nums){
if(nums.length==1){
return;
}
int mid = nums.length/2;
int[] leftArray = new int[mid];
int[] rightArray = new int[nums.length-mid];
System.arraycopy(nums,0,leftArray,0,mid);
System.arraycopy(nums,mid,rightArray,0,nums.length-mid);
// System.out.println(Arrays.toString(leftArray));
// System.out.println(Arrays.toString(rightArray));
mergeSort(leftArray);
mergeSort(rightArray);
merge(nums,leftArray,rightArray);
}

static void merge(int[] nums,int[] leftArray,int[] rightArray){
int i=0,j=0;
while (i<leftArray.length && j<rightArray.length){
if(leftArray[i]<=rightArray[j]){
nums[i+j] = leftArray[i];
i++;
}
else{
nums[i+j] = rightArray[j];
j++;
}
}
while (i<leftArray.length){
nums[i+j] = leftArray[i];
i++;
}
while (j<rightArray.length){
nums[i+j] = rightArray[j];
j++;
}
}
}

6.快速排序

快速排序与归并排序、希尔排序有个共同点,都是分组进行的

  • 选择一个基准元素,根据该元素按大于等于该元素和小于该元素分组
  • 对两个分组递归执行上述流程

快速排序的时间复杂度和空间复杂度都为

algorithm: 十种排序算法_时间复杂度_02

class Sort{
void quickSort(int[] nums){
quickSortHelper1(nums,0,nums.length-1);
}
private static void quickSortHelper1(int[] nums,int left,int right){
if(left>=right) return;
int index = quickSortHelper2(nums,left,right);
quickSortHelper1(nums,left,index-1);
quickSortHelper1(nums,index+1,right);
}
private static int quickSortHelper2(int[] nums,int left,int right){
int index = left+1;
for(int i=index;i<=right;i++){
if(nums[i]<nums[left]){
swap(nums,index,i);
index++;
}
}
swap(nums,left,index-1);
return index-1;
}
private static void swap(int[] nums,int i,int j){
int t = nums[i];
nums[i]=nums[j];
nums[j] = t;
}
}

c++版

class Solution {
public:
void quicksort(vector<int>& nums) {
int n = nums.size();
quickSort(nums, 0, n-1);
}
void quickSort(vector<int>& nums,int left,int right){
if(left>=right) return;
int x = left+1;
for(int i=x;i<=right;i++){
if(nums[i]<nums[left]){
swap(nums[x],nums[i]);
x++;
}
}
swap(nums[left], nums[x-1]);
quickSort(nums, left, x-2);
quickSort(nums, x, right);
}
};

从中间选择基准元素

void quick_sort(int q[],int l,int r)
{
if(l>=r)return;
// x作为分界点,x有四种取法,l,r,mid,随机
// 由于指针i、j交换完一次后都要往中间移动一个位置
int i=l-1,j=r+1,x=q[l+r>>1];
while(i<j)
{
// do while 语句先执行do里面的语句,再判断while条件,因此前面要将i和j都移动一个偏移量
// i找到大于等于x的,j找到小于等于x的,然后二者swap
do ++i;while(q[i]<x);
do --j;while(q[j]>x);
/*
不想写 do while 可以写下面两句
while(q[++i]<x);
while(q[--j]>x);
*/
if(i<j)swap(q[i],q[j]);
}
// 递归地处理左右两段
quick_sort(q,l,j),quick_sort(q,j+1,r);
}

​C++:双指针(快排既视感)​

7. 基数排序

基数排序分为最高位优先(Most Significant Digit first)法,简称MSD法和最低位优先(Least Significant Digit first)法,简称LSD法

以LSD为例:先从kd(个位)开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。

class RadixSort{
public:
void radixsort(int nums[],int n){
int d = maxbit(nums,n);
int *tmp = new int[n];
int *count = new int[10];
int radix = 1;
int i,j,k;
for(i=0;i<d;i++){
// count重置清0
for(j=0;j<n;j++){
count[j] = 0;
}
for(j=0;j<n;j++){
count[(nums[j]/radix)%10]++;
}
for(j=1;j<10;j++){
count[j] += count[j-1];
}
for(j=n-1;j>=0;j--){
tmp[--count[(nums[j] / radix)%10]] = nums[j];
}
for(j=0;j<n;j++){
nums[j] = tmp[j];
}
radix *= 10;
}
delete[] tmp;
delete[] count;
}
//求最大位数的辅助函数
int maxbit(int nums[],int n){
int d = 1;
int p = 10;
for(int i=0;i<n;i++){
while (nums[i]>=p){
p *= 10;
d++;
}
}
return d;
}
};

时间复杂度O(d*(n+k))=O(n)
空间复杂度O(n+k) = O(n)

十大经典排序算法(动图演示)百科-基数排序