算法(内排序)

1. 插入排序

插入排序分为直接插入排序,二分插入排序,希尔排序
直接插入排序在最好情况下时间复杂度为O(1),最坏情况为O(n2),稳定
二分插入排序在最好情况下时间复杂度为O(1),最坏情况为O(n2),稳定
希尔排序在最好情况下时间复杂度为O(1),最坏情况为O(n1.3),不稳定

1. 直接插入排序

思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止。
关键问题:在前面已经排好序的序列中找到合适的插入位置。
在这里插入图片描述

#include <stdio.h>

void Print(int* arr, int n)
{
    
    
    for (int i = 0; i < n; i++)
    {
    
    
        printf("%d ", arr[i]);
    }
}


void InsertSort(int *num, int len)
{
    
    
    int i, j, temp;
    for (i = 1; i < len; i
    ++)
    {
    
    
        // num[i]之前的序列是有序的,num[i - 1]为有序部分的最大值
        // 故只需要与有序部分的最大值进行比较,即可判断是否需要插入
        if (num[i - 1] > num[i])
        {
    
    
            // 获取需要插入的数据
            temp = num[i];
            // 依次后移,查找插入位置
            for ( j = i - 1; j >= 0 && num[j] > temp; j--)
            {
    
    
                num[j + 1] = num[j];
            }
            // 完成插入
            num[j + 1] = temp;
        }
    }
}


void test()
{
    
    
    int arr[] = {
    
     12,18,9,20,13,4};
    int n = sizeof(arr) / sizeof(arr[0]);
    InsertSort(arr, n);
    printf("排序后的数组:\n");
    Print(arr,n);
}



int main()
{
    
    
    test();
    return 0;
}

2. 二分插入排序

二分法插入排序,简称二分排序,是在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,如果小,则对前半再进行折半,否则对后半进行折半,直到low<high,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。

#include <stdio.h>
#define N 10

//二分插入排序 升序
void biInsertSort(int R[], int n)
{
    
    
    int mid, low, high, temp;

    for(int i = 0; i < n; i++)
    {
    
    
        low = 0;
        high = i - 1;
        temp = R[i];

        while(low <= high)
        {
    
    
            mid = (low + high) / 2;
            if (temp < R[mid])
                high = mid - 1;
            else
                low = mid + 1;
        }

        //将0~high范围的元素右移
        for(int j = i-1; j > high; j--)
            R[j+1] = R[j];

        R[high+1] = temp;
    }
}


void main()
{
    
    
    int array[N] = {
    
    30, 40, 60, 10, 20, 50, 70, 80, 90, 100};

    printf("排序前:\n");
    for(int i = 0; i < N; i++)
        printf("%d ", array[i]);
    printf("\n");

    biInsertSort(array, N);

    printf("排序后:\n");
    for(int i = 0; i < N; i++)
        printf("%d ", array[i]);
    printf("\n");
}

3. 希尔排序


#include<stdio.h>

// 实现希尔排序
void ShellSort(int arr[], int len)
{
    
    
    int tmp = 0;
    //gap为步长,每次减为原来的一半
    for(int gap=len/2;gap>0;gap/=2)
    {
    
    
        //共gap个组,对每一组都执行直接插入排序
        for(int i=0;i<gap;i++)
        {
    
    
            //组内的直接插入排序
            for(int j=i+gap;j<len;j+=gap)
            {
    
    
                // 如果当前元素大于加上步长后的那个元素,说明交换
                if(arr[j]<arr[j-gap])
                {
    
    
                    tmp=arr[j];
                    int k=j-gap;
                    // 将大于arr[j]的值整体后移
                    while(k>=0 && arr[k]>tmp)
                    {
    
    
                        arr[k+gap]=arr[k];
                        k-=gap;
                    }
                    arr[k+gap]=tmp;
                }
            }
        }
    }
}

// 打印函数
void show(int a[], int n)
{
    
    
    for (int i=0; i<n; i++)
    {
    
    
        printf("%d ",a[i]);
    }
    putchar('\n');
}

int main()
{
    
    
    int a[] = {
    
    3, 1, 5, 7, 2, 4, 9, 6, 10, 8};
    int n = sizeof(a)/sizeof(int);
    printf("排序前:");
    show(a, n);
    ShellSort(a, n);
    printf("排序后:");
    show(a, n);
    return 0;
}

2. 交换排序

1. 冒泡排序

每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。
而 “每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。

#include<stdio.h>

void Print(int* arr, int n)
{
    
    
    for (int i = 0; i < n; i++)
    {
    
    
        printf("%d ", arr[i]);
    }
}

void BubbleSort(int* arr, int n){
    
    
    for (int i = 0; i < n-1; i++)
    {
    
    
        for(int j = 0; j < n-i-1; j++){
    
    
            if(arr[j]>arr[j+1]){
    
    
                int temp = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

void test()
{
    
    
    int arr[] = {
    
     12,18,9,20,13,4};
    int n = sizeof(arr) / sizeof(arr[0]);
    BubbleSort(arr, n);
    Print(arr, n);
}

int main(){
    
    
    test();
    return 0;
}

2. 快速排序

我可以提供一个简单的快速排序算法,它是一种分治算法
它的基本思想是:首先从数列中取出一个元素,作为基准值;然后将比这个基准值小的放到它的左边,将比它大的放到它的右边;最后对左右两个子序列重复上述步骤,直到序列有序。


#include <stdio.h>

void quicksort(int *arr, int begin, int end)
{
    
    
    if (begin < end)
    {
    
    
        int key = arr[begin];
        int i = begin;
        int j = end;
        while (i < j)
        {
    
    
            while (arr[j] > key && j > i){
    
    
                j--;
            }
            arr[i] = arr[j];
            while (arr[i] < key && i < j){
    
    
                i++;
            }
            arr[j] = arr[i];
        }

        arr[i] = key;  // 此时,i=j
        quicksort(arr, begin, i-1);
        quicksort(arr, j+1, end);
    }
}

void Print(int* arr, int n)
{
    
    
    for (int i = 0; i < n; i++)
    {
    
    
        printf("%d ", arr[i]);
    }
}


void test()
{
    
    
    int arr[] = {
    
     12,18,9,20,13,4};
    int n = sizeof(arr) / sizeof(arr[0]);
    quicksort(arr, 0, n-1);
    Print(arr, n);
}

int main (void)
{
    
    
    test();
    return 0;
}

3.选择排序

选择排序分为直接选择排序与树形选择排序(堆排序)
直接选择排序在最好情况下时间复杂度为O(1),最坏情况为O(n2)
堆排序在最好情况下时间复杂度为O(logn),最坏情况为 O(logn2)
选择排序都是不稳定的

1. 直接选择排序

排序过程:
(1)将整个记录序列划分为有序区和无序区,初始时有序区为空,无序区含有待排序的所有记录;
(2)在无序区查找值最小的记录,将它与无序区的第一个记录交换,使得有序区扩展一个记录,同时无序区减少一个记录。
(3)不断重复步骤(2),直到无序区只剩下一个记录为止。

在这里插入图片描述

#include<stdio.h>
void Select(int* arr,int n){
    
    
    for(int i=0;i<=n-1;i++)  //进行n-1趟排序
    {
    
    
        int min=i;           //默认第一个元素为最小值
        //找出最小值下标
        for(int j=i+1;j<=n-1;j++)
        {
    
    
            if(arr[j]<arr[min])
            {
    
    
                min=j;
            }
        }

        if(min!=i)
        {
    
    
            int temp=arr[i];
            arr[i]=arr[min];
            arr[min]=temp;
        }

    }

}
void Print(int* arr, int n)
{
    
    
    for (int i = 0; i < n; i++)
    {
    
    
        printf("%d ", arr[i]);
    }
}

void test()
{
    
    
    int arr[] = {
    
     12,18,9,20,13,4};
    int n = sizeof(arr) / sizeof(arr[0]);
    Select(arr, n);
    Print(arr, n);
}



int main(){
    
    
    test();
    return 0;
}

2. 堆排序

用大根堆,每次挑选最大记录归位。挑选最大记录采用的是筛选法。
筛选法:将一个无序序列调整为堆的过程。
堆排序使用了减治法的思想。
排序过程:
(1)将记录序列用筛选法调整为大根堆;
(2)将堆顶和堆中最后一个记录交换;
(3)不断重复步骤(1)(2),直到堆中只有一个记录为止。


#include <stdio.h>
void heapify(int arr[], int n, int i)
{
    
        int largest = i; // 假设根节点最大
     int l = 2 * i + 1; // 左子节点
     int r = 2 * i + 2; // 右子节点
     // 如果左子节点比根节点大,则将largest赋值为左子节点的下标
     if (l < n && arr[l] > arr[largest])
     {
    
    
         largest = l;
     }
    // 如果右子节点比当前最大值还要大,则将largest赋值为右子节点的下标
    if (r < n && arr[r] > arr[largest])
    {
    
    
        largest = r;
    }
    // 如果最大值的位置已经不是当前根节点的位置,则交换它们的值
    if (largest != i)
     {
    
    
        int temp = arr[i];
        arr[i] = arr[largest];
        arr[largest] = temp;
        // 继续递归调用堆化,直到所有子树都符合堆的要求
        heapify(arr, n, largest);
     }
}
void heapSort(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--)
     {
    
    
         int temp = arr[0];
         arr[0] = arr[i];
         arr[i] = temp;
         // 每取出一个元素,就对堆进行一次堆化,保持堆的性质
         heapify(arr, i, 0);
     }
}


void Print(int* arr, int n)
{
    
    
    for (int i = 0; i < n; i++) {
    
    
        printf("%d ", arr[i]);
    }
}

void test()
{
    
    
    int arr[] = {
    
     12,18,9,20,13,4};
    int n = sizeof(arr) / sizeof(arr[0]);
    heapSort(arr, n);
    printf("排序后的数组:\n");
    Print(arr,n);
}

int main()
{
    
    
   test();

   return 0;
}

4.归并排序

1. 二路归并排序

归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

#include <stdio.h>
#include <stdlib.h>

void merge(int a[], int low, int mid, int high, int b[])//将a[low:mid]和a[mid+1:high]归并到b[low:high]
{
    
    
    int i = low;
    int j = mid + 1;
    int k = low;

    while(i <= mid && j <= high)
    {
    
    
        if (a[i] <= a[j])
        {
    
    
            b[k++] = a[i++];
        }
        else
        {
    
    
            b[k++] = a[j++];
        }
    }

    while(i <= mid)
    {
    
    
        b[k++] = a[i++];
    }

    while(j <= high)
    {
    
    
        b[k++] = a[j++];
    }
}

void mergesort(int a[], int length, int b[])
{
    
    
    int *temp, step, i;
    temp = (int *)malloc(sizeof(int) * length);

    if (NULL == temp)
    {
    
    
        exit(-1);
    }

    for (step = 1; step < length; step <<= 1)
    {
    
    
        for (i = 0; i <= length - step; i += (step << 1))
        {
    
    
            merge(a, i, i + step - 1, i + (step << 1) - 1 < length - 1 ? i + (step << 1) - 1 : length - 1, temp);
        }

        for (int j = 0; j < length; j++)
        {
    
    
            a[j] = temp[j];
            b[j] = a[j];
        }
    }

    free(temp);
}

int main()
{
    
    
    int a[8] = {
    
    7, 1, 4, 9, 6, 3, 5, 0};
    int b[8] = {
    
    0};

    mergesort(a, 8, b);

    int i;
    for(i = 0; i < 8; i++)
    {
    
    
        printf("%d\t", b[i]);
    }
    printf("\n");

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_55671900/article/details/129108097
今日推荐