详谈排序算法之插入类排序(两种思路实现希尔排序)

1. 排序( sorting)
的功能是将一个数据元素的任意序列,重新排列成一个按关键字有序的序列。其确切的定义为:

假设有n个数据元素的序列{R1 ,
R2 ,
… , Rn},其相应关键字的序列是{K1 ,
K2 ,
… , Kn} ,通过排序要求找出下标 1
, 2 , … , n的一种排列p1 ,
p2 ,
… , pn,使得相应关键字满足如下的非递减(或非递增)关系Kp1 ≤
Kp2 ≤
… ≤ Kpn这样,就得到一个按关键字有序的纪录序列{
Rp1 , Rp2 ,
… , Rpn }。

根据排序时待排序的数据元素数量的不同,使得排序过程中涉及的存储器不同,可以将排序方法分为两类。一类是整个排序过程在内存储器中进行,称为内部排序;另一类是由于待排序元素数量太大,以至于内存储器无法容纳全部数据,排序需要借助外部存储设备才能完成,这类排序称为外部排序。

2.
通常,在排序的过程中需要进行两种基本操作: ⑴比较两个关键字的大小; ⑵将一个关键字从一个位置移动到另一个位置。第一种操作对大多数排序方法来说都是必要的,而后一种操作可以通过改变记录的存储方式来予以避免。

   在讨论排序算法之前,我们必须要提及一下我们的前提:

   1).大多数情况下为简单起见,讨论从小到大的整数排序。

   2).只讨论基于比较的排序,即 > , < , = 均是有定义的。

   3).只讨论内部排序。

   4).稳定性:任意两个相等的数据,排序前后的相对位置不发生改变。

   5).没有一种排序在任何情况下的都是表现最好的,所以才有了如此多的排序算法。

3.直接插入排序

直接插入排序是一种最简单的插入排序方法,它的基本思想是:仅有一个元素的序列总是有序的因此对n个记录的序列,可从第二个元素开始直到第
n 个元素,逐个向有序序列中执行插入操作,从而得到n个元素按关键字有序的序列。

一般来说,在含有 j-1 个元素的有序序列中插入一个元素的方法是:从第 j-1 个元素开 始依次向前搜索应当插入的位置,并且在搜索插入位置的同时可以后移元素,这样当找到适 当的插入位置时即可直接插入元素。

以关键字序列{
26 , 53 , 48 , 11 , 13 , 48 , 32 , 15}为例:

public void Insert_Sort(int A[], int N) {
		for (int P = 1; P < N; P++) {
			int temp = A[P];
			int i = P;
			for (; i > 0 && A[i - 1] > temp; i--)
				A[i] = A[i - 1];//留出空位
			A[i] = temp;//赋值
		}
		for(int i = 0 ; i < N; i++){
			System.out.print(A[i] + " ");
		}
	}

【效率分析】

空间效率: 仅使用一个辅存单元。

时间效率: 假设待排序的元素个数为 n,则向有序表中逐个插入记录的操作进行了 n-1趟,每趟操作分为比较关键码和移动记录,而比较的次数和移动记录的次数取决于待排序列按关键码的初始排列。

⑴ 在最好情况下,即待排序序列已按关键字有序,每趟操作只需 1 次比较 0 次移动。此时有:

总比较次数 = n-1 次

总移动次数 = 0 次

⑵ 在最坏情况下,即待排序序列按关键字逆序排序,这时在第 j 趟操作中,为插入元素需要同前面的 j 个元素进行 j 次关键字比较,移动元素的次数为 j+1 次。此时有:

总比较次数 = = n(n-1)/2 次

⑶ 平均情况下:即在第 j 趟操作中,插入记录大约需要同前面的 j/2 个元素进行关键字

比较,移动记录的次数为 j/2+1 次。此时有:

总比较次数 ≈ n2/4 次

总移动次数 ≈ n2/4 次

由此,直接插入排序的时间复杂度为O(n2),并且是一个稳定的排序方法。

4.希尔排序

希尔排序又称为“缩小增量排序”,它也是一种属于插入排序类的排序方法,是一种对直接插入排序的改进,但在时间效率上却有较大的改进。从对直接插入排序的分析中知道,虽然直接插入排序的时间复杂度为O(n2),但是在待排序元素序列有序时,其时间复杂度可提高至O(n)。

由此可知在待排序元素基本有序时, 直接插入排序的效率可以大大提高。从另一方面看,由于直接插入排序方法简单,则在n值较小时效率也较高。希尔排序正是从这两点出发,对直接插入排序进行改进而得到的一种排序方法。

希尔排序的基本思想是:首先将待排序的元素分为多个子序列,使得每个子序列的元素个数相对较少,对各个子序列分别进行直接插入排序,待整个待排序序列“基本有序”后,再对所有元素进行一次直接插入排序。

根据上述排序思想,下面我们给出希尔排序的排序过程:

⑴ 选择一个步长序列t1, t2, …, tk,其中ti>tj( i<j), tk=1 ;

⑵ 按步长序列个数 k,对待排序元素序列进行 k 趟排序;

⑶ 每趟排序,根据对应的步长ti,将待排序列分割成ti个子序列,分别对各子序列

进行直接插入排序。

      

·在每趟排序过程中子序列的划分并不是简单的逐段划分,而是将间隔某个步长的元素组成一个子序列。如此,在对每个子序列进行简单插入排序时,关键字较小的元素就不是一步一步向前移动,而是按步长跳跃式向前移动,从而使得在进行最后一趟步长为 1 的插入排序时,整个序列已基本有序,此时,只需要作比较少的比较和移动即可完成排序。

public void Shell_Sort1(int[] A, int N) {
		int temp;
		for (int k = N / 2; k > 0; k /= 2) {// 希尔增量序列
			for (int i = k; i < N; i++) {// 每相隔k/2个增量进行操作
				for (int j = i; j >= k; j -= k) {
					if(A[j] < A[j - k]){
						temp = A[j - k];
						A[j - k] = A[j];
						A[j] = temp;
					}
				}
			}
		}
		for(int i = 0 ; i < N; i++){
			System.out.print(A[i] + " ");
		}
	}
public void Shell_Sort2(int[] A, int N) {
		for (int k = N / 2; k > 0; k /= 2) {// 希尔增量序列
			for (int i = k; i < N; i++) {// 插入排序
				int temp = A[i];
				int j = i;
				for (; j >= k && A[j - k] > temp; j -= k)
					A[j] = A[j - k];
				A[j] = temp;
			}
		}
		for(int i = 0 ; i < N; i++){
			System.out.print(A[i] + " ");
		}
	}

通过前面的分析,从直观上我们可以预见希尔排序的效率会较直接插入排序要高,然而对希尔排序的时间复杂度分析是一个复杂的问题,因为希尔排序的时间复杂度与步长序列的选取密切相关,如何选择步长序列才能使得希尔排序的时间复杂度达到最佳,这还是一个有待解决的问题。实际的应用中,在选择步长序列时应当注意:应使步长序列中的步长值互质,并且最后一个步长值必须等于 1。

5.后面我们将继续讨论排序算法··交换类排序

时间: 2024-11-07 06:34:08

详谈排序算法之插入类排序(两种思路实现希尔排序)的相关文章

排序算法 插入排序(直接插入排序、半插入排序、希尔排序)

一.直接插入排序. 1.介绍. 直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列.例如,已知待排序的一组记录是:60,71,49,11,24,3,66.假设在排序过程中,前3个记录已按关键码值递增的次序重新排列,构成一个有序序列:49,60,71.将待排序记录中的第4个记录(即11)插入上述有序序列,以得到一个新的含4个记录的有序序列.首先,应找到11的插入位置,再进行插入.可以

排序算法总结----比较类排序

概述:排序算法可分为比较性的排序,以及运算性的排序:这里详细介绍这些排序的原理,性能,实现,以及应用场合. 前面是维基百科的介绍,这里介绍几个比较典型的算法. 理论 计算复杂性理论 大O符号 全序关系 列表 稳定性 比较排序 自适应排序 排序网络 整数排序 交换排序 冒泡排序 鸡尾酒排序 奇偶排序 梳排序 侏儒排序 快速排序 臭皮匠排序 Bogo排序 选择排序 选择排序 堆排序 Smooth排序 笛卡尔树排序 锦标赛排序 循环排序 插入排序 插入排序 希尔排序 二叉查找树排序 图书馆排序 Pat

排序算法总结----运算类排序

运算排序 第一:计数排序 1:原理 对于每个输入数,确定小于该数的个数.这样可以直接把数放在输出数组的位置. 2:性能 最差时间复杂度 最优时间复杂度 平均时间复杂度 最差空间复杂度 注:稳定算法 3:应用 适合0~100的范围的数,当然可以和基排序结合而扩展数的范围. 4:实现 void CountingSort(int *A, int *B, int array_size, int k) { int i, value, pos; int * C=new int[k+1]; for(i=0;

排序算法-冒泡——插入——快排

冒泡排序,往两个方向泡,一个往小泡,一个网大泡 #include<stdio.h> #include<stdlib.h> #include<time.h> void bubble_sort(int *a,int n){ int temp; for(int i=0;i<n;i++) for(int j=0;j<n-i-1;j++){ if(a[j]>a[j+1]){ temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } } v

各种排序算法的一个类

#include<iostream> #define MAX 100 using namespace std; class Sample { int a[MAX]; int b[MAX]; int n; friend class Process; public: Sample(){n=0;} }; class Process { private: int s_psort(Sample &s,int first,int end); void sift(int k,int m,Sample

【轻松学排序算法】眼睛直观感受几种常用排序算法(转)

1 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性. 步骤: 从数列中挑出一个元素,称为 "基准"(pivot), 重新排序数列,所有元素

图解排序算法(一)之3种简单排序(选择、冒泡、直接插入)

先定义个交换数组元素的函数,供排序时调用 /** * 交换数组元素 * @param arr * @param a * @param b */ public static void swap(int []arr,int a,int b){ arr[a] = arr[a]+arr[b]; arr[b] = arr[a]-arr[b]; arr[a] = arr[a]-arr[b]; } 简单选择排序(O(n^2)) 简单选择排序是最简单直观的一种算法. 基本思想:每一趟从待排序的数据元素中选择最小

算法整理(二)---快速排序的两种实现方式:双边扫描和单边扫描

首先简单谈下快速排序的特点,时间复杂度O(nLog n),最差时间复杂度O(n^2),平均时间O(nLog n).因为用到了函数栈,空间复杂度为O(lg n),最差为O(n).是一种不稳定的排序方法.基本思想是分治法,这位大大的http://blog.csdn.net/morewindows/article/details/6684558 讲的非常清楚了,分治法+挖坑法,我就不多说了.就是以某个数为参照,使得左边的都小于他,右边的数都大于他.然后对他的左右两个区间采取同样的方法进行递归. 就其整

SQL Server 批量插入数据的两种方法

在SQL Server 中插入一条数据使用Insert语句,但是如果想要批量插入一堆数据的话,循环使用Insert不仅效率低,而且会导致SQL一系统性能问题.下面介绍SQL Server支持的两种批量数据插入方法:Bulk和表值参数(Table-Valued Parameters). 运行下面的脚本,建立测试数据库和表值参数. [c-sharp] view plaincopy --Create DataBase create database BulkTestDB; go use BulkTes