利用链表和数组实现5大排序算法(详细篇)

一、概念

        将一组无序的数据元素按照特定的规则(如逻辑顺序、字母顺序等)重新排列为有序序列。

1.稳定性:

        在排序过程中,对于序列中两个相等的元素,如果排序前它们的相对位置为a在b之前,排序后a仍然在b之前,则称该排序算法是稳定的。否则,如果排序前后它们的相对位置为a在b之前,则称该排序算法是不稳定的。

2.内排序和外排序:

  • 内部排序:数据在内存中进行排序
  • 外部排序:因排序数据量太大,内存不能一次性容纳全部数据,在排序过程中需要访问外存。

二、内排序算法

1.选择排序算法

步骤:

  1. 初始化:将列表分为有序部分和无序部分。最开始整个序列都是无序部分。
  2. 查找元素:在无序部分中查找最小大的元素。
  3. 交换位置:将找到的最小大元素与未排序部分的第一个元素交换位置。
  4. 更新范围:将未排序部分的起始位置向后移动一位,扩大已排序部分的范围。
  5. 重复步骤:重复上述步骤,直到未排序部分为空,列表完全有序。

动画演示:

数组实现:

#include <stdio.h>
void select_Sort(int arr[],int len);
int main()
{
    int arr[10]={5,1,8,3,9,11,34,90,24,6};
    select_Sort(arr,10);
    return 0;
}

void select_Sort(int arr[],int len)
{
    int min,temp;
    for (int i = 0; i < len; i++)
    {
        min=i;  //第一个元素作为有序序列
        for (int j = i; j < len; j++)  //从无序序列开始
        {
            if(arr[min]>arr[j])     //找到比min更小的元素
            {
                min=j;
            }
        }
        //进行交换
        temp=arr[i];     
        arr[i]=arr[min];
        arr[min]=temp;
        
    }
    for (int i = 0; i < len; i++)
    {
        printf(" %d ",arr[i]);
    }
    printf(" \n");
    
}

链表实现:

//选择排序
void select_sort(PNode head)
{
    if(list_empty(head))
        return;

    PNode min,start , node;
    start = head->next;

    while (start->next != head)
    {
        //寻找无序部分中的最小节点
        min = start;
        node = start->next;
        while (node != head)
        {
            if(node->data < min->data)
                min = node;
            node = node->next;
        }
        
        //将最小节点移动到无序部分开头前面
        if(min != start)
            mv_node(min , start->prev);
        else
            start = start->next;   
    }
}

2.插入排序

步骤:

        1.初始化:将整个序列分成有序和无序两部分,最开始只有第一个元素作为有序部分,其它的都是无序部分。

        2.插入:将无序部分的第一个元素start插入到有序部分。

                1)从右到左找到第一个比start小的元素min。

                2)将start插入到min的后面。

        3.重复步骤:循环执行步骤2,直到将无序部分的所有元素都插入到有序部分中。

动画演示:

数组实现:

#include <stdio.h>
void insert_Sort(int arr[],int len);
int main()
{
    int arr[10]={5,1,8,3,9,11,34,90,24,6};
    insert_Sort(arr,10);
    return 0;
}

void insert_Sort(int arr[],int len)
{
    int start,j;
    for (int i = 1; i < len; i++)
    {
        start=arr[i];  //作为有序序列的第一个元素
        for ( j = i-1; j >=0; j--)  //从右往左找到比start小的元素min(arr[j])
        {
            if(start>arr[j])
            break;
            arr[j+1]=arr[j]; //没有找到就不插入,直接往后继续
        }
        arr[j+1]=start;//讲=将start插入到min后面
    }
    for (int i = 0; i < len; i++)
    {
        printf(" %d ",arr[i]);
    }
    printf(" \n");
    
}

链表实现:

//插入排序
void insert_Sort(PNode head)
{
    if(list_empty(head) )
        return;
    
    PNode start = head->next->next;
    PNode dest , next;
    while (start != head)
    {
        next = start->next;
        //找到第一个比start大的节点
        dest = head;
        while (dest != start)
        {
            if(dest->next->data > start->data)
                break;
            dest = dest->next;
        }
        if(dest != start)
            mv_node(start , dest);
        
        start = next;
    }
}

3.冒泡排序

步骤:

  • 顺序:两个数据的位置符合排序需要
  • 逆序:两个数据的位置不符合排序需要

        遍历所有元素,让相邻两个元素比较,如果逆序,就交换两个元素的位置,否则就不动。经过一轮比较,“极值”就会被放到最后面。

动画演示:

数组实现:

#include <stdio.h>
// 1. 定义交换宏(必须)
#define SWAP(a, b) { int temp = a; a = b; b = temp; }
void bubble_Sort(int arr[], int len);
int main()
{
    int arr[]={5,1,8,3,9,11,34,90,24,6};
    int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    bubble_Sort(arr,len);
    printf(" 排序后的数组:\n");
    for (int i = 0; i < len; i++)
    {
        printf(" %d ",arr[i]);
    }
    printf(" \n");
    return 0;
}
//冒泡排序
void bubble_Sort(int arr[] , int len)
{
    for (int i = 0; i < len; i++)   //完成一轮排序
    {
        for(int j=0 ; j<len-1-i; j++)
        {
            if(arr[j] > arr[j+1])   //逆序
            {
                SWAP(arr[j] , arr[j+1]) //交换两个逆序的元素
            }
        }
    }
}

链表实现:

//冒泡排序
void bubble_Sort(PNode head)
{
    if(list_empty(head) )
        return;
    
    int len =0;
    bool change;
    PNode node = head->next;
    while (node != head){
        len++;
        node = node->next;
    }
    
    for (int i = 0; i < len; i++){
        change =false;
        node = head->next;
        for(int j=0 ; j<len-i-1 ; j++){
            if(node->data > node->next->data){
                change = true;
                mv_node(node , node->next);
            }
            else
                node = node->next;
        }
        if(!change)
            break;
    }
}

4.希尔排序

        是插入排序的优化。

步骤:

  1. 选择一个不序列长度len小的数字gap作为间距(步长、增量)
  2. 以gap进行分组
  3. 在各小组内部进行插入排序
  4. 再将gap减小,重新分组,并在各小组内部进行插入排序
  5. 直到gap小于等于1为止

动画演示

数组实现:

#include <stdio.h>
void shell_insert(int arr[],int count,int gap); //插入
void shell_Sort(int arr[] , int len);
int main()
{
    int arr[10]={5,1,8,3,9,11,34,90,24,6};
    shell_Sort(arr,10);
    return 0;
}
/*
对分组内部进行插入排序
arr:小组的起点
count:小组长度
gap:步长
*/
void shell_insert(int arr[],int count,int gap)
{
     if(count<=1)
        return;
    
    int start ,j;
    for(int i = gap ; i< count * gap ; i+= gap)
    {
        start = arr[i];
        for(j = i-gap ;j>=0 ; j-=gap)
        {
            if(arr[j] < start)
                break;
            arr[j+gap] = arr[j];
        }
        arr[j+gap] = start;
    }
}

//希尔排序
void shell_Sort(int arr[] , int len)
{
    //分组
    for (int gap = len/2; gap  > 0 ; gap/=2)
    {
        for (int i = 0; i < gap; i++)   //对每个小组进行排序
        {
            shell_insert(arr+i , len/gap , gap);
        }
    }
    for (int i = 0; i < len; i++)
    {
        printf(" %d ",arr[i]);
    }
    printf(" \n");
}

5.快速排序

步骤:

  1. 从序列中挑选一个元素,作为基准key,一般是最左边(升序)或最右边(降序)的元素

  2. 进行一轮排序

        i·定义一个left和right,left从左往右走,right从右往左走

        ii.走的过程中,如right遇到比key小的元素就停下来;若left遇到比key大的元素就停下来

        ii交换left和right所在位置的元素,交换后,继续相向而行

        iv.直到left和right相遇,交换key和相遇点的值

  1. 以key分成左右两部分,左边的都是比key小的,右边的都是比key大的

  2. 递归地对左右两部分分别进行快速排序

动画演示:

数组实现:

#include <stdio.h>
// 1. 定义交换宏(必须)
#define SWAP(a, b) { int temp = a; a = b; b = temp; }
void quick_Sort(int arr[] , int left , int right);
int main()
{
    int arr[]={5,1,8,3,9,11,34,90,24,6};
    int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    quick_Sort(arr,0,len-1);
    printf(" 排序后的数组:\n");
    for (int i = 0; i < len; i++)
    {
        printf(" %d ",arr[i]);
    }
    printf(" \n");
    return 0;
}
//快速排序
void quick_Sort(int arr[] , int left , int right)
{
    if(left >= right)
        return;
    
    int key = arr[left];    //比较基准
    int l = left;
    int r = right;

    //进行一轮排序,退出条件就是l和r相遇
    while(l<r){
        //r从右往左走,遇到比key小的元素就停下来
        while (l<r && arr[r] >= key){
            r--;
        }

        //l从左往右走,遇到比key大的元素就停下来
        while (l<r && arr[l] <= key){
            l++;
        }
        if(l<r){
            SWAP(arr[l] ,arr[r])
        }
    }
    //交换key和相遇点的值
    arr[left] = arr[l];
    arr[l] = key;
    
    //d.递归地对左右两部分分别进行快速排序
    quick_Sort(arr , left , l-1);
    quick_Sort(arr , l+1 , right);
}
   

三、应用场景

排序算法的种类和应用场景如下:

  1. 冒泡排序:适用于待排序序列元素个数较少,或对稳定性要求较高的场景。

  2. 快速排序:适用于待排序序列元素个数较多,且元素分布比较均匀的场景。

  3. 选择排序:适用于待排序序列元素个数较少,或对稳定性要求较高的场景。

  4. 插入/希尔排序:适用于待排序序列元素个数较少,或元素基本有序的场景。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值