Java数据结构之插入排序与希尔排序

目录
  • 一、正文
    • 1.排序的概念及其运用
      • 1.1排序的概念
      • 1.2排序运用
      • 1.3常见的排序算法
    • 2.插入排序算法的实现
      • 2.1插入排序
  • 二、测试代码
  • 三、结语

一、正文

1.排序的概念及其运用

1.1排序的概念

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

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

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

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

1.2排序运用

看过排序的基础概念,可能有的小伙伴会问就算我学会了排序,但是在实际生活中有什么用吗?其实排序在生活中无处不在,比如说对一件商品不同维度的选择,又或者是对高校的排名,其实背后都存在着排序的思想,学好排序,能够帮助我们以另一种维度来观察生活中的方方面面并帮助我们更好地解决生活中的问题

1.3常见的排序算法

在数据结构这一块,我们常见的排序算法共有四种:

插入排序:直接插入排序、希尔排序

选择排序:选择排序、堆排序

交换排序:冒泡排序、快速排序

归并排序:归并排序

2.插入排序算法的实现

由于篇幅的关系,本篇我们主要介绍的是插入排序中的直接插入排序希尔排序,而直接插入排序又常常被称为插入排序。

2.1插入排序

2.1.1基本思想

直接插入排序是一种简单的插入排序法

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

        实际中我们玩扑克牌时,就用了插入排序的思想。当你摸了一张新牌,自然而然地就会与手上已有的牌堆进行一一比较,在比较之后将其放入其应该所处的位置。所以我们可能并不知道插入排序是什么,但我们潜意识的做法恰恰就符合了插入排序。

2.1.2直接插入排序

用比较书面的语言来描述直接插入排序:当插入第i(i>=1)个元素时,前面的 array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与 array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

但这么说可能有的小伙伴会不太理解,那么通俗地来讲吧。现在在你面前有一个乱序的数组,我们的目的是要将这个乱序的数组调整为升序或者降序

以升序为例叭,由于数组是无序的,因此我们需要从数组的第二个元素开始排序。为什么不是第一个呢,因为只有一个数字的的时候,你无法与其余元素比较,自然也就没有乱序一说,因此只有一个元素的时候我们就默认它是有序的。

在理解完为什么要从第二个元素开始排序后,现在我们就要进行元素的依次插入和排序了。先是第二个元素的插入和排序,在下图中我们会发现第二个元素是44,44大于第一个元素3,因此不需要挪动第二个元素。紧接着就是第三个元素的插入和排序,我们发现第三个元素38小于第二个元素44,不符合我们升序的预期,因此将44挪动到38的位置,在第二、三个元素有序后,我们发现38大于3,也就是第一、二个元素也是有序的,因此就无需再挪动第一个元素的位置,这时候我们已经确认38应该所处的是数组中第二个元素的位置,因此我们只需将38插入到第二个元素的位置即可。相信看到这里的小伙伴对后续元素的插入与排序应该是信手拈来了,

接下来就是代码的书写了。在代码上,我们该如何实现上述元素的插入与排序呢?我们采取了两个主要的变量“des”“end”,des就是我们所要插入的元素的初识下标,而end代表的是插入之前的序列的最后一个元素的下标,随着des的比较,end要不断向前移动,那么什么时候end的移动才会停止呢,也就是比较的结束,大致分为两种情况:①des所代表的元素大于end所代表的的元素 ②end已经来到序列的第一个元素,这时候des要么是第一个元素,或者是第二个元素。

具体图片和代码如下:

//插入排序[升序]
int* InsertSort(int* arr, int n)
{

	//整体排序
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;
		int des = arr[i];
		//单趟排序
		while (end >= 0)
		{
			if (des >= arr[end])
				break;
			if (des < arr[end])
			{
				arr[end + 1] = arr[end];
				end--;
			}
			arr[end+1] = des;
		}
	}
}

注:直接插入排序特性总结

①元素集合越接近有序,直接插入排序算法的时间效率越高

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

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

④ 稳定性:稳定

2.1.3希尔排序(缩小增量排序)

希尔排序法又称缩小增量法。

希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成整数个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后重复上述分组和排序的工作,当到达整数等于1时,所有记录在统一组内排好序。

通俗来讲,希尔排序就是多次的直接插入排序,不过除了最后一次直接插入排序之外的排序又和原本的直接插入排序有所不同。那么有的小伙伴看到这里可能就会问了为什么要进行多次的插入排序,单次的插入排序又和正常的插入排序不同在哪里呢?别着急,下面我们一个个回答。

先是为什么要多次的插入排序,看过上面对于插入排序的特性总结我们会发现,当元素的集合越接近有序,那么对其进行插入排序的时间效率就越高。因此希尔排序除了最后一次的排序是正常的插入排序之外的多次插入排序的目的就是不断的调整这个元素的集合,使其不断的接近有序

紧接着就是希尔排序除最后一次插入排序之外的插入排序与正常插入排序的差异。通过上面对插入排序的学习,我们会发现对于一个乱序的数组的来说,一个元素若想来到正确的位置必须要与其余元素一一比较,也就是一步步的挪动,这种挪动在数组元素个数少的情况下尚可,但当这个数组的元素个数很多的时候,以升序来说,想象这个数组内最大的元素位于数组的第一个位置,那么是不是就要将这个元素与数组内其余元素一一比较以后,才能来到数组的最后一个位置,但当我们加大比较的步伐,也就是增大相比较的两个元素之间的距离,那么这个元素是不是就可以更快的来到它应该所处的位置。放置于飞行棋的情境之下,插入排序每次都掷出1,而哈希排序除了最后一次的插入排序掷出的点数是1,其余的插入排序所掷出的点数是都是大于1的,所以可想而知,哈希排序能够更快的到达排序的终点。

为了方便小伙伴们的理解,这部分代码共分为两部分:①固定步伐的直接插入排序②哈希排序。

先是固定步伐的直接插入排序,先让我们通过图片来直观的看到数组数组内的元素通过这种操作后的变化。

//固定步伐的直接插入排序[升序]
void ShellSort(int* arr, int n)
{
	int gap = 3;
	int end;
	//有两种写法,看你要控制end,还是des
	/*for (int i=0; i < n-gap; i++)
	{
		int end = i;
		int des = arr[end + gap];
		while (end >= 0)
		{
			if (des >= arr[end])
				break;
			if (des < arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = des;
		}
	}*/

	for (int i = gap; i < n ; i++)
	{
		int end = i-gap;
		int des = arr[end + gap];
		while (end >= 0)
		{
			if (des >= arr[end])
				break;
			if (des < arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = des;
		}
	}
}

接着就是希尔排序

上述的代码是gap=3的情况下的直接插入排序,那么对于希尔排序而言,我们该对gap该如何选择呢?对于不同gap值的插入排序来说,我们会发现:gap越大,元素跳得越快,数组越不接近有序;而gap越小,元素跳的越慢,数组越接近有序。由于数组的大小不定,因此希尔排序也没有一个合适gap值适用于所有数组,显然,这个gap值一定是动态变化的。

对于gap的动态变化,常见的有两种:

①令gap等于数组的元素个数,每次插入排序后令gap除等2

②另一种则是令gap等于数组的元素个数,不过每次插入排序后令gap除以3再加1

无论哪种处理都能使gap动态变化并最后等于1,对数组进行一次插入排序,达到最后想要的效果。

代码如下:

//希尔排序
void ShellSortPlus(int* arr, int n)
{
	int gap=n;
	int end;
	while (gap > 1)
	{
	gap = gap / 2;

		for (int i=0; i < n - gap;i++)//有两种写法,看你要控制end,还是des
		{
			int end = i;
			int des = arr[end + gap];
			while (end >= 0)
			{
				if (des >= arr[end])
					break;
				if (des < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				arr[end + gap] = des;
			}
		}

	}
}

二、测试代码

为了方便小伙伴们测试排序后的效果,为大家提供了测试的代码并包含排序的具体代码和包含的头文件。

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

//插入排序[升序]
int* InsertSort(int* arr, int n)
{

	//整体排序
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;
		int des = arr[i];
		//单趟排序
		while (end >= 0)
		{
			if (des >= arr[end])
				break;
			if (des < arr[end])
			{
				arr[end + 1] = arr[end];
				end--;
			}
			arr[end+1] = des;
		}
	}
}

//固定步伐的直接插入排序[升序]
void ShellSort(int* arr, int n)
{
	int gap = 3;
	int end;
	//有两种写法,看你要控制end,还是des
	/*for (int i=0; i < n-gap; i++)
	{
		int end = i;
		int des = arr[end + gap];
		while (end >= 0)
		{
			if (des >= arr[end])
				break;
			if (des < arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = des;
		}
	}*/

	for (int i = gap; i < n ; i++)
	{
		int end = i-gap;
		int des = arr[end + gap];
		while (end >= 0)
		{
			if (des >= arr[end])
				break;
			if (des < arr[end])
			{
				arr[end + gap] = arr[end];
				end -= gap;
			}
			arr[end + gap] = des;
		}
	}
}

//希尔排序
void ShellSortPlus(int* arr, int n)
{
	int gap=n;
	int end;
	while (gap > 1)
	{
	gap = gap / 2;

		for (int i=0; i < n - gap;i++)//有两种写法,看你要控制end,还是des
		{
			int end = i;
			int des = arr[end + gap];
			while (end >= 0)
			{
				if (des >= arr[end])
					break;
				if (des < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				arr[end + gap] = des;
			}
		}

	}
}

//打印排序好的数组
void PrintSort(int*arr,int n)
{
	for(int i=0;i<n;i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

//测试插入排序
void Text1()
{
	int arr[10] = { 25,14,8,7,18,24,16,57,98,105 };
	InsertSort(arr, sizeof(arr) / sizeof(arr[0]));
	PrintSort(arr, sizeof(arr) / sizeof(arr[0]));
}

//测试固定步伐的插入排序
void Text2()
{
	int arr[10] = { 9,1,2,5,7,4,8,6,3,5};
	ShellSort(arr, sizeof(arr) / sizeof(arr[0]));
	PrintSort(arr, sizeof(arr) / sizeof(arr[0]));
}

//测试希尔排序
void Text3()
{
	int arr[10] = { 9,1,2,5,7,4,8,6,3,5 };
	ShellSortPlus(arr, sizeof(arr) / sizeof(arr[0]));
	PrintSort(arr, sizeof(arr) / sizeof(arr[0]));
}

int main()
{
	//Text1();
	Text2();
	//Text3();

	return 0;
}

三、结语

到此为止,本期关于插入排序和希尔排序的讲解就告一段落了,在后续的文章里还有继续介绍其他的排序及其实现

以上就是Java数据结构之插入排序与希尔排序的详细内容,更多关于Java 插入与希尔排序的资料请关注我们其它相关文章!

(0)

相关推荐

  • java数据结构之希尔排序

    希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本.希尔排序是非稳定排序算法. 希尔排序是基于插入排序的以下两点性质而提出改进方法的: 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率:         但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位. 实现: 先取一个正整数d1 < n, 把所有相隔d1的记录放一组,每个组内进行直接插入排序:然后d2 < d1,重复上述分组和排序操作:直至di = 1,即所有记录放进一个组中排序为止. 简

  • java 数据结构基本算法希尔排序

    C语言数据结构基本算法希尔排序 前言: 基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序, 然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序.当增量减到1时,进行直接插入排序后,排序完成. 实现代码: public class ShellSort { /** * 原理:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的 * 下标相差d.对每组

  • java数据结构与算法之插入排序详解

    本文实例讲述了java数据结构与算法之插入排序.分享给大家供大家参考,具体如下: 复习之余,就将数据结构中关于排序的这块知识点整理了一下,写下来是想与更多的人分享,最关键的是做一备份,为方便以后查阅. 排序 1.概念: 有n个记录的序列{R1,R2,.......,Rn}(此处注意:1,2,n 是下表序列,以下是相同的作用),其相应关键字的序列是{K1,K2,.........,Kn}.通过排序,要求找出当前下标序列1,2,......,n的一种排列p1,p2,........pn,使得相应关键

  • javascript数据结构之双链表插入排序实例详解

    本文实例讲述了javascript数据结构之双链表插入排序实现方法.分享给大家供大家参考,具体如下: 数组存储前提下,插入排序算法,在最坏情况下,前面的元素需要不断向后移,以便在插入点留出空位,让目标元素插入. 换成链表时,显然无需做这种大量移动,根据每个节点的前驱节点"指针",向前找到插入点后,直接把目标值从原链表上摘下,然后在插入点把链表断成二截,然后跟目标点重新接起来即可. <!doctype html> <html> <head> <t

  • Java数据结构及算法实例:插入排序 Insertion Sort

    /** * 选择排序的思想: * 每次循环前,数组左边都是部分有序的序列, * 然后选择右边待排元素,将其值保存下来 * 依次和左边已经排好的元素比较 * 如果小于左边的元素,就将左边的元素右移一位 * 直到和最左边的比较完成,或者待排元素不比左边元素小 */ package al; public class InsertionSort { public static void main(String[] args) { InsertionSort insertSort = new Insert

  • java数据结构之插入排序

    插入排序就是把当前待排序的元素插入到一个已经排好序的列表里面. 一个非常形象的例子就是右手抓取一张扑克牌,并把它插入左手拿着的排好序的扑克里面. 插入排序的最坏运行时间是O(n2), 所以并不是最优的排序算法. 如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数. 如果输入数组是逆序排列的,将出现最坏情况.平均情况与最坏情况一样,其时间代价是Θ(n2). 简单例子: public class Demo6 { public static void main(St

  • Java数据结构之插入排序与希尔排序

    目录 一.正文 1.排序的概念及其运用 1.1排序的概念 1.2排序运用 1.3常见的排序算法 2.插入排序算法的实现 2.1插入排序 二.测试代码 三.结语 一.正文 1.排序的概念及其运用 1.1排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次 序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[

  • java数据结构与算法之希尔排序详解

    本文实例讲述了java数据结构与算法之希尔排序.分享给大家供大家参考,具体如下: 这里要介绍的是希尔排序(缩小增量排序法). 希尔排序:通过比较相距一定间隔的元素来工作:各趟比较所用的距离(增量)随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止.是插入排序的一种,是针对直接插入排序算法的改进. 算法思想:先将要排序的序列按某个增量d分成若干个子序列,对每个子序列中全部元素分别进行直接插入排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序.当增量减到1时,整个要排序的数被分成一

  • Java 插入排序之希尔排序的实例

    Java 插入排序之希尔排序的实例 Java代码 /*希尔排序(Shell Sort)是插入排序的一种.其基本思想是:先取定一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1 * 个组,所有距离为d1的倍数的记录放在同一个组中,在各个组中进行插入排序:然后,取第二个增量d2<d1,重复上述的分组和排序, * 直至所取的增量dt=1(dt<dt-1<...<d2<d1),即所有记录放在同一组中进行直接插入排序为止. * new int[]{8,5,1,7,9,4,6}

  • Java数据结构之基于比较的排序算法基本原理及具体实现

    目录 1. 七大基于比较的排序-总览 1.1常见基于比较的排序分类 1.2时间复杂度,空间复杂度以及稳定性. 2.直接插入排序 2.1 直接插入排序的基本思想 2.2 直接插入排序动画演示 2.3 代码示例 2.4 时间复杂度,空间复杂度以及稳定性 3. 希尔排序 3.1 算法思想 3.2 图片演示 3.3 代码示例 3.4 时间复杂度,空间复杂度以及稳定性 4.选择排序 4.1 算法思想 4.2 动画演示 4.3 代码示例 4.4 时间复杂度,空间复杂度以及稳定性 5.堆排序 5.1 算法思想

  • java 中基本算法之希尔排序的实例详解

    java 中基本算法之希尔排序的实例详解 希尔排序(Shell Sort)是插入排序的一种.也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本.希尔排序是非稳定排序算法.该方法因DL.Shell于1959年提出而得名. 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止. 基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差

  • java数据结构与算法之桶排序实现方法详解

    本文实例讲述了java数据结构与算法之桶排序实现方法.分享给大家供大家参考,具体如下: 基本思想: 假定输入是由一个随机过程产生的[0, M)区间上均匀分布的实数.将区间[0, M)划分为n个大小相等的子区间(桶),将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <M辅助数组B[0..n-1]是一指针数组,指向桶(链表).将n个记录分布到各个桶中去.如果有多于一个记录分到同一个桶中,需要进行桶内排序.最后依次把各个桶中的记录列出来记得到有序序列. [桶-

  • Java数据结构与算法之选择排序(动力节点java学院整理)

    每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完. 代码 public class ChoseSort { //constructor without parameters public ChoseSort(){}; //constructor with parameters public int[] ChoseSort(int[] intArr){ for(int i=0;i<intArr.length-1;i++){ int

  • java数据结构与算法之奇偶排序算法完整示例

    本文实例讲述了java数据结构与算法之奇偶排序算法.分享给大家供大家参考,具体如下: 算法思想: 基本思路是奇数列排一趟序,偶数列排一趟序,再奇数排,再偶数排,直到全部有序 举例吧, 待排数组[6 2 4 1 5 9] 第一次比较奇数列,奇数列与它的邻居偶数列比较,如6和2比,4和1比,5和9比 [6 2 4 1 5 9] 交换后变成 [2 6 1 4 5 9] 第二次比较偶数列,即6和1比,5和5比 [2 6 1 4 5 9] 交换后变成 [2 1 6 4 5 9] 第三趟又是奇数列,选择的是

  • 带你了解Java数据结构和算法之高级排序

    目录 1.希尔排序 ①.直接插入排序 ②.希尔排序图解 ③.排序间隔选取 ④.knuth间隔序列的希尔排序算法实现 ⑤.间隔为2h的希尔排序 2.快速排序 ①.快速排序的基本思路 ②.快速排序的算法实现 ③.快速排序图示 ④.快速排序完整代码 ⑤.优化分析 总结 1.希尔排序 希尔排序是基于直接插入排序的,它在直接插入排序中增加了一个新特性,大大的提高了插入排序的执行效率.所以在讲解希尔排序之前,我们先回顾一下直接插入排序. ①.直接插入排序 直接插入排序基本思想是每一步将一个待排序的记录,插入

  • C语言深入探究直接插入排序与希尔排序使用案例讲解

    目录 一.直接插入排序 1.1直接插入排序引入 1.2直接插入排序的核心思想与算法分析 1.3实例说明 1.4直接插入排序代码实现 1.5直接插入排序性能分析 二.希尔排序 2.1希尔排序引入 2.2希尔排序的核心思想与算法分析 2.3实例说明 2.4希尔排序代码实现 2.5希尔排序性能分析 一.直接插入排序 1.1直接插入排序引入 排序是我们生活中经常会面对的问题,以打扑克牌为例,你摸的手牌肯定是杂乱的,你一定会将小牌移动到大牌的左面,大牌移动到小牌的右面,这样顺序就算理好了.这里我们的理牌方

随机推荐