算法之经典排序算法小归纳

前言

数据结构和算法是写代码的基础。基本功很重要,所谓根基深度决定成长高度。以前没吃好的饭,总有一天要回来吃的。这段时间项目不忙,回来吃饭,决定花一段时间捋一捋数据结构和算法的基础知识。

正文

    这篇博客简要总结了七个算法:冒泡排序,选择排序,插入排序,希尔排序,快速排序,归并排序和堆排序。本文所有的描述都是根据自己的理解手打的,为的是方便读懂,示例代码可以实现算法,但是不敢保证就是最优的。如描述内容有误,请指正。

好了开始吧..

1.冒泡排序

从数组的一端开始两两比较,依次将当前最值移动到数组另一端端的排序方法。经实测,在这几个算法中速度是最慢的。

代码:

/**
	 * 冒泡排序(由小到大)
	 *
	 * @param array
	 * @return
	 */
	private static void bubbleSequence(int[] array) {
		if (null != array) {
			long ts = System.currentTimeMillis();
			int length = array.length;
			for (int i = 0; i < length; i++) {
				for (int j = 0; j < length - 1 - i; j++) {
					if (array[j] > array[j + 1])
						exchange(array, j, j + 1);
				}
			}
			long te = System.currentTimeMillis();
			System.out.println("冒泡排序  time cost(ms):" + (te - ts));
		}
	}

2.选择排序

从数组的一端开始选取元素依次和其他所有元素对比,将最值依次交换到目标位置的排序方法。

代码:

/**
	 * 选择排序(由小到大)
	 *
	 * @param array
	 * @return
	 */
	private static void selectSequence(int[] array) {
		if (null != array) {
			long ts = System.currentTimeMillis();
			int length = array.length;
			for (int i = 0; i < length; i++) {
				for (int j = i + 1; j < length; j++) {
					if (array[j] < array[i])
						exchange(array, j, i);
				}
			}
			long te = System.currentTimeMillis();
			System.out.println("选择排序  time cost(ms):" + (te - ts));
		}
	}

3.插入排序

把数组中的无序序列元素依次插入到有序序列中的排序方法,对于部分有序序列效率很高。如果是完全无序序列,则初始有序序列长度为1.

代码:

/**
	 * 插入排序 由小到大
	 *
	 * @param array
	 * @return
	 */
	private static void insertSequence(int[] array) {
		long ts = System.currentTimeMillis();
		if (null != array) {
			int length = array.length;
			int temp = 0;

			// 开始插入排序,第i位依次在前i-1位中找位置
			for (int i = 1; i < length; i++) {
				if (array[i] > array[i - 1]) {// 被比较值比前面都大
					continue;
				}

				for (int j = 0; j < i - 1; j++) {// 前i位依次对比

					if (array[i] < array[j]) {
						temp = array[i];
						for (int k = i; k > j; k--) {
							array[k] = array[k - 1];// 被插入位置之后的元素依次后挪
						}
						array[j] = temp;// 插入值
						break;
					}
				}
			}

			long te = System.currentTimeMillis();
			System.out.println("插入排序  time cost(ms):" + (te - ts));
		}
	}

4.希尔排序

相比而言,前三种都是比较基础的排序方法,容易理解。从这里开始,要费点脑子了。希尔排序用到了基础排序方法中的插入排序,它的基本思想是:将数组按照一定的步长分成若干个子数组,(通俗的举个例子,就像让一排人报数,循环报1、2、3,报完过后喊1的人为一组,喊2的人为一组,喊3的人为一组,这一队人就被分成了三个小组。)然后对子数组进行插入排序,使之有序。然后缩小步长,继续分组,排序。等到步长为1时,排序完成。经实测希尔排序的效率是很高的,比前面三种算法速度快十几到几十倍不等。

代码:

/**
	 * 希尔排序
	 *
	 * @param array
	 * @return
	 */
	private static void shellSequence(int[] array) {
		if (null != array) {
			long ts = System.currentTimeMillis();
			int length = array.length;
			int stepLength = 1;// 步长
			// 计算初始步长
			while (stepLength < length / 3) {
				stepLength = stepLength * 3 + 1;
			}

			while (stepLength >= 1) {
				// 使间隔为h的数组变为有序
				for (int i = 0; i < stepLength; i++) // 直接插入排序
				{
					for (int j = i + stepLength; j < length; j += stepLength)
						if (array[j] < array[j - stepLength]) {
							int temp = array[j];
							int k = j - stepLength;
							while (k >= 0 && array[k] > temp) {
								array[k + stepLength] = array[k];
								k -= stepLength;
							}
							array[k + stepLength] = temp;
						}
				}

				stepLength /= 3;// 步长缩短
			}

			long te = System.currentTimeMillis();
			System.out.println("希尔排序  time cost(ms):" + (te - ts));
		}
	}

5.归并排序

归并排序依赖归并操作,即将两个已经排序的序列合并成一个序列的操作,归并排序的过程是:将一个数组拆分成两个子数组,再对子数组进行拆分......直到最后子数组的长度为1,然后子数组按顺序合并,两两合并,两两合并,直到恢复到原数组长度,排序完成。有一个生动的动图:

代码递归实现:

/**
	 * 归并排序
	 *
	 * 先将数组拆分为子序列,递归进行,直至分为所有子序列长度都为1,然后将两两子序列合并排序,合并完成后,排序完成.
	 *
	 * @param array
	 *            待排序数组
	 * @param result
	 *            作为挪动空间的辅助数组
	 * @return
	 */
	private static void mergeSequence(int[] array, int result[], int start, int end) {
		// 参考维基百科的动图,先拆再合,递归执行
		if (start >= end)
			return;

		if (null != array) {

			int length = end - start;
			int middle = (length >> 1) + start;
			int start1 = start, end1 = middle;
			int start2 = middle + 1, end2 = end;
			mergeSequence(array, result, start1, end1);
			mergeSequence(array, result, start2, end2);

			int k = start;
			while (start1 <= end1 && start2 <= end2) // 挨个比较值,合并排序
				result[k++] = array[start1] < array[start2] ? array[start1++] : array[start2++];
			while (start1 <= end1) // 合并1的尾巴
				result[k++] = array[start1++];
			while (start2 <= end2) // 合并2的尾巴
				result[k++] = array[start2++];
			for (k = start; k <= end; k++)// 合并后的值赋回原数组
				array[k] = result[k];

		}
	}

6.快速排序

快速排序的过程是,1.选一个元素作为基准值,将大于基准值的元素放到一边,小于基准值的元素放到另一边,形成左右两个子序列。然后对子序列继续进行这样的操作,直到子序列长度为1,排序完成。

代码:

/**
	 * 快速排序
	 * 按照某个基准值,将数组分为大于该值和小于该值的两个子序列,然后递归的对子序列继续按此方法排序,直到最终子序列长度为1时,排序完成.
	 * 小规模实验测得,排序效率并不稳定,时快时慢,与具体数组有关.
	 * @param array 待排序数组
	 * @param start
	 * @param end
	 */
	private static void quickSequence(int[] array,int start, int end) {
		if (start >= end)
			return;
		int mid = array[end];//将最后一位元素作为基准值
		int left = start, right = end - 1;
		//设定:左边序列小于基准值,右边序列大于基准值
		//从左右两边向中间分别搜索小于和大于基准值的元素,将两边不符合条件的元素相互交换
		while (left < right) {
			while (array[left] < mid && left < right)
				left++;
			while (array[right] >= mid && left < right)
				right--;
			exchange(array,left, right);
		}

		//搜索完毕后,如果左边的元素还大于基准值,则将其与基准值交换,这种情况一股出现在基准值恰好选成了序列中的最小值
		if (array[left] >= array[end])
			exchange(array,left, end);
		else
			left++;

		//递归操作左边序列
		quickSequence(array,start, left - 1);
		//递归操作右边序列
		quickSequence(array,left + 1, end);
	}

7.堆排序

堆排序是一种利用堆这种数据结构的排序算法。堆是一个完全二叉树,满足条件:所有父节点的值都不大于或不小于子节点。如果,父节点值大于子节点,叫大顶堆,反之,叫小顶堆。

堆排序排序过程是:

1.首先,将数组构造成堆结构,见下图:

根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+12*i+2

2.构造好基本结构后,开始调整,使数据符合堆的特点,调整时,应该从最后一个子树结构开始,依次往上调整。

3.调出堆后,开始排序。

排序的过程是,将堆顶的数据(最大或最小的数据)依次交换到最后,交换到最后的数据固定不动(因为已经调好顺序,视为有序序列),然后再调整出堆,继续交换最值到无序序列的最后。即调整的过程,是不断将当前阶段最大值往后挪的过程。当挪到后面的有序序列占满整个数组,排序完成。所以,堆排序的本质其实就是选择排序,但是它比直接选择排序减少了许多重复的比较过程,效率更高。

所以,堆排序的最重要一个操作,就是调整堆结构。

下面演示一个示例

{3,4,6,7,2,1,8,5}

1.构造堆结构

2.调整后,构造出大顶堆

3.构造出对数据结构后开始排序

按照上面第三步,此时,数组中最大的数据已经在堆顶了,要排出由小到大的数据,要依次将最大的数挪到最后。

红色框为待交换项,绿色框为已固定的有序部分。

每次调整,将无序部分构建为大顶堆结构。每次交换,将最值交换到无序序列的最后。

如果按照固定代码逻辑,到上面一步还会继续调整两轮,然后排序完成。

代码:

package com.test;

/**
 * 堆排序
 *
 * @author Administrator
 *
 */
public class HeapSequence {

	/**
	 * 整理堆 (整理为大顶堆)
	 *
	 * @param array
	 */
	public void createHeap(int[] array) {
		if (null != array) {

			int startPst = getFatherPosition(array.length - 1);
			for (int i = startPst; i >= 0; i--) {
				sortNode(array, i, array.length);
			}
		}
	}

	/**
	 * 排序
	 *
	 * @param array
	 */
	public void sortHeap(int[] array) {
		if (null != array) {
			for (int i = array.length - 1; i > 0; i--) {
				exchange(array, 0, i);
				sortNode(array, 0, i);
			}
		}
	}

	public void sort(int[] array) {
		createHeap(array);
		sortHeap(array);
	}

	/**
	 * 整理单个节点的堆结构
	 *
	 * @param array
	 *            目标数组
	 * @param nodePosition
	 *            节点位置
	 */
	private void sortNode(int[] array, int nodePosition, int heapSize) {
		int leftChildPst = getLeftChildPosition(nodePosition);
		int rightChildPst = getRightChildPosition(nodePosition);

		int largestPosition = nodePosition;
		if (leftChildPst < heapSize && array[largestPosition] < array[leftChildPst]) {
			largestPosition = leftChildPst;
		}

		if (rightChildPst < heapSize && array[largestPosition] < array[rightChildPst]) {
			largestPosition = rightChildPst;
		}

		// 子节点值有变化,继续整理子节点
		if (largestPosition != nodePosition) {
			exchange(array, largestPosition, nodePosition);
			sortNode(array, largestPosition, heapSize);
		}

	}

	/**
	 * 父节点位置
	 *
	 * @param currPst
	 * @return
	 */
	public int getFatherPosition(int currPst) {
		return (currPst - 1) / 2;
	}

	/**
	 * 左边子节点位置
	 *
	 * @param currPst
	 * @return
	 */
	public int getLeftChildPosition(int currPst) {
		return currPst * 2 + 1;
	}

	/**
	 * 右边子节点位置
	 *
	 * @param currPst
	 * @return
	 */
	public int getRightChildPosition(int currPst) {
		return currPst * 2 + 2;
	}

	/**
	 * 交换数组中两个位置的值
	 *
	 * @param a
	 *            数组
	 * @param x
	 *            位置1
	 * @param y位置2
	 */
	private static void exchange(int[] a, int x, int y) {
		int temp = 0;
		temp = a[x];
		a[x] = a[y];
		a[y] = temp;
	}
}

最后,随机对长度为十万的数组测试一次排序,示例代码在本电脑上的运行速度:

资源代码下载:点击打开链接

堆排序资源代码:点击打开链接

时间: 2024-12-13 04:17:18

算法之经典排序算法小归纳的相关文章

[算法] 十个经典排序算法

动图演示参考:https://www.cnblogs.com/onepixel/articles/7674659.html 基数排序参考:https://blog.csdn.net/double_happiness/article/details/72452243 1.常见的排序算法 2.算法分析 3.算法的实现 1)排序类 1 #ifndef _SORT_H_ 2 #define _SORT_H_ 3 4 #include<vector> 5 6 class Sort { 7 public:

【最全】经典排序算法(C语言)

本文章包括所有基本排序算法(和其中一些算法的改进算法): 直接插入排序.希尔排序.直接选择排序.堆排序.冒泡排序.快速排序.归并排序.基数排序. 算法复杂度比较: 算法分类 一.直接插入排序 一个插入排序是另一种简单排序,它的思路是:每次从未排好的序列中选出第一个元素插入到已排好的序列中. 它的算法步骤可以大致归纳如下: 从未排好的序列中拿出首元素,并把它赋值给temp变量: 从排好的序列中,依次与temp进行比较,如果元素比temp大,则将元素后移(实际上放置temp的元素位置已经空出) 直到

经典排序算法 - 冒泡排序Bubble sort

 原文出自于 http://www.cnblogs.com/kkun/archive/2011/11/23/bubble_sort.html 经典排序算法 - 冒泡排序Bubble sort 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序, 原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 | 第一趟排序(外循环) 第

C#实现所有经典排序算法

C# 实现所有经典排序算法 1 .选择排序 选择排序 原理: 选择排序是从冒泡排序演化而来的,每一轮比较得出最小的那个值,然后依次和每轮比较的第一个值进行交换. 目的:按从小到大排序. 方法:假设存在数组:72, 54, 59, 30, 31, 78, 2, 77, 82, 72 第一轮依次比较相邻两个元素,将最小的一个元素的索引和值记录下来,然后和第一个元素进行交换. 如上面的数组中,首先比较的是72,54,记录比较小的索引是54的索引1.接着比较54和59,比较小的索引还是1.直到最后得到最

经典排序算法的PHP实现类

近期广受笔试摧残,对于各种排序也是晕头转向. 更坑爹的是貌似大多都是用C++.Java实现相关算法,让我搞PHP的情何以堪,更何况,PHP本身就有排序函数sort(),其实来说,是很简单的,这也可能是为什么不用PHP进行排序吧. 但考虑到PHP毕竟也是一门面向对象的语言吧,我们利用原生的语法,也是可以实现经典排序算法的,先不说性能如何,切不要妄自菲薄吧. 下面为具体的经典排序算法的PHP实现类. <?php /** * Author: helen * CreateTime: 2016/4/15

经典排序算法 - 归并排序Merge sort

经典排序算法 - 归并排序Merge sort 原理,把原始数组分成若干子数组,对每个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到所有合并完,形成有序的数组 举例 无序数组[6 2 4 1 5 9] 先看一下每一个步骤下的状态,完了再看合并细节 第一步 [6 2 4 1 5 9]原始状态 第二步 [2 6] [1 4] [5 9]两两合并排序,排序细节后边介绍 第三步 [1 2 4 6] [5 9]继续两组两组合并 第四步 [1 2 4 5 6 9]合并完成,排序完成 输出结

(转载)经典排序算法-希尔排序

经典排序算法 - 希尔排序Shell sort 希尔排序Shell Sort是基于插入排序的一种改进,同样分成两部分, 第一部分,希尔排序介绍 第二部分,如何选取关键字,选取关键字是希尔排序的关键 第一块希尔排序介绍 准备待排数组[6 2 4 1 5 9] 首先需要选取关键字,例如关键是3和1(第一步分成三组,第二步分成一组),那么待排数组分成了以下三个虚拟组: [6 1]一组 [2 5]二组 [4 9]三组 看仔细啊,不是临近的两个数字分组,而是3(分成了三组)的倍数的数字分成了一组, 就是每

七种经典排序算法最全攻略

经典排序算法在面试中占有很大的比重,也是基础.包括冒泡排序,插入排序,选择排序,希尔排序,归并排序,快速排序,堆排序.希望能帮助到有需要的同学.全部程序采用JAVA实现. 本篇博客所有排序实现均默认从小到大. 一.冒泡排序 BubbleSort 介绍: 冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来. 步骤: 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对第0个到第n-1个数据做同样的工作.这时,最大的数就"浮"到了

经典排序算法(Java实现)

以下程序均将数据封装于DataWrap数据包装类中,如下所示: 1 //数据包装类 2 class DataWrap implements Comparable<DataWrap> 3 { 4 int data; 5 String flag; 6 public DataWrap(int data,String flag) 7 { 8 this.data = data; 9 this.flag = flag; 10 } 11 //重写compareTo方法 12 public int compa