算法之排序

本文详细介绍了冒泡排序、选择排序、插入排序、快速排序和归并排序的基本原理、时间复杂度以及实现方法,特别强调了它们在IT技术中的应用和适用场景。
摘要由CSDN通过智能技术生成


1.冒泡排序

时间复杂度: O(N^2)

冒泡排序也就是相邻元素进行比较,如果第一个比第二个大,就交换他们两个,每次内部for循环执行完,就确定一个元素的位置,外部for循环结束,数组也就排序完了

		int[] arr={5,4,3,2,1};
        for(int i=0;i<arr.length;i++){
            for(int j=i+1;j<arr.length;j++){
                //如果当前值比后一位大,交换两个值的位置
                if(arr[i]>arr[j]){
                    int t=arr[i];
                    arr[i]=arr[j];
                    arr[j]=t;
                }
            }
            //一层循环确定一个位置的值
        }

2.选择排序

时间复杂度: O(N^2)

选择排序:选择排序思想和冒泡排序有点类似,假如是升序,对于外层for,他们都是一次排序后把最小的元素放到最前面,但是过程不同,冒泡排序是通过相邻的比较和交换,而选择排序是通过对整体的选择,内层for循环找出最小值的索引,内层for循环结束就会确定最小值得索引,在外层for循环里将找的最小值和i位置的值交换。

		int[] arr={5,4,3,2,1};
        //总共要经过 N-1 轮比较
        for(int i=0;i<arr.length-1;i++){
            int min=i;
            //每轮需要比较的次数 N-i
            for(int j=i+1;j<arr.length;j++){
                if(arr[j]<arr[min]){
                    //记录目前能找到的最小值元素的下标
                    min=j;
                }
            }
            //将找打的最小值和i位置所在的值进行交换
            if(i != min){
                int tmp=arr[i];
                arr[i]=arr[min];
                arr[min]=tmp;
            }
        }

3.插入排序

时间复杂度: O(N^2)
🍉举个例子
假如升序排列int[] arr={7,4,1,2,5};
数组下表从0开始,插入排序就如图所示,从后往前比较,并插入
在这里插入图片描述

插入排序是一种最简单直观的排序算法,他的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后往前扫描,找到相应位置并插入。
📕算法步骤
1.将第一个待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
2.从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[] arr=new int[n];
        for (int i = 0; i <n ; i++) {
                arr[i]=sc.nextInt();
        }
      //从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
      for(int i=1;i<arr.length;i++){
          //记录要插入的数据
          int tmp=arr[i];
          //从已经排序的序列最右边的开始比较,找到比其小的数
          int j=i;
          while(j>0&&tmp<arr[j-1]){
              arr[j]=arr[j-1];
              j--;
          }
          //存在比其小的数,插入
          if(j!=i){
              arr[j]=tmp;
          }
      }
        for (int i = 0; i < n; i++) {
            System.out.print(arr[i]+" ");
        }
    }
}

4.快速排序

时间复杂度: O(N^2)
🍉举个例子
假如升序排序7 5 2 3 4
若随机选择3为基数,比3小的放3前面,比3大的放3后面,则排序后为:2 3 7 5 4
然后若随机选择5为基数,比5小的放5前面,比5大的放5后面,则排序后为:2 3 4 5 7

🍉快速排序的基本思想是通过一趟排序将待排记录分隔成独立的两部分其中一部分记录的数据均比另一部分记录的数据小,然后对这两部分记录继续进行排序,以达到整个序列有序。

快速排序是一种原地排序算法,它对输入数组进行原地排序而不需要额外的存储空间。算法的时间复杂度为O(n log n),其中n是数组的长度。快速排序的最坏情况是O(n^2),比如说顺序数列的快排。但它的平摊期望时间是O(nlogn),且O(nlogn)记号中隐含的常数因子很小,比复杂度稳定等于O(nlogn)的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序

🍉算法步骤
1.从数列中挑出个元素,称为"基准"( pivot)
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区( partition)操作
3.递归地( recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[] arr=new int[n];
        for (int i = 0; i <n ; i++) {
                arr[i]=sc.nextInt();
        }
        arr=quickSort(arr,0,n-1);

        for (int i = 0; i < n; i++) {
            System.out.print(arr[i]+" ");
        }
    }
    //left为起始位置  right为结束位置
    private static int[] quickSort(int[] arr,int left,int right){
        if(left<right){

            int partitionIndex=partition(arr,left,right);
            //递归
            quickSort(arr,left,partitionIndex-1);
            quickSort(arr,partitionIndex+1,right);
        }
        return arr;
    }
    //分区
    private static int partition(int[] arr,int left,int right){
        //设置基准值
        int pivot=left;
        int index=pivot+1;
        for(int i=index;i<=right;i++){
            if(arr[i]<arr[pivot]){
                swap(arr,i,index);
                index++;
            }
        }
        swap(arr,pivot,index-1);
        return index-1;
    }
    private static void swap(int[] arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
}

5.归并排序

时间复杂度: o(nlogn)
归并排序:是创建在归并操作上的一种有效的排序算法。算法是采用分治法的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项和对有序的数列。时间复杂度:o(nlogn)
📕算法步骤
1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4.重复步骤3直到某一指针达到序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[] arr=new int[n];
        for (int i = 0; i <n ; i++) {
            arr[i]=sc.nextInt();
        }
        arr=mergeSort(arr,0,n-1);
        for(int x:arr){
            System.out.print(x+" ");
        }
    }
    //l为起始位置  h为结束位置
    public static int[] mergeSort(int[] nums,int l,int h){
        //如果起始位置等于结束位置
        if(l==h){
            return new int[]{nums[l]};
        }
        //否则 取中间值
        int mid=(l+h)/2;
        //通过递归调用自己,获取已经排好序的左有序数组
        int[] leftArr=mergeSort(nums,l,mid);
        //通过递归调用自己,获取已经排好序的右有序数组
        int[] rightArr=mergeSort(nums,mid+1,h);
        //申请一个长度为左数组长度加右数组长度之和的新有序数组
        int[] newNum=new int[leftArr.length+rightArr.length];

        int m=0,i=0,j=0;
        while (i<leftArr.length && j<rightArr.length){
            newNum[m++]=leftArr[i]<rightArr[j] ? leftArr[i++] : rightArr[j++];
        }
        while (i<leftArr.length){
            newNum[m++]=leftArr[i++];
        }
        while (j<rightArr.length){
            newNum[m++]=rightArr[j++];
        }
        return newNum;
    }
}

6.桶排序

桶排序是一种非比较的排序算法。采用分类和分治的思想,把元素的值域分为若干段,每一段对应一个桶。在排序的时候,首先把每一个元素放到对应的桶中,再对每一个桶中的元素分别排序,再按顺序把每个桶中的元素依次取出,合并成最终答案。

①对于数据量较大但值域较小的数据,如n>10的7次方,a(i)<10的6次方,可以做到每个值对应一个桶,桶排序的时间复杂度为O(n)。推荐使用桶排序。

②对于值域较大的数据,桶排序的时间复杂度与每个桶内排序的方法有关,优势不明显,对于这种数据一般不适用桶排序。

import java.util.*;

public class Main {
    private static final int MAXN = 500007;
    private static int[] bucket = new int[MAXN];
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        for (int i = 1; i <= n; i++) {
            int x = scanner.nextInt();
            bucket[x]++;
        }
        for (int i = 0; i <= n; i++) {
            for (int j = 1; j <= bucket[i]; j++) {
                System.out.print(i + " ");
            }
        }
        System.out.println();
        scanner.close();
    }
}

c语言代码为:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 7;
int n;
int bucket[MAXN];
int main() {
    cin >> n;
    for (int i = 1; i <= n; i ++ ) {
        int x;
        cin >> x;
        bucket[x] ++;
    }
    for (int i = 0; i <= n; i ++ ) {
        for (int j = 1; j <= bucket[i]; j ++ )
            cout << i << " ";
    }
    cout << endl;
}
  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

miss you ya

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

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

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

打赏作者

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

抵扣说明:

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

余额充值