排序算法集合

一、内部排序

1.冒泡排序:

外循环: 0 <= i <= n - 1
内循环:r [ j ] 和 r [ j + 1 ] 比较(j >= 0 && j < n - 1 - i )
0.0
内外循环控制循环次数:首先内循环比较的是 0 和 1 ,1 和 2……n-2 和 n - 1进行比较,将较大值冒泡到后面,最终第 n - 1 位置是最大值,内循环的范围是 0 到 n - 1。
0.0
接下来的内循环的次数就发生变化了,变成 0 到 n - 2了,因为最后一个是最大了,因而需要确定第 2 大的数字,比较的方法还是一样。
0.0
最终,比较 0 和 1 位置的元素值,将较大的放在 1 的位置,完成整个算法过程。

循环次数\下标01234
1544232418
2523442418
3523244418
4523241844
5523241844
6523182444
7……

0.0
下面算法中,还提供了一个标志:sorted
当第一次看到这个sorted就深深被它吸引,这个标志避免了不需要的比较过程:我们每次打算开始比较过程之前,都设置一个标志sorted = true,只有当交换之后sorted才为false。假如,某次循环过程中,sortedtrue了,说明交换过程没有发生,说明所有的数字都已经排好序了,一个交换都没有,从而停止了外层循环,减少了不必要的比较。

void BubbleSort(int a[],int n)
{
	int temp;
	bool sorted = false;
	for(int i = 0; i < n && sorted == false;n--)
		for(int j = 0;j< n-i-1;j++)
		{
			sorted = true;
				if( a[j] >a[j+1])
				{
					temp = a[j] ;
					a[j] = a[j+1];
					a[j+1] = temp;
					sorted = false;
				}
		}
}

换一种写法:

void bubblesort(int A[],int n)
{
	bool sorted = false;
	while(!sorted)
	{
		sorted = true;
		for(int i = 1;i < n;++i)
			if(A[i-1]> A[i])
			{
				swap(A[i-1],A[i]);
				sorted = false;
			}
		--n;
	}
}

再换一种写法:

void bubblesort(int A[],int n)
{
	for(bool sorted = false; sorted = !sorted;--n)
	{	
		sorted = true;
		for(int i = 1;i < n;++i)
		{
			if(A[i-1] > A[i])
			{
				swap(A[i-1],A[i]);
				sorted = false;
			}
		}
	}
}

算法的最好情况:
只需要一趟循环:0 和 1 ,1 和 2……n - 2 和 n - 1,总共是n - 1次,O(n)。
最坏的情况:需要比较 n - 1,n - 2,n - 3,…… 1 次,总共是n (n - 1)/2,O(n^2)。

2.选择排序:

从第一个数开始,和剩下没比较过的数相比,比它小就交换值,知道比到n-2。

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

O(n*n)

3.插入排序:

3.1直接插入排序

0.0
顾名思义,直接二字,我们假定原序列是一个排序好的(假定从小到大),要将新的数字插入,那么他的前一个数字一定比他小,后一个数字一定比他大。
0.0
定义很抽象,为什么能假定序列是排好序的?我们不妨将假定序列从1个数字开始。

原序列55173477985过程
插入11755347798517插到55前
插入21734557798534插到55前
插入3517345577985插到17前

0.0
算法的过程可以分为以下几步:

  1. 从位置 1 的数字开始,与前一个数字比较,如果比前一个数字小,则需要将 1 位置元素插入到 0 的前面,将 0 位置的元素后移到1的位置。我们很容易看到,插入的过程也同时带着数组元素后移的问题。
  2. 位置 2 的数字和位置 1 的数字比较,如果大于它,说明顺序正确;如果比 1 的数字小,那么就需要插入了,需要找到第一个比a[ i ]小的数字,将a[2]插入到这个数字的后面,将后面所有的元素后移一位。
  3. 其实插入2和插入3两个过程中也有好几次比较,只是不用插入罢了。
void InsertSort(int a[],int n)
{
	for (int i = 1; i < n; i++) 
	{
        int temp = a[i];
        int j;
        for (j = i - 1; j >= 0 && temp < a[j]; j--)
        //最终是遇到了一个temp > a[j]的情况,就将temp赋值到a[j+1]
      	 	a[j + 1] = a[j];			//将元素后移一位
        a[j + 1] = temp;
    }
}

最好情况:
原序列正序的情况下,完全不需要插入和移动,这时候只需要比较n - 1次,复杂度为O(n)
原序列逆序的情况下,每次都需要移动和插入,每次的比较的数量达到最大值,( n + 2 )( n - 1 ) / 2,复杂度为O( n ^ 2 )。

3.2折半插入

利用了之前的折半查找的知识,折半查找,最终找到mid值等于需要查找的value,不难推导,循环结束之后,high+1的值永远大于value。high的值永远小于等于value。

void BInsertSort(int a[],int n)
{
	int temp,low,high;
	int mid;
	for (int i = 1;i < n;i++)
	{
		temp = a[i];
		low = 0,high = i - 1;
		while (low<=high)
		{
			mid = (low+high)/2;
			if ( temp < a[mid])
				high = mid - 1;
			else
				low = mid + 1;
		}
		for(int j = i - 1;j>=high + 1;--j)
			a[j+1] = a[j];
		a[high + 1] = temp;
	}
}

3.3二路插入排序

4.希尔排序(插入排序改进)

void shellsort(int a[],int n)
{
	int i,j,gap = n,temp;
	do 
	{
		gap = gap/3+1;
		for(i = gap;i<n;i++)
		{
			gap = gap/3+1;
			if ( a[i] < a[i-gap])
			{
				temp = a[i];
				for ( j = i-gap;j >= 0 && temp < a[j]; j-=gap )
				{
					a[j+gap] = a[j];
				}
				a[j+gap] = temp;
			}
		}
	} while (gap > 1);
}

5. 归并排序

#define MAXSIZE 10

// 实现归并,并把最后的结果存放到list1里
void merging(int *list1, int list1_size, int *list2, int list2_size)
{
	int i, j, k, m;
	int temp[MAXSIZE];

	i = j = k = 0;

	while( i < list1_size && j < list2_size )
	{
		if( list1[i] < list2[j] )
		{
			temp[k++] = list1[i++];
		}
		else
		{
			temp[k++] = list2[j++];
		}
	}

	while( i < list1_size )
	{
		temp[k++] = list1[i++];
	}

	while( j < list2_size )
	{
		temp[k++] = list2[j++];
	}

	for( m=0; m < (list1_size + list2_size); m++ )
	{
		list1[m] = temp[m];
	}
}

void MergeSort(int k[], int n)
{
	if( n > 1)
	{
		int *list1 = k;
		int list1_size = n/2;
		int *list2 = k + n/2;
		int list2_size = n - list1_size;

		MergeSort(list1, list1_size);
		MergeSort(list2, list2_size);

		merging(list1, list1_size, list2, list2_size);
	}
}
```c
#include <stdio.h>
#define MAXSIZE 10

// 实现归并,并把最后的结果存放到list1里
void merging(int *list1, int list1_size, int *list2, int list2_size)
{
	int i, j, k, m;
	int temp[MAXSIZE];

	i = j = k = 0;

	while( i < list1_size && j < list2_size )
	{
		if( list1[i] < list2[j] )
		{
			temp[k++] = list1[i++];
		}
		else
		{
			temp[k++] = list2[j++];
		}
	}

	while( i < list1_size )
	{
		temp[k++] = list1[i++];
	}

	while( j < list2_size )
	{
		temp[k++] = list2[j++];
	}

	for( m=0; m < (list1_size + list2_size); m++ )
	{
		list1[m] = temp[m];
	}
}

void MergeSort(int k[], int n)
{
	if( n > 1)
	{
		int *list1 = k;
		int list1_size = n/2;
		int *list2 = k + n/2;
		int list2_size = n - list1_size;

		MergeSort(list1, list1_size);
		MergeSort(list2, list2_size);

		merging(list1, list1_size, list2, list2_size);
	}
}
int main()
{
	int i, a[10] = {5, 2, 6, 0, 3, 9, 1, 7, 4, 8};

	MergeSort(a, 10);

	printf("排序后的结果是:");
	for( i=0; i < 10; i++ )
	{
		printf("%d", a[i]);
	}
	printf("\n\n");

	return 0;
}
归并排序复杂度nlog(n)

6.快速排序

快速排序:利用递归与分治的思想。基本思路:选个key值,将所有小于key的数排到key的左边,所有大于key的数字排到key的右边,记录key的下标j,然后将数组分成两部分,继续这样的操作。
快排里有一些变量:
low:数组开始下标。
high:数组结尾下标。
key:关键数据。

012345
432615

上图是一个需排序的数组a。
快排的基本思想:
限定条件:i < j
作用域左括号{
循环1:
key 赋值 a[0] = 4;
low = 0;high = 5;
从左到右,寻找比key大的数字,找到了 a[3] = 6,记录下标 i = 3 .。

012345
432615

从右到左,寻找比key小的数字,找到了 a[4] = 1,记录下标 j = 4;

012345
432615

交换 i 与 j 对应数组元素,即 a[3] 与 a[4]。
此时的数组:

012345
432165

从i开始再向右,从左到右,寻找比key大的数字,找到了 a[4] = 6,记录下标 i = 4 .。
从j开始再向左,从右到左,寻找比key小的数字,找到了 a[3] = 1,记录下标 j = 3;
此时 i > j,跳出循环。
作用域右括号}
下面有了一步操作:
交换key值 a [ 0 ] 和 a [ j ]的值,这样就保证了key的左边,全是小于key的数字,key的右边,全是大于key的数字。
数组变成了这样:

012345
132465

接下来就容易理解了:
将数组切成两半,0-j,j-high两部分,再进行循环操作,最终会形成一个两个数一组的比较大小,得到答案。结合代码看是这样:

#include <iostream>
 
using namespace std;
 
void Qsort(int arr[], int low, int high)
{
    if (high <= low) return;
    int i = low;
    int j = high + 1;
    int key = arr[low];
    while (true)
    {
        while (arr[++i] < key)
        {
            if (i == high){
                break;
            }
        }
        while (arr[--j] > key)
        {
            if (j == low){
                break;
            }
        }
        if (i >= j) break;
        /*交换i,j对应的值*/
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    int temp = arr[low];
    arr[low] = arr[j];
    arr[j] = temp;
    Qsort(arr, low, j - 1);
    Qsort(arr, j + 1, high);
}
 
int main()
{
    int a[100000];
    int n;
    cin >> n;
    for(int i =0;i<n;i++)
    	cin >> a[i];
	Qsort(a, 0, n-1);
    for(int i = 0; i < n ; i++)
    {
        cout << a[i] << " ";
    }
    return 0;
}

换一种写法

int Partition(int data[],int length,int start,int end){
	if(data == nullptr || length <= 0 || start < 0 || end >= length)
		return -1;
	int index = RandomInRange(start,end);
	swap(data[index],data[end]);
	int small = start - 1;
	for(index = start;index < end;++index){
		if(data[index] < data[end]){
			++small;
			if(small != index)
				swap(data[index],data[small]);
		}
	}
	++small;
		swap(data[index],data[small]);
		return small;
}

void qsort(int data[],int length,int start,int end){
	if(start == end)
		return;
	int index = Partition(data,length,start,end);
	if(index > start)
		qsort(data,length,start,index - 1);
	if(index < end)
		qsort(data,length,index + 1,end);
}

7.基数排序

二、外部排序

1.堆排序

基本思想:
1.将序列形成一个大顶堆或者小顶堆
2.根节点移到末尾
3.剩余的n-1个构成一个新的堆
反复进行形成有序序列

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;//堆排序的核心是建堆,传入参数为数组,根节点位置,数组长度

void Heap_adjust(int a[],int root,int len){
    
    int lc = root * 2 + 1;//根节点的左子结点下标

    if (lc < len){//左子结点下标不能超出数组的长度
        int flag = lc;//flag保存左右节点中最大值的下标
        int rc = lc + 1;//根节点的右子结点下标

        if (rc < len)//右子结点下标不能超出数组的长度(如果有的话)
            if (a[rc] > a[flag])//找出左右子结点中的最大值
                flag = rc;

        if (a[root] < a[flag]){
            //交换父结点和比父结点大的最大子节点
            swap(a[root],a[flag]);
            //从此次最大子节点的那个位置开始递归建堆
            Heap_adjust(a,flag,len);
        }
    }
}
 
void Heap_sort(int a[],int len){
    for (int i = len / 2; i >= 0; --i)//从最后一个非叶子节点的父结点开始建堆
        Heap_adjust(a,i,len);
 
    for (int j = len-1; j > 0; --j){//j表示数组此时的长度,因为len长度已经建过了,从len-1开始
        swap(a[0],a[j]);//交换首尾元素,将最大值交换到数组的最后位置保存
        Heap_adjust(a,0,j);//去除最后位置的元素重新建堆,此处j表示数组的长度,最后一个位置下标变为len-2
    }
 
}
int main(int argc, char **argv)
{
    int a[10] = {12,45,748,12,56,3,89,4,48,2};

    Heap_sort(a,10);

    for (size_t i = 0; i != 10; ++i)
         cout << a[i] << " ";

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值