排序

本文总结了十个常见排序。主要是我在找实习复习时学习一像素博主的文章
https://www.cnblogs.com/onepixel/articles/7674659.html
然后自己写了代码,再加上了一些自己的理解所得。

冒泡排序

int sort(int *a, int size)
{
	for (int i = size-1; i >= 0; i--)
	{
		for (int j = 0; j < i; j++)
		{
			if (a[j] > a[j+1])
			{
				int temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
	return 0;
}

时间复杂度(n-1)+(n-2)+(n-3)+…+1=n*(n-1)/2 即O(n^2).
最好情况下第一次扫描发现没有不符合的项。O(n)
即可将上述代码添加一些条件,第二个for循环,若是一遍下来没发现交换项,即结束。
空间复杂度,交换时占一个,所以O(1).






选择排序

int sort(int *a, int size)
{
	for (int i = 0; i < size; i++)
	{
		int min = a[i], count = i;
		for (int j = i; j < size; j++)
		{
			if (min > a[j])
			{
				min = a[j];
				count = j;
			}
		}
		int temp = a[i];
		a[i] = a[count];
		a[count] = temp;
	}
	return 0;
}

时间复杂度同冒泡
最好也要O(n^2) 因为每轮都要找最大,所以是否排好序对程序没有影响,依然要执行很多步。
空间复杂度,比较占用O(1).






插入排序

int sort(int *a, int size)
{
	for (int i = 0; i < size; i++)
	{
		int tem = a[i];
		for (int j = i; j > 0; j--)
		{
			if (a[ j - 1 ] >= tem)
				a[ j ] = a[ j - 1 ];
			else
			{
				a[j] = tem;
				break;
			}
		}
	}
	return 0;
}

每次选择一个元素放在他对应的位置,最快O(n),平均是和冒泡和选择时间一样。
空间复杂度是O(1),需要一个额外的变量存放当前的数。






希尔排序

int sort(int *a, int size)
{
	int preIndex = 0, current = 0,adde;
	for (adde = size / 2; adde >= 1; adde /= 2)
	{
		for (int i = adde; i < size; i++)
		{
			int j = i;
			current = a[j];
			while (j - adde >= 0 && a[j - adde] > current)
			{
				a[j] = a[j - adde];
				j = j - adde;
			}
			a[j] = current;
		}
	}
	return 0;
}

设待排序元素序列有n个元素,首先取一个整数作为间隔(上面取得是size一半),将全部元素分为所取整数个子序列,所有距离为该整数的元素放在同一个子序列中,在每一个子序列中分别实行直接插入排序。然后缩小间隔(可以逐渐减小,也可以直接/2),重复上述子序列划分和排序工作。直到最后取1,将所有元素放在同一个子序列中排序为止。

由于开始时,整数的取值较大,每个子序列中的元素较少,排序速度较快,到排序后期取值逐渐变小,子序列中元素个数逐渐增多,但由于前面工作的基础,大多数元素已经基本有序,所以排序速度仍然很快。

时间复杂度和空间复杂度与插入排序一样,但平均时间复杂度,平均性能好很多。






归并排序

void merge_sort(int *data, int start, int end, int *result)
{
    if(1 == end - start)//如果区间中只有两个元素,则对这两个元素进行排序
    {
        if(data[start] > data[end])
        {
            int temp  = data[start];
            data[start] = data[end];
            data[end] = temp;
        }
        return;
    }
    else if(0 == end - start)//如果只有一个元素,则不用排序
        return;
    else
    {
        //继续划分子区间,分别对左右子区间进行排序
        merge_sort(data,start,(end-start+1)/2+start,result);
        merge_sort(data,(end-start+1)/2+start+1,end,result);
        //开始归并已经排好序的start到end之间的数据
        merge(data,start,end,result);
        //把排序后的区间数据复制到原始数据中去
        for(int i = start;i <= end;++i)
            data[i] = result[i];
    }
}

void merge(int *data,int start,int end,int *result)
{
    int left_length = (end - start + 1) / 2 + 1;//左部分区间的数据元素的个数
    int left_index = start;
    int right_index = start + left_length;
    int result_index = start;
    while(left_index < start + left_length && right_index < end+1)
    {
        //对分别已经排好序的左区间和右区间进行合并
        if(data[left_index] <= data[right_index])
            result[result_index++] = data[left_index++];
        else
            result[result_index++] = data[right_index++];
    }
    while(left_index < start + left_length)
        result[result_index++] = data[left_index++];
    while(right_index < end+1)
        result[result_index++] = data[right_index++];
}

分为划分和归并过程。
时间复杂度O(nlogn)但需要额外的空间,空间复杂度为O(n)





快速排序

int quickSort(int *a,int start,int end)
{
	if (start >= end)
		return 0;
	int k = start;
	int i = start + 1, j = end;
	while (i < j )
	{

		while (a[i] < a[k] && i<end)
			i++;
		while (a[j] > a[k] && j>start)
			j--;
		if (i < j)
		{
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
		}
	}
	if (a[j]<a[k])
	{
		int temp = a[k];
		a[k] = a[j];
		a[j] = temp;
	}
	quickSort(a, start, j-1);
	quickSort(a, j + 1, end);
	return 0;
}

也是分区排序的思想,上面程序利用递归来实现,还有非递归,有时间看看。
时间复杂度O(nlogn),每次划分为n/2时情况最好。
空间复杂度传参两个,平均递归深度logn,所以平均O(logn),可以取首中尾三个数据优化。但也不能完全消除退化成冒泡那样的可能。





堆排序

int adjust(int *a,int size)
{
	if (size <= 1)
		return 0;
	int subroot = size / 2 - 1, subright = size / 2 * 2, subleft = size / 2 * 2 - 1;
	int largest = subroot;
	if (subright <= size - 1 && a[subright] > a[largest])
		largest = subright;
	if (subleft <= size - 1 && a[subleft] > a[largest])
		largest = subleft;
	if (largest != subroot)
	{
		swap(a[largest], a[subroot]);
		adjust(a, subroot + 1);
	}
	else
		adjust(a, size - 1);
	return 0;
}
int heapSort(int *a,int size)
{
	adjust(a,size);
	int last = size - 1;
	for (int i = 0; i < size; i++)
	{
		swap(a[0], a[last]);
		last--;
		adjust(a, last + 1);
	}
	return 0;
}

采用的是最大堆最小堆的特殊性,此处用数组来表示。
堆排序可以分成两个过程,一个是建最大堆,然后不断取出最大数,再进行堆调整。
建最大堆,即使是最慢的两两比较,又多次重复也还是常数级,然后成了最大堆之后,将堆顶元素向下调整,而由于已经是最大堆了,所以第二层两个有一个一定为剩余中最大,和那个交换,然后将小元素依次向下调整即可。n个元素,logn层,总共取n次最大值。所以复杂度为最大不过n+nlogn。且没有什么最坏或最好情况,因为一旦建成最大最小堆,则不存在好坏。好坏只影响第一次建堆。

我上面的代码可能复杂度比我说的要高,因为我每一次都重复了最麻烦的第一次建堆。





计数排序

int max(int *a,int size)
{
	int maxvalue = 0;
	for (int i = 0; i < size; i++)
	{
		if (maxvalue < a[i])
			maxvalue = a[i];
	}
	return maxvalue;
}

int countSort(int *a,int maxValue,int size)
{
	int *result = new int[maxValue];
	for (int i = 0; i < maxValue; i++)
	{
		result[i] = 0;
	}
	for (int i = 0; i < size; i++)
	{
		result[a[i]-1]++;
	}
	int k = 0;
	for (int i = 0; i < maxValue; i++)
	{
		for (int j = 0; j < result[i]; i++)
		{
			a[k] = i+1;
			k++;
		}
	}
	delete[] result;
	return 0;
}

比较容易理解,就是用一个包括数字大小范围的数组,而且只能是整数,或者小数位数小也可以,如果数字错综复杂而且个数没多少,这样做就不必了。如果数字只是1到1000,却有几十万的数据,那用计数排序确实是会很快。
首先遍历一遍数组,找出最大数。动态建立一个数组,赋初值为0,再遍历一遍原数组。将原数组中含有得数在动态数组中统计数量。最后根据是否有数将之还给原数组。最后记得回收动态建立的数组,将内存还给自由存储区。
时间复杂度2(maxvalue+n) 空间复杂度maxvalue+n





桶排序
代码就不写了,和计数排序差不多,关键就在于桶的划分,划分得越多,排序时间越快,空间消耗越大。




时间复杂度N+K,但最坏情况下取决于桶内用什么排序。





基数排序
也是和上面两个类似。不过是根据位数来划分,本质都是分配、收集。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值