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;
}