algorithm: 十种排序算法

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

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)的计算
∑ k = 1 n n − k = n 2 − ∑ i = 1 n k = n 2 − n ( n + 1 ) 2 = n 2 − n 2 \sum_{k=1}^nn-k=n^2-\sum_{i=1}^nk=n^2-\frac{n(n+1)}{2}=\frac{n^2-n}{2} k=1nnk=n2i=1nk=n22n(n+1)=2n2n

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.归并排序

其实也是分组的原理:

  • 将待排序的数组分为两组,分别排序,然后合并两个有序数组
  • 递归上述流程
def mergesort(nums):
    n = len(nums)
    if n <= 1: return
    mid = n//2
    L = nums[:mid]
    R = nums[mid:]
    mergesort(L)
    mergesort(R)
    i=j=k=0
    while i<len(L) and j<len(R):
        if L[i]<=R[j]:
            nums[k] = L[i]
            i += 1
        else:
            nums[k] = R[j]
            j += 1
        k += 1
    while i < len(L):
        nums[k] = L[i]
        i += 1
        k += 1
    while j < len(R):
        nums[k] = R[j]
        j += 1
        k += 1
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.快速排序

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

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

快速排序的时间复杂度和空间复杂度都为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

def helper(nums, st, et):
    if st < et:
        n = nums[et]
        i = st -1
        for j in range(st,et):
            if nums[j] < n:
                i += 1
                nums[i], nums[j] = nums[j], nums[i]
        nums[i+1], nums[et] = nums[et], nums[i+1]
        helper(nums, st, i)
        helper(nums, i+2, et)

def quicksort(nums):
    helper(nums, 0, len(nums)-1)
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)

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值