插入排序、直接选择(简单)排序、冒泡排序、希尔排序、快速排序

参考:

  1. bilibili:直接选择排序、冒泡排序、希尔排序、快速排序手推步骤
  2. bilibili:五分钟学会希尔排序【中英字幕】
  3. bilibili:插入排序(直接插入、希尔排序)算法思想

插入排序

插入排序就是在之前排好的有序表中找到合适位置插入。

例如一个乱序的数组:
{51,28,38,86,70,90,7,30,40,86,25}

  1. 第一个是51,此时还没有有序表,51作为有序表中的第一个。
  2. 28小于51,排在51前边{28,51}
  3. 38大于28小于51,排在28和51中间。{28,38,51}
  4. 86大于51,排在最后{28,38,51,86}

    直到排序完成。

代码

1234567891011121314151617181920212223
#include <stdio.h>void Sort(int arr[],int sz);int main(){    int arr[] = {51,28,28,86,-70,90,7,28,40,86,25};    int sz = sizeof(arr)/sizeof(arr[0]);    Sort(arr,sz);    return 0;}void Sort(int arr[],int sz){    int i;    for (int i = 1; i < sz; i++) {           int key = arr[i];           int j = i - 1;           // 找到 key 的插入位置           while (j >= 0 && arr[j] > key) {               arr[j + 1] = arr[j];               j--;           }           arr[j + 1] = key; // 插入 key       }}

直接选择(简单)排序

例如一个数组:
{51,28,38,86,70,90,7,30,40,86,25}
先找到这个数组中最小的数字,也就是7,把它和第一个数字,51交换位置,数组变成了:
{(7),28,38,86,70,90,51,30,40,86,25}
再从这个数组的第二个数字开始寻找最小的数字,也就是25,和第二个数字28交换位置:
{(7,25),38,86,70,90,51,30,40,86,28}

直到排序完成。

代码

123456789101112131415161718192021222324252627282930313233
#include <stdio.h>void Sort(int arr[],int sz);int main(){    int arr[] = {51,28,28,86,-70,90,7,28,40,86,25};    int sz = sizeof(arr)/sizeof(arr[0]);    Sort(arr,sz);    return 0;}void Sort(int arr[],int sz){    int i = 0;    int j = 0;    for (j=0; j<sz; j++)    {        int minValue = arr[j];        int minIndex = j;        for (i=j+1; i<sz; i++)//找到最小的数字        {            if (arr[i] < minValue)            {                minValue = arr[i];//更新最小的数字                minIndex = i;//最小数字的位置            }        }        if (minIndex != j)//只在必要时交换元素        {            int temp = arr[j];            arr[j] = minValue;            arr[minIndex] = temp;        }    }}

冒泡排序

假设一个数组:
{51,28,38,86,70,90,7,30,40,86,25}
51先与28进行比较,51>28,数组变成
{28,51,38,86,70,90,7,30,40,86,25}
51再与38比较,51>38,数组变成
{28,38,51,86,70,90,7,30,40,86,25}

直至第一轮排序完成,数组变为:
{28,38,51,70,86,7,30,40,86,25,(90)}
可以看到,经过了第一轮排序,把最大的数字移到数组的末尾,之后不会再对这个数字进行排序。

再次进行第二轮冒泡排序:
{28,38,51,70,86,7,30,40,86,25,(90)}
28与38比较,28<38,数组不变。
38再与51比较,38<51,数组不变。

86与7比较,86>7,数组改为:
{28,38,51,70,7,86,30,40,86,25,(90)}
86与30比较,数组变成:
{28,38,51,70,7,30,86,40,86,25,(90)}
86与40比较,数组变成:
{28,38,51,70,7,30,40,86,86,25,(90)}
下次比较时,86与86相等,顺序不变,然后86再与25比较:
{28,38,51,70,7,30,40,86,25,86,(90)}
经过第二轮冒泡排序,数组变成:
{28,38,51,70,7,30,40,86,25,(86,90)}
下次再排序时,后两位不会再修改。

直到排序完毕。

代码

12345678910111213141516171819202122232425262728293031323334
#include <stdio.h>void Sort(int arr[],int sz);int main(){    int arr[] = {51,28,28,86,-70,90,7,28,40,86,25};    int sz = sizeof(arr)/sizeof(arr[0]);    Sort(arr,sz);    return 0;}void Sort(int arr[],int sz){    int i = 0;    int j = 0;    int swapped;//发生交换标记    for (j=0; j<sz-1; j++)//趟数    {        swapped = 0; // 标记这一趟是否有交换        for (i=0; i<sz-j-1; i++)//一趟冒泡排序        {            if (arr[i] > arr[i+1])            {                int temp = arr[i];                arr[i] = arr[i+1];                arr[i+1] = temp;                swapped = 1; // 有交换发生            }        }        // 如果没有交换,提前结束        if (!swapped)        {            break;        }    }}

希尔排序

希尔排序首先需要一个步长,这个步长不是固定的,根据实际情况调整。
在这里我们取步长为d=(5,3,1)。
假设一个数组:
{51,28,38,86,70,90,7,30,40,86,25}
步长为5,即51和90为一组,28和7为一组,38和30为一组,86和40为一组,70和86为一组,25单独为一组,本次不对其排序。
进行第一趟排序,不同组进行分别比较:

  1. 51<90,这组顺序不改变
  2. 28>7,把28和7调换位置
  3. 38>30,调换位置
  4. 86>40,调换位置
  5. 70<86,不变
  6. 25单独一组,不变
    进行第一趟排序后,数组变为:
    {51,7,30,40,70,90,28,38,86,86,25}

再以步长为3,在第一次排序后的基础上,进行第二趟希尔排序:
{51,7,30,40,70,90,28,38,86,86,25}
51和40为一组,7和70为一组,30和90为一组,28和86为一组,38和25为一组,倒数第三个数86单独一组。

  1. 51>40,调换位置
  2. 7<70,不变
  3. 30<90,不变
  4. 28<86,不变
  5. 38>25,调换位置
  6. 86单独一组,位置不变
    按照上面进行第二趟希尔排序:
    {40,7,30,51,70,90,28,25,86,86,38}

再以步长为1,进行第三趟希尔排序:

  1. 40先和7比较,调换位置
    {7,40,30,51,70,90,28,25,86,86,38}
  2. 40和30比较,调换位置
    {7,30,40,51,70,90,28,25,86,86,38}
  3. 40和51比较,不变
  4. 51和70比较,不变
  5. 70和90比较,不变,90和28比较,调换位置

代码

12345678910111213141516171819202122232425
#include <stdio.h>void Sort(int arr[],int sz);int main(){    int arr[] = {51,28,28,86,-70,90,7,28,40,86,25};    int sz = sizeof(arr)/sizeof(arr[0]);    Sort(arr,sz);    return 0;}void Sort(int arr[],int sz){    int gap,i,j,temp;    for (gap = sz / 2; gap > 0; gap = gap / 2)//先定义步长    {        for (i = gap; i < sz; i++)//元素间移动位置        {            for (j = i - gap; j>=0 && arr[j]>arr[j+gap]; j = j - gap)//比较各相距离gap个位置的元素            {                temp = arr[j];//更换位置                arr[j] = arr[j+gap];                arr[j+gap] = temp;            }        }    }}

快速排序

先选出一个节点(取有意义的,或者取第一个),把这个节点放在一个数组的中间,把要排序的数字中小于它的放在它的左侧,右侧都是大于它的数字,最后对两边的数字进行排序。
假设一个数组
{51,28,38,86,70,90,7,30,40,86,25}
选取数组中第一个数字作为节点P,最左边为L指针,最右边为R指针。

512838867090730408625
⬆️P
⬆️L⬆️R

P节点在最左边,要先从最右边的R指针开始比较,由于25<51,把25放在左边。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
25

接下来L指针右移,由于28<51,把28放在左边,L指针右移。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
2528

L指针指向的38<51,把38放在左边,L指针右移。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
252838

L指针指向的86>51,86去R指针所在位置,接下来左移R指针。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
25283886

R指针指向的86>51,86放在右边,R指针左移。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
2528388686

R指针指向的40<51,40移动到L指针所在的位置,接下来右移L指针。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
252838408686

L指针指向的70>51,70移动到R指针所在的位置,接来下左移R指针。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
25283840708686

R指针指向的30<51,30移动到L指针所在的位置,接下来右移L指针。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
2528384030708686

L指针指向的90>51,90移动到R指针所在位置,接下来左移R指针。⬇️

512838867090730408625
⬆️P
⬆️L⬆️R
252838403090708686

R指针指向的7<51,7移动到L指针位置,接下来右移L指针,L和R指针汇合,把P指针(51)移动到这里。⬇️

512838867090730408625
⬆️R⬆️L
252838403075190708686
⬆️P

接下来把P指针左侧和右侧作为两个单独的数组,再次排序,直到数组有序。

代码

1234567891011121314151617181920212223242526272829303132333435363738394041
#include <stdio.h>void swap(int* a, int* b) {    int temp = *a;    *a = *b;    *b = temp;}int partition(int arr[], int low, int high) {    int pivot = arr[high]; // 选择最后一个元素作为基准    int i = (low - 1); // 小于基准的元素索引    for (int j = low; j < high; j++) {        if (arr[j] < pivot) {            i++;            swap(&arr[i], &arr[j]); // 交换        }    }    swap(&arr[i + 1], &arr[high]); // 将基准放到正确位置    return (i + 1);}void quickSort(int arr[], int low, int high) {    if (low < high) {        int pi = partition(arr, low, high); // 找到基准元素的索引        quickSort(arr, low, pi - 1);  // 对基准左侧进行排序        quickSort(arr, pi + 1, high); // 对基准右侧进行排序    }}void printArray(int arr[], int n) {    for (int i = 0; i < n; i++) {        printf("%d ", arr[i]);    }    printf("\n");}int main() {    int arr[] = {51, 28, 38, 86, 70, 90, 7, 30, 40, 86, 25};    int n = sizeof(arr) / sizeof(arr[0]);    printf("原数组: ");    printArray(arr, n);    quickSort(arr, 0, n - 1);    printf("排序后数组: ");    printArray(arr, n);    return 0;}
🔍 ×