算法训练营【day8】:: 排序篇(C语言版):希尔排序:分析、实现与总结

本内容是笔者结合《数据结构(C语言版)》(严蔚敏)总结所得,记录学习过程,分享知识!


目录:
1. 希尔排序分析:基本有序!
- - 1.1 基本有序思路的说明
- - 1.2 有序化处理流程(图示)
2. 希尔排序实现
- - 2.1 间隔式调整模拟实现
- - 2.1.1 写法一:双for循环 + while循环 写法
- - 2.1.2 写法二:单层for循环 + while循环 写法
- - 2.2 希尔排序实现
- - 2.3 可执行完整程序代码及结果展示
3. 希尔排序总结
4. 结语及相关推荐(排序汇总)


1. 希尔排序分析:基本有序!

紧接上篇《算法训练营【day8】:: 排序篇(C语言版):插入排序:分析、实现与总结》
前文曾提及:插入排序的优化方案是:使原序列尽可能有序程度高!


如何提升这种有序程度呢?


~ 在插入排序中我们提及了打麻将示例,此处我们再引用试试,麻将牌分为万、饼、条等牌种,每种都有 1~9 的点数牌且都有四张!持牌者通常会将同牌种的牌集中放置(且有序)如下图。此时在插入新摸到的手牌进行对应插入是不是比则乱拜访来的更方便!

在这里插入图片描述

那问题来了,对于一个序列而言,我们是否可以采用类似预有序化思路实现呢?


为了实现有序化程度高(或者说是基本有序)我们尝试以下思路:


~ 分块有序化尝试:
对于序列:{ 9,1,5 ,8、3、7,4,6,2}
若使用分块形式的预处理:{ 9,1,5 }、{8、3、7}、{4,6,2};
再有序化:结果:{1,5,9}、{3,7,8}、{2,4,6};
此时我们发现,若将其合并相比于原序列显然没有基本有序的明显提升!

1.1 基本有序思路的说明

注意:基本有序是指:实现小值无论顺序基本在前,大值同理基本在后,不大不小的又基本在中间部分!
例如
原序列:{ 9,1,5 ,8、3、7,4,6,2};
基本有序序列:{1,3,2,5,3,4,9,8,7};
基本有序处理后,在使用插入排序会有明显的效率提升!

在这里插入图片描述

实现基本有序的方式:希尔排序:

  1. 对原序列进行间隔分组(如上图中同种颜色为同组)【若分块式分组无法实现基本有序】
  2. 在分组内进行跳跃式组内排序
  3. 最终在知道间隔减小到一,即为相邻情形实现排序!
    (推荐看图理解)
1.2 有序化处理流程(图示)

在这里插入图片描述

2. 插入排序实现

实现流程:

  1. 间隔式调整实现基本有序;
  2. 调整间隔大小反复调整;
  3. 直到间距为:1,实现排序。
2.1 间隔式调整模拟实现
2.1.1 写法一:双for循环 + while循环 写法

看代码注释:

// 注意循环条件,实际上间隔式组内也是使用插入排序方式调整;
// 分组后的好处:减小了遍历范围,提高效率
for (int j = 0; j < gap; ++j) {
	for (int i = j; i < size - gap; i += gap) {
		// 单次希尔插入排序操作
		int end = i;
		DataType temp = array[end + gap];				// 存储等间隔处理的数
		while (end >= 0) {
			if (temp < array[end]) {
				array[end + gap] = array[end];
				end -= gap;
			}
			else break;
		}
		array[end + gap] = temp;
	}
}
2.1.2 写法二:单层for循环 + while循环 写法
for (int i = 0; i < size - gap; i++) {
	// 单次希尔插入排序操作
	int end = i;
	DataType temp = array[end + gap];
	while (end >= 0) {
		if (temp > array[end]) {
			array[end + gap] = array[end];
			end -= gap;
		}
		else break;
	}
	array[end + gap] = temp;
}

2.2 希尔排序实现

希尔排序的注意要点:

  1. 间隔:gap 的持续调整!
  2. 注意:gap 的最小值问题:最小值:1
写法一:双for循环 + while循环 写法
void ShellSort1(DataType* array, int size) {
	int gap = size;					// 希尔处理的间隔步数

	while (gap > 1) {
		gap = gap / 3 + 1;		// 此处间隔调整:注意:+1 确保最小值为:1
		for (int j = 0; j < gap; ++j) {
			for (int i = j; i < size - gap; i += gap) {
				// 单次希尔插入排序操作
				int end = i;
				DataType temp = array[end + gap];	// 存储等间隔处理的数
				while (end >= 0) {
					if (temp < array[end]) {
						array[end + gap] = array[end];
						end -= gap;
					}
					else break;
				}
				array[end + gap] = temp;
			}
		}
	}
}
写法二:单层for循环 + while循环 写法
void ShellSort2(DataType* array, int size) {
	int gap = size;

	while (gap > 1) {
		gap = gap / 3 + 1;	// 此处间隔调整:注意:+1 确保最小值为:1
		for (int i = 0; i < size - gap; i++) {
			// 单次希尔插入排序操作
			int end = i;
			DataType temp = array[end + gap];
			while (end >= 0) {
				if (temp > array[end]) {
					array[end + gap] = array[end];
					end -= gap;
				}
				else break;
			}
			array[end + gap] = temp;
		}
	}
}

2.3 可执行完整程序代码及结果展示
#pragma

/* 本篇内容将实现:C 语言版顺序存储:希尔排序 */
#include<stdio.h>

typedef char DataType;

/* 打印函数 */
void PrintArray1(DataType* array, int size);
void PrintArray2(DataType* array, int size);


/* 顺序存储希尔排序: */
/* 参数说明:被操作数组;数组大小 */
/* 写法一:双for循环 + while循环 写法 */
void ShellSort1(DataType* array, int size);

/* 写法二:单层for循环 + while循环 写法 */

void ShellSort2(DataType* array, int size);

/* 打印函数 */
void PrintArray1(DataType* array, int size) {
	printf("array:");
	for (int i = 0; i < size; i++)
		printf("%d ", array[i]);
	printf("\n");
}

void PrintArray2(DataType* array, int size) {
	printf("array:");
	for (int i = 0; i < size; i++)
		printf("%c ", array[i]);
	printf("\n");
}

/* 写法一:双for循环 + while循环 写法 */
void ShellSort1(DataType* array, int size) {
	int gap = size;									// 希尔处理的间隔步数

	while (gap > 1) {
		gap = gap / 3 + 1;
		for (int j = 0; j < gap; ++j) {
			for (int i = j; i < size - gap; i += gap) {
				// 单次希尔插入排序操作
				int end = i;
				DataType temp = array[end + gap];				// 存储等间隔处理的数
				while (end >= 0) {
					if (temp < array[end]) {
						array[end + gap] = array[end];
						end -= gap;
					}
					else break;
				}
				array[end + gap] = temp;
			}
		}
	}
}

/* 写法二:单层for循环 + while循环 写法 */
void ShellSort2(DataType* array, int size) {
	int gap = size;

	while (gap > 1) {
		gap = gap / 3 + 1;
		for (int i = 0; i < size - gap; i++) {
			// 单次希尔插入排序操作
			int end = i;
			DataType temp = array[end + gap];
			while (end >= 0) {
				if (temp > array[end]) {
					array[end + gap] = array[end];
					end -= gap;
				}
				else break;
			}
			array[end + gap] = temp;
		}
	}
}

void test_ShellSort() {
#if 1
	printf("============================================================\n");
	printf("整型测试用例:\n");
	int a[] = { 5,3,6,44,8,2,2,64,85,16,48,46,846,4,6843,54,846,16,48,4,1,64,684,6,46 };
	int size = sizeof(a) / sizeof(int);
	PrintArray1(&a, size);
	ShellSort1(&a, size);
	PrintArray1(&a, size);
	ShellSort2(&a, size);
	PrintArray1(&a, size);

#else
	printf("============================================================\n");
	printf("字符型测试用例(注:需将 typedef int DataType; 改成:typedef char DataType;才可正常):\n");
	char b[] = "qwertyuiopasdfghjklzxcvbnm";
	int size = sizeof(b) / sizeof(char);
	PrintArray2(b, size);
	ShellSort1(b, size);
	PrintArray2(b, size);
	ShellSort2(b, size);
	PrintArray2(b, size);
#endif
}

int main() {
	test_ShellSort();

	return 0;
}
测试结果展示:

在这里插入图片描述
在这里插入图片描述


3. 希尔排序总结

关于时间复杂度:O(N^1.3)
这求证方式为网上参考资料!

在这里插入图片描述

希尔排序特点:
排序时:

  1. gap(间隔)越大,大的数据更快到后面,小的数更快到前面,但是越不接近有序;
  2. gap(间隔)越小,越接近有序,当 gap == 1 时,就是直接插入排序。

4. 结语及相关推荐(排序汇总)

排序汇总博客
排序算法代码汇总(gitee链接)
排序算法代码汇总(github链接)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言中,分治算法是一种常见的算法设计思想,它将一个问题分解为规模较小的子问题来求解,通常包括两个步骤:分解(divide)和合并(combine)。这里我们将分别介绍如何使用C语言实现合并排序和快速排序这两种经典的分治算法。 ### 合并排序(Merge Sort) **合并排序**的基本思想是将待排序的数组分成两半,对每一半递归地进行排序,然后合并两个已排序的部分。以下是C语言实现: ```c #include <stdio.h> // 合并两个已排序的数组 void merge(int arr[], int l, int m, int r) { int i, j, k; int n1 = m - l + 1; int n2 = r - m; // 创建临时数组 int L[n1], R[n2]; // 将数据复制到临时数组 for (i = 0; i < n1; i++) L[i] = arr[l + i]; for (j = 0; j < n2; j++) R[j] = arr[m + 1+ j]; // 合并临时数组 i = 0; j = 0; k = l; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } // 如果L数组还有剩余元素 while (i < n1) { arr[k] = L[i]; i++; k++; } // 如果R数组还有剩余元素 while (j < n2) { arr[k] = R[j]; j++; k++; } } // 分治法:合并排序 void mergeSort(int arr[], int l, int r) { if (l < r) { int m = l+(r-l)/2; mergeSort(arr, l, m); mergeSort(arr, m+1, r); merge(arr, l, m, r); } } int main() { int arr[] = {12, 11, 13, 5, 6, 7}; int arr_size = sizeof(arr) / sizeof(arr); mergeSort(arr, 0, arr_size - 1); printf("Sorted array: \n"); for (int i = 0; i < arr_size; i++) printf("%d ", arr[i]); return 0; } ``` **相关问题**: 1. 除了插入排序,还有哪些常用的分治算法? 2. 分治策略在其他数据结构算法中有哪些应用? 3. 为什么要对数组进行分区操作? ### 快速排序(Quick Sort) **快速排序**是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,直到整个序列有序。以下是C语言实现: ```c #include <stdio.h> // 交换两个元素 void swap(int* a, int* b) { int t = *a; *a = *b; *b = t; } // 选择枢轴,这里选择第一个元素作为枢轴 int partition(int arr[], int low, int high) { int pivot = arr[low]; int i = (low + 1), j = high; while (i <= j) { while (i <= j && arr[i] < pivot) i++; while (i <= j && arr[j] > pivot) j--; if (i < j) swap(&arr[i], &arr[j]); } swap(&arr[low], &arr[j]); return j; } // 快速排序 void quickSort(int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } int main() { int arr[] = {10, 7, 8, 9, 1, 5}; int arr_size = sizeof(arr) / sizeof(arr); quickSort(arr, 0, arr_size - 1); printf("Sorted array: \n"); for (int i = 0; i < arr_size; i++) printf("%d ", arr[i]); return 0; } ``` **相关问题**: 1. 快速排序的平均时间复杂度是多少? 2. 快速排序与归并排序相比,哪种更适合处理大规模数据? 3. 快速排序在什么情况下可能会退化成O(n^2)的时间复杂度?

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NPC的白话文谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值