排序算法(Sorting algorithm)(二)

写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

 

接上一篇:排序算法(Sorting algorithm)(一)

 

目录

6、快速排序(Quick Sort)

7、堆排序(Heap Sort)

8、计数排序(Counting Sort)

9、桶排序(Bucket Sort)

10、基数排序(Radix Sort)

四、十大经典排序算法 C语言总代码实现


 

6、快速排序(Quick Sort)

快速排序(英语:Quick Sort),又称分区交换排序(partition-exchange sort),简称快排,是对冒泡排序的一种改进,亦是分而治之思想在排序算法上的典型应用。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列的目的。

6.1 执行流程:

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

排序算法(Sorting algorithm)(二)_第1张图片

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

6.2 动图演示:

排序算法(Sorting algorithm)(二)_第2张图片

6.3 代码实现:

void Quick_Sort(int *arr, int low, int high)
{
    int i = low;
    int j = high;
    int pivot = arr[i];

    if(low > high) {
        return ;
    }
    while(i < j) {
        while((arr[j] >= pivot) && (i < j)) {
            j--;
        }
        arr[i] = arr[j];
        while((arr[i] <= pivot) && (i < j)) {
            i++;
        }
        arr[j]= arr[i];
    }
    arr[i] = pivot;
    Quick_Sort(arr, low, i-1);
    Quick_Sort(arr, j+1, high);
}

7、堆排序(Heap Sort)

堆排序(英语:Heap Sort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。

排序算法(Sorting algorithm)(二)_第3张图片

7.1 执行流程:

  • 将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
  • 将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
  • 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

假设给定无序序列结构如下(以大顶堆为例):

排序算法(Sorting algorithm)(二)_第4张图片

1) 构造大顶堆

a. 从最后一个非叶子结点开始,从左至右,从下至上进行调整。叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的 6结点;在[6, 5, 9]这个小堆里边,父节点 97最大,所以 6和 9交换。

排序算法(Sorting algorithm)(二)_第5张图片

b. 找到第二个非叶节点 4,由于[4, 9, 8]中 9元素最大,4和 9交换。

排序算法(Sorting algorithm)(二)_第6张图片

c. 这时,交换导致了子根[4, 5, 6]结构混乱,继续调整,在[4, 5, 6]这个小堆里边,父节点 6最大,所以 4和 6交换。

排序算法(Sorting algorithm)(二)_第7张图片

至此,一个无序序列已经构造成一个大顶堆。

2) 堆排序

a. 将堆顶元素 9和末尾元素 4进行交换

排序算法(Sorting algorithm)(二)_第8张图片

b. 重新调整结构,使其继续满足堆定义

排序算法(Sorting algorithm)(二)_第9张图片

c. 再将堆顶元素 8与末尾元素 5进行交换,得到第二大元素 8

排序算法(Sorting algorithm)(二)_第10张图片

d. 后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

排序算法(Sorting algorithm)(二)_第11张图片

7.2 动图演示:

排序算法(Sorting algorithm)(二)_第12张图片

7.3 代码实现:

void swap(int *a, int *b)
{
    int temp = *b;
    *b = *a;
    *a = temp;
}

void Heapify(int *arr, int start, int end)
{
#if 1
    int i, temp;

    temp = arr[start];
    for (i = 2*start + 1; i < end; i = 2*i + 1) {// 左孩子 2*i + 1,右孩子 2*i + 2
        if (i + 1 < end && arr[i] < arr[i+1]) {// 如果左子结点小于右子结点,i指向右子结点
            i++;
        }
        if (temp < arr[i]) {
            arr[start] = arr[i];// 将根节点设置为子节点的较大值
            start = i;// 继续往下
        } else
            break;// 已经满足大根堆
    }
    arr[start] = temp;// 将temp值放到最终的位置

#else
    // 建立父節點指標和子節點指標
    int dad = start;
    int son = dad * 2 + 1;

    while (son < end) { // 若子節點指標在範圍內才做比較
        if (son + 1 < end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
            son++;
        if (arr[dad] >= arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
            return;
        else { // 否則交換父子內容再繼續子節點和孫節點比較
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }

#endif
}

void Heap_Sort(int *arr, int len)
{
    int i;

	// 先将数组构造成大顶堆
    for (i = len / 2 - 1; i >= 0; i--)
        Heapify(arr, i, len);// 从第一个非叶子结点从下至上,从右至左调整结构

    // 调整堆结构+交换堆顶元素与末尾元素
    for (i = len - 1; i > 0; i--) {
        swap(&arr[0], &arr[i]);// 将堆顶元素与末尾元素进行交换
        Heapify(arr, 0, i);// 重新对堆进行调整
	}
}

8、计数排序(Counting Sort)

计数排序(Counting Sort)是一种稳定的线性时间排序算法。该算法于1954年由 Harold H. Seward 提出。比较适合数值跨度比较小的,  也就是数组中最大值减去最小值得到的值尽量小,  同时数组元素又比较多的情况下用计数排序效率比较高。计数排序使用一个额外的数组 C,其中第 i个元素是待排序数组 A中值等于 i的元素的个数。然后根据数组 C来将 A中的元素排到正确的位置。

8.1 执行流程:

  • 找出待排序的数组中最大的元素;
  • 设置一个定量的数组 counts,最大范围为 max(max为步骤 1求得的最大值);
  • 统计数组中每个值为 i的元素出现的次数,存入数组 counts的第 i项;
  • 对所有的计数累加(从 counts中的第一个元素开始,每一项和前一项相加);
  • 反向填充目标数组:将每个元素 i放在新数组的第 counts[i]项,每放一个元素就将 counts[i]减去 1。

8.2 动图演示:

8.3 代码实现:

int Counting_Sort(DataType *arr, int len)
{
    DataType *counts,
             *temp;

    int i;
    int max_val = arr[0];

    for(i = 1; i < len; i++)
        if(arr[i] > max_val)
            max_val = arr[i];

    /*为计数器数组分配空间*/
    counts = (DataType *)malloc((max_val+1) * sizeof(DataType));
    /*为已排序元素临时存放数组分配空间*/
    temp = (DataType *)malloc(len * sizeof(DataType));

    if(counts == NULL)
        return -1;

    if(temp == NULL)
        return -1;

    /* 初始化计数数组 */
    for(i = 0; i < max_val+1; i++)
        counts[i] = 0;

    /* 统计每个元素出现的次数(counts的下标索引即是要统计的元素本身)*/
    for(i = 0; i < len; i++)
        counts[arr[i]]++;

    /* 将元素本身的次数加上它前一个元素的次数(得到元素偏移量) */
    for(i = 1; i < max_val+1; i++)
        counts[i] += counts[i - 1];

    /* 关键代码:使用上面得到的计数数组去放置每个元素要排序的位置 */
    for(i = len - 1; i >= 0; i--) {
        temp[counts[arr[i]] -1] = arr[i];      /* counts的值是元素要放置到 temp中的偏移量 */
        counts[arr[i]]--;  /* counts的计数减 1 */

//        temp[--counts[arr[i]]] = arr[i];
    }
	
    /* 将 Counting_Sort已排序的元素从 temp拷贝回 data */
    memcpy(arr, temp, len * sizeof(DataType));

    /*释放前面分配的空间*/
    free(counts);
    free(temp);

    return 0;
}

9、桶排序(Bucket Sort)

桶排序(Bucket Sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。

为了使桶排序更加高效,我们需要做到这两点:

  • 在额外空间充足的情况下,尽量增大桶的数量
  • 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

9.1 执行流程:

  • 找出待排序数组 arr中的最大值 max、最小值 min;
  • 设置一个定量的数组当作空桶,范围为 min~max(min、max为步骤 1求得的最小值、最大值);
  • 遍历待排序数组 arr,计算每个元素 arr[i]放的桶,把数据放到对应的桶里;
  • 如果桶不为空,对桶中的数据进行排序;
  • 遍历桶数组,把所有桶中排序好的元素放到一个新的数组里。

9.2 动图演示:

排序算法(Sorting algorithm)(二)_第13张图片

9.3 代码实现:

int Bucket_Sort(DataType *arr, int len)
{
    int i, j, n;
    DataType *buckets;

    int max_val = arr[0];
	int min_val = arr[0];

    for(i = 1; i < len; i++) {
        if(arr[i] > max_val)
            max_val = arr[i];
        else if(arr[i] < min_val)
            min_val = arr[i];
    }
	
    n = max_val - min_val + 1;

    buckets = (DataType *)malloc((n) * sizeof(DataType));

    if(buckets == NULL)
        return -1;

    memset(buckets, 0, (n) * sizeof(DataType));

    for (i = 0; i < len; i++)
        buckets[arr[i] - min_val]++;

    for (i = 0, j = 0; i < n; i++)
        while((buckets[i]--) > 0)
            arr[j++] = i + min_val;
	
    return 0;
}

10、基数排序(Radix Sort)

基数排序(英语:Radix Sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

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

基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

10.1 执行流程:

  • 取得数组中的最大数,并取得位数;
  • arr为原始数组,从最低位开始取每个位组成 radix数组;
  • 对 radix进行计数排序(利用计数排序适用于小范围数的特点)。

10.2 动图演示:

10.3 代码实现:

#define BASE            10

int Radix_Sort(DataType *arr, int len)
{
    DataType *temp,
	         *counts; //计数器
    int i, j, k;
    int radix = 1;

    int max_val = arr[0];

    for(i = 1; i < len; i++)
        if(arr[i] > max_val)
            max_val = arr[i];

	/* 为计数器数组分配空间 */
    counts = (DataType *)malloc((BASE+1) * sizeof(DataType));
    /* 为已排序元素临时存放数组分配空间 */
    temp = (DataType *)malloc(len * sizeof(DataType));

    if(counts == NULL)
        return -1;

    if(temp == NULL)
        return -1;

    while (max_val / radix > 0) {
        for(j = 0; j < BASE+1; j++)
            counts[j] = 0; // 每次分配前清空计数器

        for(j = 0; j < len; j++) {
            k = (arr[j] / radix) % BASE; // 统计每个桶中的记录数
            counts[k]++;
        }

        for(j = 1; j < BASE+1; j++)
            counts[j] += counts[j - 1]; // 将 temp中的位置依次分配给每个桶

        for(j = len - 1; j >= 0; j--) { // 将所有桶中记录依次收集到 temp中
            k = (arr[j] / radix) % BASE;
            temp[counts[k] - 1] = arr[j];
            counts[k]--;
        }

        for(j = 0; j < len; j++) // 将临时数组的内容复制到 data中
            arr[j] = temp[j];

        radix *= 10;
    }

    /*释放前面分配的空间*/
    free(counts);
    free(temp);

    return 0;
}

 

四、十大经典排序算法 C语言总代码实现

/* 排序算法 */

#include
#include
#include

typedef int DataType;

#define SIZEOF(x,y)		(sizeof(x)/sizeof(y))
#define LEN				SIZEOF(buffer,DataType)

int buffer[10] = {3,6,13,1,78,35,22,11,32,60};

void Bubble_Sort(DataType *arr, int len)
{
	int i, j, temp;

	for (i = 0; i < len - 1; i++)
		for (j = 0; j < len - 1 - i; j++)
			if (arr[j] > arr[j + 1]) {
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
}

void Selection_Sort(DataType *arr, int len)
{
	int i, j, k, temp;

	for (i = 0; i < len - 1; i++) {
		k = i;
		for (j = i + 1; j < len; j++)
			if (arr[j] < arr[k])
				k = j;
		if(k != i) {
			temp = arr[k];
			arr[k] = arr[i];
			arr[i] = temp;
		}
	}
}

void Insertion_Sort(DataType *arr, int len)
{
	int i, j, temp;

	for (i = 1; i < len; i++)
		if (arr[i] < arr[i-1]) {
			temp = arr[i];
			for (j = i - 1; j >= 0 && arr[j] > temp; j--)
				arr[j+1] = arr[j];
			arr[j+1] = temp;
		}
}

void Shell_Sort(DataType *arr, int len)
{
	int gap, i, j;
	int temp;

	for (gap = len >> 1; gap > 0; gap >>= 1)
		for (i = gap; i < len; i++) {
			temp = arr[i];
			for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
				arr[j + gap] = arr[j];
			arr[j + gap] = temp;
		}
}

#define MIN(x,y)     ((x) < (y) ? (x) : (y))

int Merge_Sort(DataType *arr, int len)
{
	DataType *a = arr;
	DataType *b = (DataType *) malloc(len * sizeof(DataType));
	int seg, start;

	if(b == NULL)
		return -1;

	for (seg = 1; seg < len; seg += seg) {
		for (start = 0; start < len; start += seg * 2) {
			int low = start, mid = MIN(start + seg, len), high = MIN(start + seg * 2, len);
			int k = low;
			int start1 = low, end1 = mid;
			int start2 = mid, end2 = high;
			while (start1 < end1 && start2 < end2)
				b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
			while (start1 < end1)
				b[k++] = a[start1++];
			while (start2 < end2)
				b[k++] = a[start2++];
		}
		DataType *temp = a;
		a = b;
		b = temp;
	}
	if (a != arr) {
		int i;
		for (i = 0; i < len; i++)
			b[i] = a[i];
		b = a;
	}
	free(b);

	return 0;
}

void Quick_Sort(DataType *arr, int low, int high)
{
	int i = low;
	int j = high;
	int pivot = arr[i];

	if(low > high) {
		return ;
	}
	while(i < j) {
		while((arr[j] >= pivot) && (i < j)) {
			j--;
		}
		arr[i] = arr[j];
		while((arr[i] <= pivot) && (i < j)) {
			i++;
		}
		arr[j]= arr[i];
	}
	arr[i] = pivot;
	Quick_Sort(arr, low, i-1);
	Quick_Sort(arr, j+1, high);
}

void swap(DataType *a, DataType *b)
{
	DataType temp = *b;
	*b = *a;
	*a = temp;
}

void Heapify(DataType *arr, int start, int end)
{
#if 0
	int i, temp;

	temp = arr[start];
	for (i = 2*start + 1; i < end; i = 2*i + 1) {// 左孩子 2*i + 1,右孩子 2*i + 2
		if (i + 1 < end && arr[i] < arr[i+1]) {// 如果左子结点小于右子结点,i指向右子结点
			i++;
		}
		if (temp < arr[i]) {
			arr[start] = arr[i];// 将根节点设置为子节点的较大值
			start = i;// 继续往下
		} else
			break;// 已经满足大根堆
	}
	arr[start] = temp;// 将temp值放到最终的位置

#else
	// 建立父節點指標和子節點指標
	int dad = start;
	int son = dad * 2 + 1;

	while (son < end) { // 若子節點指標在範圍內才做比較
		if (son + 1 < end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
			son++;
		if (arr[dad] >= arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
			return;
		else { // 否則交換父子內容再繼續子節點和孫節點比較
			swap(&arr[dad], &arr[son]);
			dad = son;
			son = dad * 2 + 1;
		}
	}

#endif
}

void Heap_Sort(DataType *arr, int len)
{
	int i;

	// 先将数组构造成大顶堆
	for (i = len / 2 - 1; i >= 0; i--)
		Heapify(arr, i, len);// 从第一个非叶子结点从下至上,从右至左调整结构

	// 调整堆结构+交换堆顶元素与末尾元素
	for (i = len - 1; i > 0; i--) {
		swap(&arr[0], &arr[i]);// 将堆顶元素与末尾元素进行交换
		Heapify(arr, 0, i);// 重新对堆进行调整
	}
}

int Counting_Sort(DataType *arr, int len)
{
	DataType *counts,
	         *temp;

	int i;
	int max_val = arr[0];

	for(i = 1; i < len; i++)
		if(arr[i] > max_val)
			max_val = arr[i];

	/*为计数器数组分配空间*/
	counts = (DataType *)malloc((max_val+1) * sizeof(DataType));
	/*为已排序元素临时存放数组分配空间*/
	temp = (DataType *)malloc(len * sizeof(DataType));

	if(counts == NULL)
		return -1;

	if(temp == NULL)
		return -1;

	/* 初始化计数数组 */
	for(i = 0; i < max_val+1; i++)
		counts[i] = 0;

	/* 统计每个元素出现的次数(counts的下标索引即是要统计的元素本身)*/
	for(i = 0; i < len; i++)
		counts[arr[i]]++;

	/* 将元素本身的次数加上它前一个元素的次数(得到元素偏移量) */
	for(i = 1; i < max_val+1; i++)
		counts[i] += counts[i - 1];

	/* 关键代码:使用上面得到的计数数组去放置每个元素要排序的位置 */
	for(i = len - 1; i >= 0; i--) {
		temp[counts[arr[i]] -1] = arr[i];      /* counts的值是元素要放置到 temp中的偏移量 */
		counts[arr[i]]--;  /* counts的计数减 1 */

//		temp[--counts[arr[i]]] = arr[i];
	}

	/* 将 Counting_Sort已排序的元素从 temp拷贝回 data */
	memcpy(arr, temp, len * sizeof(DataType));

	/*释放前面分配的空间*/
	free(counts);
	free(temp);

	return 0;
}

int Bucket_Sort(DataType *arr, int len)
{
	int i, j, n;
	DataType *buckets;

	int max_val = arr[0];
	int min_val = arr[0];

	for(i = 1; i < len; i++) {
		if(arr[i] > max_val)
			max_val = arr[i];
		else if(arr[i] < min_val)
			min_val = arr[i];
	}

	n = max_val - min_val + 1;

	buckets = (DataType *)malloc((n) * sizeof(DataType));

	if(buckets == NULL)
		return -1;

	memset(buckets, 0, (n) * sizeof(DataType));

	for (i = 0; i < len; i++)
		buckets[arr[i] - min_val]++;

	for (i = 0, j = 0; i < n; i++)
		while((buckets[i]--) > 0)
			arr[j++] = i + min_val;

	return 0;
}

#define BASE            10

int Radix_Sort(DataType *arr, int len)
{
	DataType *temp,
	         *counts; //计数器
	int i, j, k;
	int radix = 1;

	int max_val = arr[0];

	for(i = 1; i < len; i++)
		if(arr[i] > max_val)
			max_val = arr[i];

	/*为计数器数组分配空间*/
	counts = (DataType *)malloc((BASE+1) * sizeof(DataType));
	/*为已排序元素临时存放数组分配空间*/
	temp = (DataType *)malloc(len * sizeof(DataType));

	if(counts == NULL)
		return -1;

	if(temp == NULL)
		return -1;

	while (max_val / radix > 0) {
		for(j = 0; j < BASE+1; j++)
			counts[j] = 0; // 每次分配前清空计数器

		for(j = 0; j < len; j++) {
			k = (arr[j] / radix) % BASE; // 统计每个桶中的记录数
			counts[k]++;
		}

		for(j = 1; j < BASE+1; j++)
			counts[j] += counts[j - 1]; // 将 temp中的位置依次分配给每个桶

		for(j = len - 1; j >= 0; j--) { // 将所有桶中记录依次收集到 temp中
			k = (arr[j] / radix) % BASE;
			temp[counts[k] - 1] = arr[j];
			counts[k]--;
		}

		for(j = 0; j < len; j++) // 将临时数组的内容复制到 data中
			arr[j] = temp[j];

		radix *= 10;
	}

	/*释放前面分配的空间*/
	free(counts);
	free(temp);

	return 0;
}


int main(void)
{
	int i;

//	Bubble_Sort(buffer, LEN);
//	Selection_Sort(buffer, LEN);
//	Insertion_Sort(buffer, LEN);
//	Shell_Sort(buffer, LEN);
//	Merge_Sort(buffer, LEN);
//	Quick_Sort(buffer, 0, LEN-1);
//	Heap_Sort(buffer, LEN);
//	Counting_Sort(buffer, LEN);
//	Bucket_Sort(buffer, LEN);
//	Radix_Sort(buffer, LEN);


	for(i = 0; i < LEN; i++)
		printf("buffer[%d]=%3d\n", i, *(buffer+i));

	return 0;
}


/* -------------------- END OF FILE -------------------- */

 

你可能感兴趣的:(算法)