算法思想-排序

排序的实质

排序并不是一种算法思想,而是基于遍历的一种算法实现。形形色色的排序算法,并不能绝对的认为哪一种排序最好,因为最好的度量指标究竟是耗时、内存占用亦或是稳定性是不确定的,这个要根据使用场景来决定,但是我们必须掌握各种排序方法的实现原理,这样才能做到“具体问题具体分析”。下图为多种排序算法的特性归纳:
在这里插入图片描述

参考:各种排序动图展示

选择排序:直接选择排序

直接选择排序(Straight Select Sorting) 是一种简单的排序方法,它的基本思想是:第一次从R[0] ~ R[n-1]中选取最小值,与R[0]交换,第二次从R[1] ~ R[n-1]中选取最小值,与R[1]交换,…,第i次从R[i-1] ~ R[n-1]中选取最小值,与R[i-1]交换,…,第n-1次从R[n-2] ~ R[n-1]中选取最小值,与R[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

时间复杂度:o(n^2) 稳定性:不稳定
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
void SeletedSont(int a[],int n)
{
	int i,j,kmin,t;
	for(i=0;i<n-1;i++)//每一次找到一个范围内数据的最小值,逐渐缩小比较范围,第一次范围是R[0] ~ R[n-1],第二次为R[0] ~ R[n-1]...
	{
		kmin=i; //取一个数据作为比较对象
		for(j=i+1;j<n;j++) //将比较对象依次与右边的数据比较大小
		{
			if(a[j]<a[kmin])  kmin=j;//如果右边的某一数比比较对象小,则记录该数据下标,注意这里可能记录多次,因为要找到右边最小的那个数
		}
		if(i-kmin)//判断是否下标发生改变
		{
			t=a[i];//若改变则将比较对象与找到的更小的值的位置互换
			a[i]=a[kmin];
			a[kmin]=t;
		}
	}
 }
 int main(void)
 {
 	int a[7]={3,2,5,8,4,7,6};
 	SeletedSont(a,7);
 	for(int i=0;i<7;i++)
 	{
 		printf("%5d",a[i]);
	}
	return 0;
 } 
趟数监视哨排序结果
125(12,)25,36,45,2,9,39,22,98,37
212(12,25,)36,45,2,9,39,22,98,37
336(12,25,36,)45,2,9,39,22,98,37
445(12,25,36,45,)2,9,39,22,98,37
52(2,12,25,36,45,)9,39,22,98,37
69(2,9,12,25,36,45,)39,22,98,37
739(2,9,12,25,36,39,45,)22,98,37
822(2,9,12,22,25,36,39,45,)98,37
998(2,9,12,22,25,36,39,45,98,)37
1037(2,9,12,22,25,36,37,39,45,98,)

插入排序:直接插入排序

插入排序会将某一值插入到合适位置,其它数据作相应移动,因此称之为插入排序。
1、将第二个数据与第一个比较,如果第二个数据小,则第一个数据右移一个位置,第一个数据空出的位置放置第二个数据
2、将第三个数据与第二个比较,如果第三个数据小,则依次与第二个、第一个数据比较,若第三个数据最小,则将第一、第二个数据依次右移一个位置,最左边空出的位置放置第三个数据;若第三个数据比第二个数据小,但是比第一个数据大,则只将第二个数据右移一个位置,原先第二数据的位置放置第三个数据。
3、依次比较当前数据与上一数据大小,作插入动作,每一次插入都能保证右侧值大于左侧值,最后就形成由小到大的排序
插入排序动图
插入排序动图

//array[]为待排序数组,n为数组长度
void insertSort(int array[], int n)
{
    int i,j,temp;
    for( i=1;i<n;i++) //依次选择所有元素,作为比较值
    {
        if(array[i]<array[i-1]) //若比较值小于其临近左侧数据,则需要做插入动作
        {
            temp=array[i];
            for( j=i;array[j-1]>temp;j--)//向左侧依次查找,直到比较值大于左侧某一个数据
            {
                array[j]=array[j-1]; //将连续存放的,大于比较值得所有数据依次右移一个位置
            }
            array[j]=temp; //空出的位置放置比较值,实现一个数据插入
        }
    }
}

交换排序:冒泡排序

冒泡排序从小到大排序:一开始交换的区间为0 ~ N-1,将第1个数和第2个数进行比较,前面大于后面,交换两个数,否则不交换。再比较第2个数和第三个数,前面大于后面,交换两个数否则不交换。依次进行,最大的数会放在区间最后的位置(完成一次冒泡)。然后将范围变为0~N-2,再做冒泡,最终数组第二大的数会放在数组倒数第二的位置。依次进行整个交换过程,最后范围只剩一个数时数组即为有序。
在这里插入图片描述

//array[]为待排序数组,n为数组长度
void BubbleSort(int array[], int n)
{
    int i, j, k;
    for(i=0; i<n-1; i++)
        for(j=0; j<n-1-i; j++)
        {
            if(array[j]>array[j+1])
            {
                k=array[j];
                array[j]=array[j+1];
                array[j+1]=k;
            }
        }
}

交换排序:快速排序

快速排序从小到大排序:在数组中随机选一个数(默认数组首个元素,本例程中使用随机算法优化了快速排序,参考:舍伍德优化快速排序),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。
在这里插入图片描述

#include <iostream>
#include<cstdio>
using namespace std;

int randPartition(int A[], int left, int right)
{
	//变化开始处
    srand((unsigned)time(NULL));
    int p = 1.0*rand() / RAND_MAX*(right-left) +left;
    swap(A[left], A[p]);
    //变化终止处
    //比较的起始仍是0数据,只是该数据已被随机替换为其它数据
    //暂存A[0]内存放的数据
    int temp = A[left];
    
    //3、循环执行1、2两步,right逐渐变小,left逐渐变大,最终,比A[0]大的数都在left下标的右侧,比A[0]小的数都在left下标左侧
    while(left < right)
    {
    	//1、从最右侧开始找到比A[0]内数据小的数据A[right],暂存到A[left]位置
        while(temp < A[right] && left < right) right--;
        A[left] = A[right];
        //2、从最左侧查找比A[0]内数据大的数据,存放到上一步空出的右侧的A[right]位置
        while(temp >= A[left] && left < right) left++;
        A[right] = A[left];
    }
    //将A[0]内数据放到A[left]位置
    A[left] = temp;
    //以left作为分界,将序列分为左右两部分
    return left;
}
void QuickSOrt(int A[], int left, int right)
{
	//递归结束条件,left = right,即最终递归的结果是分割的子序列只有两个元素,不需要继续分割仅排序即可
    if(left < right)
    {
    	//1、先完成基于基础数据的左右比较放置
        int pos = randPartition(A, left, right);
        //2、将基础数据处于的位置作为中点,分割为两个子序列,然后对两个子序列继续执行步骤1、2
        QuickSOrt(A, left, pos-1);
        QuickSOrt(A, pos+1, right);
    }

}
int main()
{
    int A[11] = {35,18,16,72,24,65,12,88,46,28,55};
    
    QuickSOrt(A, 0, 10);
    for(int i = 0; i< 11; i++)
    {
        printf("%d ", A[i]);
    }
    return 0;
}

假设随机出的起始数据是A[0] = 35,那么一次randPartition的过程如下:

int A[11] = {35,18,16,72,24,65,12,88,46,28,55}; //原始数据

int A[11] = {28,18,16,72,24,65,12,88,46,28,55}; //right=9,A[9]数据拷贝到A[0]
int A[11] = {28,18,16,72,24,65,12,88,46,72,55}; //left=3, A[3]数据拷贝到A[9]

int A[11] = {28,18,16,12,24,65,12,88,46,72,55}; //right=6, A[6]数据拷贝到A[3]
int A[11] = {28,18,16,12,24,65,65,88,46,72,55}; //left=5, A[5]数据拷贝到A[6]

int A[11] = {28,18,16,12,24,65,12,88,46,72,55}; //right=5, 不满足left <right,只是空跑一次,right–
int A[11] = {28,18,16,12,24,65,65,88,46,72,55}; //left=5

int A[11] = {28,18,16,12,24,35,65,88,46,72,55}; //left=right-5,将起始数据拷贝到A[5]
此时A[5]左边的数据全部小于35,右边的数据全部大于35,后面再对{28,18,16,12,24}, {65,88,46,72,55}分别进行上述操作

归并排序

排序的实质是按照大小关系排列数据,通常都需要以此比较两个数据的大小,实际上数据量为4个序列排序,可以看做两组数据量为2的序列的组合,先将子序列排序,再讲子序列合并就能得到最终的排序序列。这一过程与分治思想完全吻合,可按照如下步骤来做:
1、分解:将待排序序列逐级分解,直到序列的数据量为1个
2、解决:反向排序,即对每一个数量为1的序列排序(实质一个数据不需要排序)
3、合并:反向合并,按照分解顺序反向组合,组合式要考虑同时排序
在这里插入图片描述在这里插入图片描述

#include<stdio.h>
#define ArrLen 20
void printList(int arr[], int len) {
	int i;
	for (i = 0; i < len; i++) {
		printf("%d\t", arr[i]);
	}
}
void merge(int arr[], int start, int mid, int end) {
	int result[ArrLen]; //存放某一次组合后结果,限定成20不太合适
	int k = 0;
	int i = start;
	int j = mid + 1;
	//执行一次左右两段序列的合并,i为左边序列的下标,j为右边序列的下标,实质是两组数据按照下标顺序依次比较并存放
	while (i <= mid && j <= end) {
		if (arr[i] < arr[j]){
			result[k++] = arr[i++];
        }
		else{
			result[k++] = arr[j++];
        }
	}
	//左边序列先到达结尾,则右边序列剩余数据依次放到result
	if (i == mid + 1) {
		while(j <= end)
			result[k++] = arr[j++];
	}
	//右边序列先到达结尾,则左边序列剩余数据依次放到result
	if (j == end + 1) {
		while (i <= mid)
			result[k++] = arr[i++];
	}
	for (j = 0, i = start ; j < k; i++, j++) {
		arr[i] = result[j];
	}
}
 
void mergeSort(int arr[], int start, int end) {
	//一直递归分解,按照中间位置进行拆分,当start=end时,表示只有一个数据,停止拆分
	if (start >= end)
		return;
	int mid = ( start + end ) / 2;
	//每次递归同时拆分左右两段序列
	mergeSort(arr, start, mid);
	mergeSort(arr, mid + 1, end);
	//拆分递归结束了,便开始排序与组合
	merge(arr, start, mid, end);
}
 
int main()
{
	int arr[] = {4, 7, 6, 5, 2, 1, 8, 2, 9, 1};
	mergeSort(arr, 0, 9);
	printList(arr, 10);
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值