学习笔记---排序

排序算法

在这里插入图片描述

在这里插入图片描述

快速排序(重点)

基本步骤

  1. 确定分界点
  2. 调整区间
  3. 递归处理

基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

注意边界问题,分界点和递归参数选取不当可能造成死循环

##时间复杂度

最优O(nlogn)\最差O(n2)

##空间复杂度

最优O(logn)\最差O(n)

具体复杂度的推导可以参考:快速排序的时间复杂度和空间复杂度分析(图文结合)-CSDN博客

#经典快排
#include<iostream>
using namespace std;

const int N = 1e6 + 10;
int q[N];
int n;

void quik_sort(int q[], int l,int r){
    if(l>=r)
    return;
    
    int mid = q[(l+r)>>1],i=l-1,j=r+1;
    while(i<j){
        do i++;while(q[i]<mid);
        do j--;while(q[j]>mid);
        if(i<j)swap(q[i],q[j]);
    }
    quik_sort(q, l, j);
    quik_sort(q, j+1, r);
    
}

int main(){
    scanf("%d",&n);
    
    for(int i = 0;i < n;i++)
    scanf("%d",&q[i]);
    
    quik_sort(q,0,n-1);
    
    for(int i = 0;i < n;i++)
    printf("%d ",q[i]);
    
    return 0;
}
#第k个数
#include<iostream>
using namespace std;

const int N = 1e6+10;
int k,n;
int q[N];

int quik_sort(int l,int r,int k){
    if(l>=r)
    return q[l];
    
    int mid = q[(l+r)>>1],i = l-1,j = r+1;
    while(i<j){
        do i++;while(q[i]<mid);
        do j--;while(q[j]>mid);
        if(i<j)swap(q[i],q[j]);
    }
    #选择对应区间
    int num = j-l+1;
    if(num>=k) return quik_sort(l,j,k);
    else return quik_sort(j+1,r,k-num);
}

int main(){
    scanf("%d",&n);
    scanf("%d",&k);
    
    for(int i = 0;i<n;i++)
    scanf("%d",&q[i]);
    
    printf("%d",quik_sort(0,n-1,k));
    return 0;
}

归并排序(重点)

基本步骤

  1. 确定分界点,mid=(l+r)/2
  2. 递归排序left和right
  3. 归并,合二为一

基本思想:归并排序算法有两个基本的操作,一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素,这时,可以认为只包含一个元素的子表是有序表。将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。

时间复杂度

O(nlogn)

空间复杂度

O(n),归并排序需要一个与原数组相同长度的数组做辅助来排序

参考文章:排序——归并排序(Merge sort)-CSDN博客

#经典归并排序
#include<iostream>
using namespace std;

const int N = 1e5+10;
int n;
int q[N],temp[N];

void merge_sort(int q[],int l ,int r){
    if(l>=r)
    return;
    
    int mid = (l+r)>>1;
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    
    int num = 0,i = l,j = mid+1;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[num++]=q[i++];
        else temp[num++]=q[j++];
    }
    while(i<=mid) temp[num++]=q[i++];
    while(j<=r) temp[num++]=q[j++];
    
    for(int i=l ,j = 0;i<=r;i++,j++) q[i]=temp[j];
}

int main(){
    scanf("%d",&n);
    for(int i = 0;i<n;i++)
    scanf("%d",&q[i]);
    
    merge_sort(q,0,n-1);
    
    for(int i = 0 ;i<n;i++)
    printf("%d ",q[i]);
    
    return 0;
}
#逆序对数量
#include<iostream>
using namespace std;

typedef long long ll;
const int N = 1e5+10;
int n;
int q[N],temp[N];

ll merge_sort(int l,int r){
    if(l>=r)
    return 0;
    
    int mid = (l+r)>>1;
    ll num = 0;
    num+=merge_sort(l,mid);
    num+=merge_sort(mid+1,r);
    
    int i = l,j = mid+1,k = 0;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else{
            temp[k++]=q[j++];
            num+=mid-i+1;
        }
    }
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(int i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
    return num;
}

int main(){
    scanf("%d",&n);
    for(int i = 0;i<n;i++)
    scanf("%d",&q[i]);
    
    printf("%lld",merge_sort(0,n-1));
    return 0;
}

冒泡排序

算法步骤

冒泡排序(Bubble Sort)是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

时间复杂度

平均:O(n2),最好O(n),最坏O(n2)

空间复杂度

O(1)

#经典款冒泡排序
void bubble_sort(int arr[], int len) {
        int i, j;
        for (i = 0; i < len - 1; i++)
                for (j = 0; j < len - 1 - i; j++)
                        if (arr[j] > arr[j + 1])
                                swap(arr[j], arr[j + 1]);
}
 #增设判断的冒泡排序,不过感觉用处不大
 void bubbleSort(int array[], int length)
 {
     int i, j, tmp;
     int flag = 1;
     
     if (1 >= length) return;
 
     for (i = length-1; i > 0; i--, flag = 1){ 
         for (j = 0; j < i; j++){
             if (array[j] > array[j+1]){
                 tmp = array[j];
                 array[j] = array[j+1];
                 array[j+1] = tmp;
                 flag = 0;
             }   
         }   
         if (flag) break;
     }   
 }
#改进的冒泡排序(鸡尾酒排序)就是把最大的数往后面冒泡的同时,最小的数也往前面冒泡
void cocktailSort(int arr [] , int n) {
        int L = 0;
        while(L < n) {
            for(int i = L; i < n-1; i++) 
            {
                if(arr[i] > arr[i+1])
                {
                        int temp = arr[i];
                        arr[i] = arr[i+1]; 
                        arr[i+1] = temp;
                }
            }
            n--;
            for(int i = n-1; i > L; i--) 
            {
                if(arr[i] < arr[i-1])
                {
                        int temp = arr[i];
                        arr[i] = arr[i-1]; 
                        arr[i-1] = temp;
                }
            }
            L++;
        }
}

选择排序

算法步骤

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。重复第二步,直到所有元素均排序完毕。

时间复杂度

O(n2)

空间复杂度

O(1)

#经典选择排序
void Select_Sort(int arr[], int n)   
{
	for (int i = 0; i < n-1; i++) {
		int min = i;
		for (int j = i; j < n; j++) {
			if (arr[min] > arr[j]) {
				min = j;
			}
		}
		if (min != i) {
			swap(arr[i], arr[min]);
		}
	}

插入排序

算法步骤

插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

详细资料:排序算法——直接插入排序(图文超详细!)-CSDN博客

时间复杂度

平均:O(n2),最好O(n),最坏O(n2)

空间复杂度

O(1)

#经典插入排序
void insert_sort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

希尔排序(插入排序的优化版本)

算法步骤

先选定一个整数,把待排序文件中所有记录分成多个组。所有距离为的记录分在同一组内,并对每一组内的记录进行直接插入排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

详细资料:七大排序算法——希尔排序,通俗易懂的思路讲解与图解(完整Java代码)-CSDN博客

时间复杂度

平均:O(n1.3),最好O(n),最坏O(n2)

空间复杂度

O(1)

#经典希尔排序
void shell_sort(int arr[], int n) {
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int key = arr[i];
            int j = i - gap;
            while (j >= 0 && arr[j] > key) {
                arr[j + gap] = arr[j];
                j -= gap;
            }
            arr[j + gap] = key;
        }
    }
}

堆排序

算法步骤

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端

2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1

3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

详细讲解:【从堆的定义到优先队列、堆排序】 10分钟看懂必考的数据结构——堆_哔哩哔哩_bilibili

时间复杂度

平均:O(nlogn),最好O(nlogn),最坏O(nlogn)

空间复杂度

O(1)

#构建最大堆,升序
void heapify(int arr[], int n, int i) {
    int largest = i;
    int l = 2 * i + 1;
    int r = 2 * i + 2;
    if (l < n && arr[l] > arr[largest]) largest = l;
    if (r < n && arr[r] > arr[largest]) largest = r;
    if (largest != i) {
        swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}
void heap_sort(int arr[], int n) {
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    for (int i = n - 1; i > 0; i--) {
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

计数排序

算法步骤:

  • 找出待排序的数组中最大和最小的元素
  • 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  • 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  • 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减1

局限:

  1. 当数列最大最小值差距过大时,并不适用于计数排序
  2. 当数列元素不是整数时,并不适用于计数排序

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)

详细参考:算法系列十一:十大经典排序算法之——计数排序-CSDN博客

时间复杂度

O (n+range)

空间复杂度

O(range)

#经典计数排序
void counting_sort(int arr[], int n) {
    int max = *max_element(arr, arr + n);
    int min = *min_element(arr, arr + n);
    int range = max - min + 1;
    int count[range] = {0};
    int output[n];
    for (int i = 0; i < n; i++) {
        count[arr[i] - min]++;
    }
    for (int i = 1; i < range; i++) {
        count[i] += count[i - 1];
    }
    for (int i = n - 1; i >= 0; i--) {
        output[count[arr[i] - min] - 1] = arr[i];
        count[arr[i] - min]--;
    }
    for (int i = 0; i < n; i++) {
        arr[i] = output[i];
    }
}

桶排序(计数排序的升级版)

算法步骤

参考:

1.9 桶排序 | 菜鸟教程 (runoob.com)

c++桶排序(刚学也能看懂)-CSDN博客

时间复杂度

最好:O(n+k),最坏:所有数据都集中在一个桶中,桶排序退化为桶内排序算法的时间复杂度,例如O(n^2)

空间复杂度

O(n+k)

#经典桶排序
void bucket_sort(int arr[], int n) {
    int max = *max_element(arr, arr + n);
    int min = *min_element(arr, arr + n);
    int range = max - min + 1;
    int bucketCnt = 10;
    vector<int> buckets[bucketCnt];
    
    // 计算桶的大小
    int bucketSize = 1;
    while (max) {
        max /= 10;
        bucketSize *= 10;
    }
    bucketSize /= 10;

    // 将元素分配到桶中
    for (int i = 0; i < n; i++) {
        int idx = arr[i] / bucketSize;
        buckets[idx].push_back(arr[i]);
        // 对每个桶进行插入排序
        for (int j = int(buckets[idx].size()) - 1; j > 0; j--) {
            if (buckets[idx][j] < buckets[idx][j - 1]) {
                std::swap(buckets[idx][j], buckets[idx][j - 1]);
            }
        }
    }

    // 将桶中的元素合并到原数组中
    for (int i = 0, k = 0; i < bucketCnt; i++) {
        for (int j = 0; j < int(buckets[i].size()); j++) {
            arr[k++] = buckets[i][j];
        }
    }
}

基数排序

算法步骤

参考:最详细的【基数排序】—排序算法,思路清晰动图保姆级讲解,五分钟搞懂!_csdn基数排序动图-CSDN博客

时间复杂度

O(K*n),k一般取10

空间复杂度

O(N+K)

#经典基数排序
void radix_sort(int arr[], int n) {
    int max = *max_element(arr, arr + n);
    int exp = 1;
    int output[n];
    while (max / exp > 0) {
        int count[10] = {0};
        for (int i = 0; i < n; i++) {
            count[arr[i] / exp % 10]++;
        }
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
            output[count[arr[i] / exp % 10] - 1] = arr[i];
            count[arr[i] / exp % 10]--;
        }
        for (int i = 0; i < n; i++) {
            arr[i] = output[i];
        }
        exp *= 10;
    }
}

vscode代码整合

在这里插入图片描述

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很好的问题!让我来解答。 首先,让我们来了解一下 `Comparable` 接口。这个接口是 Java 中的一个接口,它只有一个方法 `compareTo()`。这个方法的作用是定义类的自然排序顺序。 如果我们要对一个对象集合进行排序,只需要让这个类实现 `Comparable` 接口,并且重写 `compareTo()` 方法。这个方法返回一个整型值,表示该对象与另一个对象的大小关系。如果该对象小于另一个对象,则返回负整数;如果两个对象相等,则返回零;如果该对象大于另一个对象,则返回正整数。 下面是一个简单的例子,展示如何使用 `Comparable` 接口进行排序。 ```java public class Student implements Comparable<Student> { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } // 重写 compareTo 方法 @Override public int compareTo(Student o) { // 按照年龄从小到大排序 return this.age - o.getAge(); } } ``` 在这个例子中,我们定义了一个 `Student` 类,并且让它实现了 `Comparable` 接口。我们重写了 `compareTo()` 方法,按照年龄从小到大排序。 现在,我们可以使用 `Collections.sort()` 方法对 `Student` 对象进行排序了: ```java List<Student> students = new ArrayList<>(); students.add(new Student("Tom", 20)); students.add(new Student("Jerry", 18)); students.add(new Student("Alice", 22)); Collections.sort(students); for (Student student : students) { System.out.println(student.getName() + " " + student.getAge()); } ``` 输出结果为: ``` Jerry 18 Tom 20 Alice 22 ``` 可以看到,我们成功地按照年龄从小到大对 `Student` 对象进行了排序。 希望这个例子能够帮助你理解如何使用 `Comparable` 接口进行排序。如果你还有其他问题,请随时提出!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值