排序的概念,以及8种基本排序算法的优缺点总结与c语言实现

1. 熟悉排序的相关概念:什么是排序,排序的稳定性,内部排序与外部排序

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


稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法

冒泡排序、插入排序、归并排序和基数排序都是稳定的排序算法。

内部排序:数据元素全部放在内存中的排序。


外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。


2. 熟悉插入、希尔、选择、堆排、冒泡、归并、计数、基数排序的以下内容:
排序原理、代码实现、稳定性、时间空间复杂度、应用场景

插入排序:直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一 个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1),它是一种稳定的排序算法

4. 稳定性:稳定

#include <stdio.h>
#include <stdlib.h>

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Insert_Sort(int *array, int size)
{
	int i, j;
	for (i = 1; i < size; ++i)	//待排序的
	{
		for (j = 0; j < i; ++j)	//排好序的
		{
			if (array[i] < array[j])
			{
				int tmp = array[i];	//需要排序的元素array[i]
				array[i] = array[j];	//向后搬移
				array[j] = tmp;		//填入记录的数据
			}
		}
	}
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);

	//插入排序
	Insert_Sort(array, size);
	printf("插入排序后的数据:\n");
	Printf(array, size);

	system("pause");
	return 0;
}

希尔排序:希尔排序法又称缩小增量法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

希尔排序的特性总结:
1. 希尔排序是对直接插入排序的优化。

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3—N^2)

4. 稳定性:不稳定 

#include <stdio.h>
#include <stdlib.h>

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Shell_Sort(int *array, int size)
{
	int i, j;
	int gap;
	int tmp;
	for (gap = size / 2; gap > 0; gap = gap / 2)
	{
		for (i = gap; i < size; i++)
		{
			tmp = array[i];
			for (j = i - gap; (j >= 0) && (array[j] > tmp); j = j - gap)
			{
				array[j + gap] = array[j];
			}
			array[j + gap] = tmp;
		}
	}
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);
        //希尔排序
	Shell_Sort(array, size);
	printf("希尔排序后的数据:\n");
	Printf(array, size);
	system("pause");
	return 0;
}

选择排序:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完 。

直接选择排序的特性总结: 
1. 直接选择排序思考非常好理解,但是效率不是很好,实际中很少使用。

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

#include <stdio.h>
#include <stdlib.h>

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Select_Sort(int *array, int size)
{
	int i, j, k, tmp;
	for (i = 0; i < size; ++i)
	{
		for (j = i + 1; j < size; ++j)
		{
			//k = i;
			if (array[j] < array[i])
			{
				//k = j;
				tmp = array[j];
				array[j] = array[i];
				array[i] = tmp;
			}
		}
	}
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);

	//选择排序
	Select_Sort(array, size);
	printf("选择排序后的数据:\n");
	Printf(array, size);

	system("pause");
	return 0;
}

堆排序:堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

#include <stdio.h>
#include <stdlib.h>

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Swap(int *a, int *b)
{
	int t = 0;
	t = *a;
	*a = *b;
	*b = t;
}
void AdjustDown(int *array, int i, int size)
{
	int child = i * 2 + 1;
	int tmp;
	for (tmp = array[i]; 2 * i + 1 < size; i = child) {
		child = 2 * i + 1; //注意数组下标是从0开始的,所以左孩子的求发不是2*i
		if (child != size - 1 && array[child + 1] > array[child])
			++child;                //找到最大的儿子节点
		if (tmp < array[child])
			array[i] = array[child];
		else
			break;
	}
	array[i] = tmp;
}

void Heap_Sort(int *array, int size)
{
	int i;
	for (i = (size - 2) / 2; i >= 0; i--)
	{
		AdjustDown(array, i, size);
	}
	for (i = size - 1; i > 0; --i)
	{
		Swap(&array[0], &array[i]);
		AdjustDown(array, 0, i);
	}
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);

    //堆排序
	Heap_Sort(array, size);
	printf("堆排序后的数据:\n");
	Printf(array, size);

	system("pause");
	return 0;
}

冒泡排序:它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

冒泡排序的特性总结:
1. 冒泡排序是一种非常容易理解的排序

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定

#include <stdio.h>
#include <stdlib.h>

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

//冒泡排序(升序)
void Bubble_Sort(int *array, int size)
{
	int i = 0;
	int j = 0;
	for (int i = 0; i < size - 1; ++i)
	{
		for (j = 0; j < size - i - 1; ++j)
		{
			if (array[j] > array[j + 1])
			{
				int tmp = array[j];
				array[j] = array[j + 1];
				array[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);

    //冒泡排序
	Bubble_Sort(array, size);
	printf("冒泡排序后的数据:\n");
	Printf(array, size);

	system("pause");
	return 0;
}

归并排序:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 

归并排序的特性总结: 
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(N)

4. 稳定性:稳定
 

#include <stdio.h>
#include <stdlib.h>
#define N 10

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void merge(int arr[], int low, int mid, int high) 
{
	int i, k;
	int *tmp = (int *)malloc((high - low + 1) * sizeof(int));
	int left_low = low;
	int left_high = mid;
	int right_low = mid + 1;
	int right_high = high;

	for (k = 0; left_low <= left_high && right_low <= right_high; k++)
	{  
		if (arr[left_low] <= arr[right_low]) 
		{
			tmp[k] = arr[left_low++];
		}
		else 
		{
			tmp[k] = arr[right_low++];
		}
	}

	if (left_low <= left_high) 
	{ 
		for (i = left_low; i <= left_high; i++)
			tmp[k++] = arr[i];
	}

	if (right_low <= right_high) 
	{
		for (i = right_low; i <= right_high; i++)
			tmp[k++] = arr[i];
	}
	for (i = 0; i < high - low + 1; i++)
		arr[low + i] = tmp[i];
	free(tmp);
	return;
}

void Merge_Sort(int arr[], unsigned int first, unsigned int last) 
{
	int mid = 0;
	if (first < last) 
	{
		mid = (first + last) / 2; /* 注意防止溢出 */
		/*mid = first/2 + last/2;*/
		//mid = (first & last) + ((first ^ last) >> 1);
		Merge_Sort(arr, first, mid);
		Merge_Sort(arr, mid + 1, last);
		merge(arr, first, mid, last);
	}
	return;
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);

    //归并排序
	Merge_Sort(array, 0, size - 1);
	printf("归并排序后的数据:\n");
	Printf(array, size);

    system("pause");
	return 0;
}

计数排序:计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。

如果用快速排序,该算法的复杂度为O(nlog^2n)。改用计数排序后,复杂度降为O(nlogn)

计数排序算法是一个稳定的排序算法

#include <stdio.h>
#include <stdlib.h>

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

void Count_Sort(int *array, int size)
{
	int max = array[0];
	int min = array[0];
	for (int i = 0; i < size; i++)
	{
		if (array[i] > max)
			max = array[i];
		if (array[i] < min)
			min = array[i];
	}
	int range = max - min + 1;
	int *b = (int *)calloc(range, sizeof(int));
	for (int i = 0; i < size; i++)
	{
		b[array[i] - min] += 1;
	}
	int j = 0;	
	for (int i = 0; i < range; i++)
	{		
		while (b[i]--)
		{			
			array[j++] = i + min;		
		}	
	}	
	free(b);	
	b = NULL;
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);
    Count_Sort(array, size);
	printf("计数排序后的数据:\n");
	Printf(array, size);
    system("pause");
	return 0;
}

基数排序:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

时间效率 [1]  :设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(radix),共进行d趟分配和收集。 空间效率:需要2*radix个指向队列的辅助空间,以及用于静态链表的n个指针

#include <stdio.h>
#include <stdlib.h>
#define RADIX_10 10    
#define KEYNUM_31 10

void Printf(int array[],int size)
{
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

//基数排序(升序)
int GetNumInPos(int num, int pos)
{
	int temp = 1;
	for (int i = 0; i < pos - 1; i++)
		temp *= 10;
	return (num / temp) % 10;
}

void Radix_Sort(int *array, int size)
{
	int *array1[RADIX_10];
	for (int i = 0; i < 10; i++)
	{
		array1[i] = (int *)malloc(sizeof(int) * (size + 1));
		array1[i][0] = 0;
	}
	for (int pos = 1; pos <= KEYNUM_31; pos++)	
	{
		for (int i = 0; i < size; i++)		
		{
			int num = GetNumInPos(array[i], pos);
			int index = ++array1[num][0];
			array1[num][index] = array[i];
		}
		for (int i = 0, j = 0; i < RADIX_10; i++)	
		{
			for (int k = 1; k <= array1[i][0]; k++)
				array[j++] = array1[i][k];
			array1[i][0] = 0;	
		}
	}
}

int main()
{
	int array[] = { 8,3,2,1,9,5,6,0,4,7 };
	int size = sizeof(array)/sizeof(array[0]);
	printf("原始数据:\n");
	Printf(array, size);

    //基数排序
	Radix_Sort(array, size);
	printf("基数排序后的数据:\n");
	Printf(array, size);

	system("pause");
	return 0;
}


 

猜你喜欢

转载自blog.csdn.net/qq_43210641/article/details/89438674