排序算法白话实现

排序的实现过程,这个比任何理论都好懂,如果要定义的话,直接百科搜就是了

1、冒泡排序:

34,8,64,51,32,21

34与8比较:8,34,64,51,32,21

34与64比较:8,34,64,51,32,21

64与51比较:8,34,51,64,32,21

64与32比较:8,34,51,32,64,21

64与21比较:8,34,51,32,21,64(这样一趟结束了,需要判断本趟是否有进行交换,如果有,则进入下一趟冒泡)

8与34比较:8,34,51,32,21,64

34与51比较:8,34,51,32,21,64

51与32比较:8,34,32,51,21,64

51与21比较:8,34,32,21,51,64

51与64比较:8,34,32,21,51,64(这样一趟结束了,需要判断本趟是否有进行交换,如果有,则进入下一趟冒泡)

8与34比较:8,34,32,21,51,64

34与32比较:8,32,34,21,51,64

34与21比较:8,32,21,34,51,64

34与51比较:8,32,21,34,51,64

51与64比较:8,32,21,34,51,64(这样一趟结束了,需要判断本趟是否有进行交换,如果有,则进入下一趟冒泡)

8与32比较:8,32,21,34,51,64

32与21比较:8,21,32,34,51,64

后面还需要完成本趟的比较,因为没有变化了,所以没写;然后还要进行一躺的比较,因为本趟比较有进行交换,只有下一趟没有交换,才能让标志位判定排序结束

时间复杂度:O(n^2)

空间复杂度:O(1)

是否稳定:是

因为每次都是相邻两个值比较,如果遇到相等的值就不会进行交换,就不会造成相同值之间顺序先后的变化,所以是稳定的算法

/**
 * 冒泡排序
 *
 */
public class TestBubble {

	//注意算法的终止条件,优化终止条件,可以提供算法效率,此算法没有考虑到没有发生比较时,便停止排序
	public static void bubleSort(int a[]) {

		boolean flag=true;

		for (int i = 0; i < a.length - 1&&flag; i++) {
			flag=false;
			for (int j = 0; j < a.length-1 - i; j += 1) {

				if (a[j] > a[j + 1]) {
					swap(a, j, j+1);
					//每趟发生交换才进行下一趟排序,所以标示
					flag=true;
				}

			}

		}
	}

	private static void swap(int[] number, int i, int j) {
		int t;
		t = number[i];
		number[i] = number[j];
		number[j] = t;
	}

	public static void main(String[] args) {
		int[] a = new int[] {26, 3, 42, 13, 8, 0, 122 };
		bubleSort(a);
		for (int i = 0; i < a.length; i++) {
			System.out.println(a[i]);
		}
	}
}

2、(直接)插入排序:

34,8,64,51,32,21

34与8比较:8,34,64,51,32,21

64与8,34比较:8,34,64,51,32,21

51与8,34,64比较:8,34,51,64,32,21

32与8,34,51,64比较:8,32,34,51,64,21

21与8,32,34,51,64比较:8,21,32,34,51,64

需要注意的是:每次i位置上的值是与前面有序队列从左到有进行比较,也就是从小到大的比较;由于这样的比较只需要一趟,所以需要有个标志位来判定有序队列和初始化数列的长度是否一致,如果一直则结束排序

时间复杂度:O(n^2)

控件复杂度:O(1)

是否稳定:否

由于每次排序时,都已经是有序队列,如果Ai=Aj,且Ai在Aj前面;也就意味着Ai比Aj先排。而插入的条件是要插入的值小于那个位置的值,然后就插入到这个值的前面,所以所以,当遇到需要插入的Aj等于被比较的Ai值时,将会跳过,进行下一个位置的比较。所以,进行排序后,这样的位置顺序是不变的

3、选择排序:

34,8,64,51,32,21

从序列选出最小值8与第一个位置的值交换:8,34,64,51,32,21

从第一个位置右边的序列选出最小值21与第二个位置的值交换:8,21,64,51,32,34

从第二个位置右边的序列选出最小值32与第第三个位置的值交换:8,21,32,51,64,34

从第三个位置右边的序列选出最小值34与第四个位置的值交换:8,21,32,34,64,51

从第四个位置右边的序列选出最小值51与第五个位置的值交换:8,21,64,51,51,64

需要注意的是:这里的零界条件应该是:当原始队列中的值为空时,说明初始队列已经都被选择完了,也就排完了。选择排序可以把每次选的条件进行变化,可以是最小值,也可以是最大值,也可以最小的质数之类的,所以选择排序很灵活

时间复杂度:O(n^2)

控件复杂度:O(1)

是否稳定:否

因为每次的选择最小的,都需要进行一次位置上的交换。设Ai=Aj,且Ai在Aj的前面,但后面的最小值要与Ai交换,且这个最小值又在Aj的右边(这种情况相当合理的),此时交换,就把Ai换到了Aj的右边,虽然有几率再次换回来,那也仅仅是有可能,所以,这是不稳定的算法

4、快速排序(所有内部排序方法中最高好的,一大多数况下是最好的):

34,8,64,51,32,21

选择一个基准数,通常是第一个数 X = 34,产生两个指针,i=0,j为数组的长度-1,j=6-1=5

我们要做的是将比X小于等于的数放到X的左边,大于的数放到右边

之所以说是分治,是因为这种做法先是从右边开始找小于等于X的数,再从左边开始找大于X的数,当i=j的时候,就说明排序完毕

从j位置往左找一个小于等于X的数32将这个值复制到第一个位置:21,8,64,51,32,X;i =0j=5

从i位置往右找一个大于X的数64将这个值复制到上一次赋值的地方,21,8,X,51,32,64;i=2   j=5

从j位置往左找一个小于等于X的数32将这个值复制到上一次赋值的地方,21,8,32,51,X,64;i=2   j=4

从i位置往右找一个大于X的数51将这个数复制到上一次赋值的地方,21,8,32,X,51,64; i=3  j=4

由于,i<y是满足条件,所以当j自减一次后,就达到零界,21,8,32,51,51,64,i=3   j=3

最后将i位置上的值赋为X:21,8,32,34,51,64

这样就得到两个子集,{21,8,32},{34},{51,64}

对这两个子集进行上述相同的操作,直到序列的左边界大于等于右边的边界

需要注意的是,每次的分治,条件应该是相对的,可以是从右开始往左找大的数,从左开始往右找小于等于的数,也可以从反过来

时间复杂度:平均为O(nlog2n),最坏的情况为O(n^2)

空间复杂度:O(nlog2n)

是否稳定:否

因为,每次的赋值,很容就将原本是顺序的两个相同的值变成逆序的,如小于等于X的Ai=Aj,且Ai在Aj之前,那么第一次从从右往左找小于等于的值,极有可能将Ai移到前面。所以,不稳定

//快速排序
void quick_sort(int s[], int l, int r)
{
    if (l < r)
    {
<span style="white-space:pre">		</span>//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
<span style="white-space:pre">				</span>j--;  
            if(i < j) 
<span style="white-space:pre">				</span>s[i++] = s[j];
<span style="white-space:pre">			</span>
            while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
<span style="white-space:pre">				</span>i++;  
            if(i < j) 
<span style="white-space:pre">				</span>s[j--] = s[i];
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 递归调用 
        quick_sort(s, i + 1, r);
    }
}

5、归并排序:

针对的是两个已有序的序列,进行合并

A{8,34,64}

B{21,32,51}

将序A的第一个元素和序列B的第一个元素进行比较,将较小值放入另一个队列中:C{8},A{34,64},B{21,32,51}

重复上面的步骤,直到有一个序列为空,然后将另一个非空序列直接添加到序列C中,即为完成排序

需要注意的是,递归算法的空间复杂度一般都为O(nlog2n),但是那是指空间不释放的情况下,如快速排序,空间资源是不释放的,而归并排序,序列A和序列B的空间是随时释放的而C的空间是不断增加的

时间复杂度:O(nlog2n)

控件复杂度:O(n)

是否稳定:是

因为,在上述操作过程中,没有改变相同的值的位置的可能,所以稳定

6、堆排序

先将其转化成优先序列(堆),然后重复将根值输出,并把最后一个树叶的值移到根节点,从新下滤操作,得到一个优先序列(堆)。重复以上操作,直到最后一个

需要注意的是,下滤操作的过程,就是保证根节点的值比两个子节点的值都小,而且下滤过程中,优先选择子节点中最小的值

时间复杂度:O(nlog2n)

空间复杂度:O(1)

是否稳定:否

因为,在堆的左右子树中,有可能存在相同大小的值,而这个顺序是不可能控的,所以不稳定

7、希尔排序:

34,8,64,51,32,21

3排:{8,34},{51,64},{21,32}

1排:{8,21,32,34,51,64}

需要注意的是,n排中,n的值为数组长度整除2的值

时间复杂度:O(nlog2n)

空间复杂度:O(1)

是否稳定:否

因为,当一个队列中存在相同值且在不同的模块中,那么正在合并的时候,可能改变先后顺序,所以不稳定

算法分类图:

算法总结:

排序法 平均时间 最差情形 稳定度 额外空间 备注
冒泡 O(n2)     O(n2) 稳定 O(1) n小时较好
交换     O(n2)     O(n2) 不稳定 O(1) n小时较好
选择 O(n2) O(n2) 不稳定 O(1) n小时较好
插入 O(n2) O(n2) 稳定 O(1) 大部分已排序时较好
基数 O(logRB) O(logRB) 稳定 O(n)
B是真数(0-9),

R是基数(个十百)

Shell O(nlogn) O(ns) 1 不稳定 O(1) s是所选分组
快速 O(nlogn) O(n2) 不稳定 O(nlogn) n大时较好
归并 O(nlogn) O(nlogn) 稳定 O(1) n大时较好
O(nlogn) O(nlogn) 不稳定 O(1) n大时较好
时间: 2024-10-10 05:45:11

排序算法白话实现的相关文章

排序算法: 堆排序法

一,使用堆排序法之前,需要了解堆的特性: 1,堆一般都用数组的方式来存储,堆分为“最大堆”和“最小堆”: 2,以“最大堆”为例: (1)一个节点最多有两个子节点,即左右节点,每个节点都是一个堆: (2)父节点的值不小于子节点的值: (3)一个i节点,其父节点为(i-1)/2,左节点(2*i+1),右节点(2*i+2) 一个最大堆例子: 数组 int a[]: 83 78 81 48 17 27 二,将一个数组形成堆(以最大堆为例) 数组a[]: 27 48 81 78 17 83 形成最大堆思路

白话排序算法--选择排序

前言: 继续排序方法,紧接早上的插入排序,紧跟其后,现在跟新上选择排序算法 选择排序:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾.以此类推,直到所有元素均排序完毕. 情景描述: 新学期换了新体育老师,面对同样的排队情景,他说道:“我不管之前是怎么教的,之后都按照我的来做,大家自己进行选择排序”. 从第一位同学开始,和第二位同学比较,谁矮了,谁就记着目前自己是最矮的(可以站前面,嘿嘿), 然后再和第三位

【整理】常见排序算法及其时间复杂度总结

原文出处: 1. 白话经典算法系列之八 MoreWindows白话经典算法之七大排序总结篇 2. 面试常用算法总结--排序算法(java版) 3. 常见排序算法小结 本篇主要整理了冒泡排序,直接插入排序,直接选择排序,希尔排序,归并排序,快速排序,堆排序七种常见算法,是从上面三篇博文中摘抄整理的,非原创. 一.冒泡排序 主要思路是: 通过交换相邻的两个数变成小数在前大数在后,这样每次遍历后,最大的数就"沉"到最后面了.重复N次即可以使数组有序. 冒泡排序改进1: 在某次遍历中,如果没有

经典排序算法--希尔排序

算法简介 希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进.希尔排序又叫缩小增量排序. 白话理解: 我们依然已排队为例,加入队伍中,有一些小个子站在了队伍的后面,而一些大个子又站在了队伍的前面,这是如果再使用插入排序,那就太没有效率了.通常情况下,老师会先看一眼,然后将后面的小个子和前面的大个子互换位置.当这个队伍顺序差距不是特别明显后,再使用插入排序. 但是计算机没办法一眼看出来哪些元素差距比较大,于是我们可以每趟排序,根据对应的增量,将待排序列分割成若干长度为m

排序算法系列——快速排序

记录学习点滴 快速排序算法是一种很有趣的算法,短小精悍,性能强劲,对于大部分情况都可以胜任,但对极端环境难以应付. 快速排序我理解为:这是一个“以自我为中心的”+“分治法”思想的算法. 分治法不必多说,化繁为简,那就是逐个击破. 那什么是“以自我为中心”?顾名思义,就是每次都一个“我”,每个人都要围绕“我”行事,比“我”小的都去左边站着,比“我”他大的都去右边站着,而且“我”不去关心每一边都有谁,反正你没“我”大或者小就行.一旦“我”落位了妥帖了,“我”就不动了.然后再在左右两边分别产生新“我”

十种常见排序算法

1.常见算法分类 十种常见排序算法一般分为以下几种: (1)非线性时间比较类排序:交换类排序(快速排序和冒泡排序).插入类排序(简单插入排序和希尔排序).选择类排序(简单选择排序和堆排序).归并排序(二路归并排序和多路归并排序): (2)线性时间非比较类排序:计数排序.基数排序和桶排序. 总结: (1)在比较类排序中,归并排序号称最快,其次是快速排序和堆排序,两者不相伯仲,但是有一点需要注意,数据初始排序状态对堆排序不会产生太大的影响,而快速排序却恰恰相反. (2)线性时间非比较类排序一般要优于

常用排序算法的python实现和性能分析

http://www.cnblogs.com/wiki-royzhang/p/3614694.html 一年一度的换工作高峰又到了,HR大概每天都塞几份简历过来,基本上一天安排两个面试的话,当天就只能加班干活了.趁着面试别人的机会,自己也把一些基础算法和一些面试题整了一下,可以阶段性的留下些脚印——没办法,平时太忙,基本上没有时间写博客.面试测试开发的话,这些也许能帮得上一些. 这篇是关于排序的,把常见的排序算法和面试中经常提到的一些问题整理了一下.这里面大概有3个需要提到的问题: 虽然专业是数

排序算法系列:冒泡排序与双向冒泡排序

概述 排序算法应该算是一个比较热门的话题,在各个技术博客平台上也都有一些博文进行了一定程度的讲解.但还是希望能从自我完善的角度出发,可以更详细.全面.形象地表达这些算法的精髓.本文就先从最简单的冒泡排序开始说起,别说你已经彻底了解了冒泡排序算法(虽然一开始我也是这样以为的). 版权说明 本文链接:http://blog.csdn.net/lemon_tree12138/article/details/50474230 – Coding-Naga - 转载请注明出处 目录 概述 版权说明 目录 冒

各种排序算法总结篇(高速/堆/希尔/归并)

1.高速排序 交换排序有:冒泡(选择)排序和高速排序,冒泡和选择排序的时间复杂度太高,思想非常easy临时不讨论.高速排序基于一种分治的思想,逐步地使得序列有序. #include <iostream> #include <conio.h> using namespace std; int arrs[] = { 23, 65, 12, 3, 8, 76, 345, 90, 21, 75, 34, 61 }; int arrLen = sizeof(arrs) / sizeof(ar