排序算法详解

       记录排序的知识,也在后面不断学习的过程之中不断丰富该类算法,同样自己也把这里作为自己的笔记进行记录;这里也会详细介绍思想。

科普

什么是排序算法

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

什么是算法稳定性

排序算法的稳定性是指在待排序序列中,若存在两个相等的元素,排序后这两个元素的相对位置是否会发生变化。如果相等元素的相对位置在排序后仍然保持不变,那么这样的排序算法被认为是稳定的;如果相对位置产生改变,则被认为该算法不稳定。

如何判断算法是否稳定

要判断一个排序算法是否稳定,可以通过分析该算法的特性来确定。对于不稳定的排序算法,只需要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须深入分析算法以得出其稳定性。

冒泡排序

冒泡排序是一种计算机领域的简单的排序算法(也是最为基础的一种),在面临时间要求的时候可能会出现超时的情况。但是冒泡排序也是初学者必须要掌握的一种基础算法。

原理:

它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

以下图片来源于百度百科

代码样例示范:


#include<stdio.h>
int main(){
	int i,j;
	int n;
	int arr[100]={0};
	
	printf("请输入数组个数: ");
	scanf("%d",&n);
	
	printf("请输入数组:");
	for(i=0;i<n;i++)
	 scanf("%d",&arr[i]);
	 
	  for ( i = 0; i < n - 1; i++) {   //循环n-1次,
        for ( j = 0; j < n - 1 - i; j++) {   //逐渐减少是因为最后面最先确认好
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    
    printf("冒泡排序之后:");
	for(i=0;i<n;i++)
	 printf("%d ",arr[i]); 
    
    return 0;
} 

上述代码可以做一个优化,就是通过判断一轮比较是否存在交换,若不存在交换,则说明已经按照要求成功排序,可以考虑多加一个参数进行判断,然后直接退出;这样一来则会节省时间。


时间复杂度:

若数组初始状态是正序的话,一趟扫描就可以完成,所以比较次数C和记录移动次数M达到最少,

则C=n-1,M=0。

如果是若初始文件是反序的,需要进行 n -1 趟排序。每趟排序要进行 n-i 次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到 最大值

冒泡排序的最坏时间复杂度为O(n^2)

算法稳定性

冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

快速排序

快速排序是基于冒泡排序的一种改进。

原理:

快速排序采用的是分治思想,即在一个无序的序列中选取一个任意的基准元素x,利用x将待排序的序列分成两部分,前面部分元素均小于或等于基准元素,后面部分均大于或等于基准元素,然后采用递归的方法分别对前后两部分重复上述操作,直到将无序序列排列成有序序列。

代码样例示范


void quick_sort(int arr[], int left, int right) {
    if (left >= right) {
        return;
    }
    int i = left, j = right, key = arr[left];
    while (i < j) {
        while (i < j && arr[j] >= key) {
            j--;
        }
        if (i < j) {
            arr[i++] = arr[j];
        }
        while (i < j && arr[i] <= key) {
            i++;
        }
        if (i < j) {
            arr[j--] = arr[i];
        }
    }
    arr[i] = key;
    quick_sort(arr, left, i - 1);
    quick_sort(arr, i + 1, right);
}

时间复杂度:

最好情况下,每次划分都能将待排序序列划分为两个长度相等的子序列,此时时间复杂度为O(nlogn)。最坏情况下,每次划分都不能将待排序序列划分为两个长度相等的子序列,此时时间复杂度为O(n^2)。平均情况下,时间复杂度为O(nlogn)。

算法稳定性:

快速排序是不稳定的排序算法。因为当待排序序列中存在多个相同的元素时,快速排序可能会改变它们的相对位置。

选择排序

选择排序(Selection Sort)是一种简单直观的排序算法。

原理

每次从未排序的部分选择出最小(或最大)的元素,将其放到已排序序列的末尾,直到所有元素都被排序。

  1. 从未排序的序列中找到最小(或最大)的元素。
  2. 将找到的最小(或最大)元素与未排序序列的第一个元素交换位置。
  3. 在剩余的未排序序列中重复步骤1和2,直到所有元素都被排序。

(图片来源于百度)

代码样例示范


#include <stdio.h>
void selectionSort(int arr[], int n) {
    int i, j, min_idx;
    for (i = 0; i < n-1; i++) {
        min_idx = i;  //记录此时的下标 
        for (j = i+1; j < n; j++) {
            if (arr[j] < arr[min_idx])   //找到最小的数值的下标 
                min_idx = j;
        }

        int temp = arr[min_idx];  //进行置换 
        arr[min_idx] = arr[i];
        arr[i] = temp;
    }
}

int main() {
    int arr[50]={0};
    int n,i;
    
    printf("数组元素个数: ");
    scanf("%d",&n);
    
    printf("Original array: ");
     for (i = 0; i < n; i++)
       scanf("%d", &arr[i]);
        
    selectionSort(arr, n);
    printf("Sorted array: ");
     for (i = 0; i < n; i++)
        printf("%d ", arr[i]);
        
    return 0;
}


时间复杂度

O(n^2),

其中n是待排序数组的长度。这是因为选择排序的每一轮都需要找到未排序序列中的最小(或最大)元素,需要进行n-1次比较。总共有n-1轮,所以平均情况和最坏情况下的时间复杂度都是O(n^2)。

算法稳定性

选择排序是一种不稳定的排序算法,即相等元素的相对顺序可能会改变。

例如: 对于序列[3, 3, 1],在第一轮选择后,第一个3会与1交换位置,导致序列变为[1, 3, 3]。因此,选择排序在某些情况下可能无法保持相等元素的相对顺序。

插入排序

一种简单直观的排序算法。

定义

直接插入排序是一种将待排序数据按照插入的方式,逐个插入到已排好序的有序数列中的算法。

原理

直接插入排序的核心思想是将待排数据分为两部分,一部分为已排序好的有序数组,一部分为待排序的无序数组,每次从待排序数组中取出一个元素,将它插入到有序数组中的合适位置。具体实现时,我们可以从第二个元素开始,将当前元素插入到已排序序列中,直到所有的元素插入完毕。


代码样例

#include <stdio.h>
void insertionSort(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;
    }
}
int main() {
    int arr[50]={0};
    int n,i;
    
    printf("数组元素个数: ");
    scanf("%d",&n);
    
    printf("Original array: ");
     for (i = 0; i < n; i++)
       scanf("%d", &arr[i]);
        
    insertionSort(arr, n);
    printf("Sorted array: ");
     for (i = 0; i < n; i++)
        printf("%d ", arr[i]);
        
    return 0;
}


时间复杂度

在最好情况下(即待排序数组本身就是有序的情况下),直接插入排序的时间复杂度为O(n),其中n为待排序数组的长度;在最坏情况下(即待排序数组本身就是逆序的情况下),时间复杂度为O(n^2);平均情况下,时间复杂度为O(n^2)。

算法稳定性

直接插入排序是一种稳定的排序算法,即对于具有相同关键字的元素,在排序前后它们的相对位置不会改变。

二分查询

二分查找(Binary Search)是一种常用的快速查找算法,也叫折半查找。

原理

有序数组分成左右两个区间,每次都取中间位置的元素进行比较,如果中间位置元素等于目标值,则查找成功;如果中间位置元素大于目标值,则在左区间继续查找;否则在右区间继续查找,直到找到目标位置或区间为空为止。


代码实现

#include <stdio.h>
#include<string.h>
int binarySearch(int arr[], int l, int r, int x) {
    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (arr[mid] == x) {
            return mid;
        }
        if (arr[mid] < x) {
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    return -1;
}

int main() {
    int arr[10]={0} ;
    int n,i;
    int x ;
    
    printf("请输入数组个数:");
    scanf("%d",&n);
    
    for(i=0;i<n;i++)
    scanf("%d",&arr[i]);
    
    printf("请输入要查找的数字: ");
    scanf("%d",&x);
    
    int result = binarySearch(arr, 0, n - 1, x);
    if (result == -1) {
        printf("Element not found\n");
    } else {
        printf("Element found at index %d\n", result);
    }
    return 0;
}

样例截图:


算法的时间复杂度: O(log n)

希望以上对大家有所帮助,由于时间准备的比较少,后续也会在此片章之中不断完善各种排序。

如果上述文章存在一些问题也可以私信纠正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值