数据结构——(第八章:排序)

目录

一、排序总结

二、插入的排序

三、交换的排序

四、选择的排序

五、归并排序

六、基数排序

七、计数排序(空缺)

八、排序代码

1、插入的排序

2、交换的排序

3、选择的排序

4、归并排序

5、计数排序


一、排序总结

1、排序的稳定性:

  • 稳定:插、插、泡、归、基
  • 不稳定:快、选、希、堆

2、每趟至少确定一个元素最终位置的排序有

  • 选择排序、冒泡排序、堆排序、快排(确定枢轴)

3、顺序存储与链式存储:

  • 仅顺序存储:折半插入排序、希尔排序、快速排序、堆排序
  • 适合顺序存储和链式存储:直接插入排序、简单选择排序、归并排序、基数排序

4、排序的元素个数:

  • n较小时:直接插入排序(比较次数少)、简单选择排序(移动次数少)
  • n较大时:快速排序、堆排序、归并排序

5、选取排序需要考虑的因素:

  • 待排序元素个数n、元素初始状态、关键字的结构及其分布状态
  • 稳定性的要求、存储结构及辅助空间的大小限制等。

6、算法的稳定性与算法优劣无关。(稳定排序不一定优于不稳定)

|----------|---------------|---------------|---------------|------------|
| 排序 | 最好时间 | 平均时间 | 最坏时间 | 空间复杂度 |
| 插入排序 | O(n) | O(n*n) | O(n*n) | O(1) |
| 冒泡排序 | O(n) | O(n*n) | O(n*n) | O(1) |
| 选择排序 | O(n*n) | O(n*n) | O(n*n) | O(1) |
| 希尔排序 | O(n^1.3) ||| O(1) |
| 快速排序 | O(n*lgn) | O(n*lgn) | O(n*n) | O(lgn) |
| 堆排序 | O(n*lgn) | O(n*lgn) | O(n*lgn) | O(1) |
| 归并排序 | O(n*lgn) | O(n*lgn) | O(n*lgn) | O(n) |
| 基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(r) |

1、总比较次数与初始状态无关的是:

  • 选择排序、归并排序、基数排序(不基于比较和移动排序)
  • 折半插入排序的折半减少了比较次数,但这个折半与初始状态无关,仅取决于元素个数。

2、总移动次数与初始状态无关的是:

  • 归并排序、基数排序(不基于比较和移动排序)

3、排序趟数与初始状态有关的排序:(注:排序趟数和时间复杂度的区别)

  • 快速排序、冒泡排序**(直接插入排序无关,每趟插入一个元素,需要n-1趟)**

4、时间复杂度与初始状态无关的排序:

  • 选择排序、堆排序、归并排序、基数排序

二、插入的排序

1、插入排序的三种情况:

  • 直接插入排序、折半插入排序、希尔排序

2、直接插入排序:

  • **适应于基本有序****O(N)**或排序记录较小。
  • 适用于顺序存储和链式存储。
  • 局部有序,不能提高直接插入的时间性能

3、折半插入排序:

  • 减少了比较次数,但没有减少移动次数,平均仍为O(n^n).
  • 仅适用于顺序存储**,涉及二分故不能链式。**

**4、希尔排序:**缩小增量排序

  • 希尔排序开始时增量较大,每个子序列中的元素个数较少,从而排序速度快;
  • 当增量较小时,虽然每个子序列中的元素个数较多,但元素已经基本有序故排序速度也较快。

三、交换的排序

1、交换排序的两种情况:

  • 冒泡排序、快速排序

2、冒泡排序:

3、快速排序:分治思想

  • 快排运行效率:取决于划分过程的对称性。(若能尽可能均分,树尽可能是对称的,则速度最快)
  • 每次排序都可以将枢纽元素放入最终最终位置。
  • 当元素已经有序或逆序时速度最慢。****(单支数最慢)

4、快排可能的排序的结果

  • 若有头尾的最大或最小值相等,第二趟最少只需要2个相等的。
  • 若不包含头尾的最大值最小值,则第二趟最少需要3个相等的。
cpp 复制代码
#include <iostream>  
using namespace std;   

int partition(int a[], int left, int right) {  
    int pivot = a[left];  
    while (left < right) {  
        while (left < right && a[right] >= pivot) right--;  
        a[left] = a[right];  
        while (left < right && a[left] <= pivot) left++;  
        a[right] = a[left];  
    }  
    a[left] = pivot;  
    return left;  
}  
void quickSort(int a[], int left, int right) {  
    if (left < right) {  
        int pivotIndex = partition(a, left, right);  
        quickSort(a, left, pivotIndex - 1);  
        quickSort(a, pivotIndex + 1, right);  
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    quickSort(a, 0, n - 1);  
    for (int i = 0; i < n; i++) {  
        printf("%d ", a[i]);  
    }  
    printf("\n");  
    return 0;  
}

四、选择的排序

1、交换排序的两种情况:

  • 选择排序、堆排序

2、选择排序:

  • 每次遍历一趟,找到最大或最小值的下标,遍历完一趟最后再对下标进行交换。

3、堆排序:

  • 堆排序是完全二叉树,但不是二叉排序树。(左不一定比右大)
  • 建堆O(n),删除结点O(lgn),插入节点O(lgn)。

4、堆的插入和删除:

  • 插入(上浮):插入点依次与其根比较,一直往上走,不需要与其根的左孩子进行比较。(注:与删的区别)
  • 删除(下落):先交换,然后拿根与左右孩子比较,找小的那棵树,然后依次比较。(注:插和删除的区别)
  • 输出某一关键字后的剩余堆,需要的是重新排完序的一串数字。(最短路那个也一样)
cpp 复制代码
#include <iostream>  
using namespace std;  
void swap(int &a, int &b) {  
    int temp = a;  
    a = b;  
    b = temp;  
}  
void HeapAdjust(int a[], int n, int i) {//调整堆  
    int max = i;  
    int lchild = 2 * i + 1; // 左孩子  
    int rchild = 2 * i + 2; // 右孩子  
    if (lchild < n && a[lchild] > a[max])  max = lchild;  
    if (rchild < n && a[rchild] > a[max])  max = rchild;  
    if (max != i) {  
        swap(a[max], a[i]);  
        HeapAdjust(a, n, max);  
    }  
}  
void HeapSort(int a[], int n) {// 堆排序    
    for (int i = n / 2 - 1; i >= 0; i--) {  
        HeapAdjust(a, n, i);  
    }  
    for (int i = n - 1; i > 0; i--) {  
        swap(a[0], a[i]);  
        HeapAdjust(a, i, 0);  
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    HeapSort(a, n);  
    for (int i = 0; i < n; i++) 
        cout << a[i] << " ";   
    return 0;  
}

五、归并排序

1、归并排序:分治思想

  • 将两个或两个以上的有序表合并成一个新的有序表。
  • 每趟归并时间O(n),共需要lng趟。

六、基数排序

1、基数排序(计数排序):

  • 借助多关键字排序的思想对逻辑关键字进行排序的方法。
  • 不基于比较和移动,而仅仅基于关键字大小排序。
  • 当n很大时,记录的关键字位数较少且可以分解,必须都为整数。

2、两种基数排序方法:

  • 最低位优先(LSD):从最低有效位开始(个、十、百....),按顺序引入对应的数字队列中,连接成一个有序序列,以此类推。
  • 最高位优先(MSD):从最高数字位开始。

3、性能分析:

  • 辅助空间为需要的r个队列,即O(r)
  • 需要d趟分配和收集,一趟分需要O(n),一趟收集需要O(r),所以时间为O(d(n+r))

七、计数排序(空缺)

八、排序代码

1、插入的排序

cpp 复制代码
#include <iostream>  
using namespace std;  

void insertionSort(int a[], int n){
    int i, j, key;
    for (i = 1; i < n; i++){
        key = a[i];
        for (j = i - 1; j >= 0 && a[j] > key; j--){
            a[j + 1] = a[j];
        }
        a[j + 1] = key;
    }
}
int main(){
    int a[] = {12, 11, 13, 5, 6};
    int n = sizeof(a) / sizeof(a[0]);
    insertionSort(a, n);
    for (int i = 0; i < n; i++)
        printf("%d ", a[i]);
    return 0;
}

折半插入

cpp 复制代码
#include <iostream>  
using namespace std;  

void InsertSort(int a[], int n) {  
    int i, left, right, mid, j;  
    for (i = 2; i <= n; i++) {  
        a[0] = a[i];  
        left = 1;  
        right = i - 1;  
        while (left <= right){  
            mid = (left + right) / 2;  
            if (a[mid] > a[0]) right = mid - 1;  
            else left = mid + 1;  
        }  
        for (j = i - 1; j >= left; j--) {  
            a[j + 1] = a[j];  
        }  
        a[left] = a[0];  
    }  
}  
int main() {  
    int a[] = {4, 3, 2, 10, 12, 1, 5, 6};  
    int n = sizeof(a) / sizeof(a[0]);  
    InsertSort(a, n);  
    for (int i = 1; i <= n; i++) {  
        printf("%d ", a[i]);  
    }  
    printf("\n");  
    return 0;  
}

希尔

cpp 复制代码
#include <iostream>  
using namespace std;  

void shell_sort(int a[], int n) {  
    int i, j, temp, d;  
    for (d = n / 2; d >= 1; d /= 2) {  
        for (i = d; i < n; i++) {  
            temp = a[i];  
            // 直接插入排序  
            for (j = i - d; j >= 0 && a[j] > temp; j -= d) {  
                a[j + d] = a[j];  
            }  
            a[j + d] = temp;  
        }  
    }  
}  
int main() {  
    int a[] = {12, 34, 54, 2, 3};  
    int n = sizeof(a) / sizeof(a[0]);  
    shell_sort(a, n);  
    for (int i = 0; i < n; i++) {  
        printf("%d ", a[i]);  
    }  
    printf("\n");  
    return 0;  
}

2、交换的排序

冒泡

cpp 复制代码
#include <iostream>  
using namespace std;  

void bubbleSort(int a[], int n) {  
    int i, j, temp;  
    for (i = 0; i < n-1; i++) {  
        int flag = 0;  
        for (j = 0; j < n-1-i; j++) {  
            if (a[j] > a[j+1]) {  
                temp = a[j];  
                a[j] = a[j+1];  
                a[j+1] = temp;  
                flag = 1;  
            }  
        }  
        if (flag == 0) return;  
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    bubbleSort(a, n);  
    for (int i = 0; i < n; i++) {  
        printf("%d ", a[i]);  
    }  
    printf("\n");  
    return 0;  
}

快排

cpp 复制代码
#include <iostream>  
using namespace std;   

int partition(int a[], int left, int right) {  
    int pivot = a[left];  
    while (left < right) {  
        while (left < right && a[right] >= pivot) right--;  
        a[left] = a[right];  
        while (left < right && a[left] <= pivot) left++;  
        a[right] = a[left];  
    }  
    a[left] = pivot;  
    return left;  
}  
void quickSort(int a[], int left, int right) {  
    if (left < right) {  
        int pivotIndex = partition(a, left, right);  
        quickSort(a, left, pivotIndex - 1);  
        quickSort(a, pivotIndex + 1, right);  
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    quickSort(a, 0, n - 1);  
    for (int i = 0; i < n; i++) {  
        printf("%d ", a[i]);  
    }  
    printf("\n");  
    return 0;  
}

3、选择的排序

选择

cpp 复制代码
#include <iostream>  
using namespace std;  
 
void swap1(int &a, int &b) {// 通过引用交换两个整数   
    int temp = a;  
    a = b;  
    b = temp;  
}  
void swap2(int *a, int *b) {// 通过指针交换两个整数  
    int temp = *a;  
    *a = *b;  
    *b = temp;  
}
void selectSort(int a[], int n) {  
    int i, j, k;  
    for (i = 0; i < n; i++) {  
        k = i;  
        for (j = i + 1; j < n; j++) { // 找当前最小值的下标  
            if (a[j] < a[k]) {  
                k = j;  
            }  
        }  
        swap1(a[k],a[i]);   //swap2(a[k],a[i]);
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    selectSort(a, n);  
    for (int i = 0; i < n; i++) {  
        printf("%d ", a[i]);  
    }  
    printf("\n");  
    return 0;  
}

7、堆排序

cpp 复制代码
#include <iostream>  
using namespace std;  
void swap(int &a, int &b) {  
    int temp = a;  
    a = b;  
    b = temp;  
}  
void HeapAdjust(int a[], int n, int i) {//调整堆  
    int max = i;  
    int lchild = 2 * i + 1; // 左孩子  
    int rchild = 2 * i + 2; // 右孩子  
    if (lchild < n && a[lchild] > a[max])  max = lchild;  
    if (rchild < n && a[rchild] > a[max])  max = rchild;  
    if (max != i) {  
        swap(a[max], a[i]);  
        HeapAdjust(a, n, max);  
    }  
}  
void HeapSort(int a[], int n) {// 堆排序    
    for (int i = n / 2 - 1; i >= 0; i--) {  
        HeapAdjust(a, n, i);  
    }  
    for (int i = n - 1; i > 0; i--) {  
        swap(a[0], a[i]);  
        HeapAdjust(a, i, 0);  
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    HeapSort(a, n);  
    for (int i = 0; i < n; i++) 
        cout << a[i] << " ";   
    return 0;  
}

4、归并排序

cpp 复制代码
#include <iostream>  
using namespace std;  
  
void merge(int a[], int temp[], int left, int mid, int right) {  
    int i = left;  
    int j = mid + 1;  
    int k = 0;  
    while (i <= mid && j <= right) {  
        if (a[i] <= a[j])  temp[k++] = a[i++];  
        else   temp[k++] = a[j++];  
    }  
    while (i <= mid)    temp[k++] = a[i++];  
    while (j <= right)   temp[k++] = a[j++];  
    for (int p = 0; p < k; p++) 
        a[left + p] = temp[p];  
}  
void mergeSort(int a[], int temp[], int left, int right) {  
    if (left < right) {  
        int mid = (left + right) / 2;  
        mergeSort(a, temp, left, mid);  
        mergeSort(a, temp, mid + 1, right);  
        merge(a, temp, left, mid, right);  
    }  
}  
int main() {  
    int a[] = {64, 34, 25, 12, 22, 11, 90};  
    int n = sizeof(a) / sizeof(a[0]);  
    int* temp = new int[n];  
    mergeSort(a, temp, 0, n - 1);  
    for (int i = 0; i < n; i++) {  
        cout << a[i] << " ";  
    }  
    delete[] temp;  
    return 0;  
}

5、计数排序

相关推荐
tinker在coding29 分钟前
Coding Caprice - Linked-List 1
算法·leetcode
XH华5 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生5 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_5 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子6 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡6 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin6 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码6 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7246 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活7 小时前
理解支持向量机
算法·机器学习·支持向量机