码迷,mamicode.com
首页 > 编程语言 > 详细

排序算法白话实现

时间:2016-03-27 01:48:34      阅读:208      评论:0      收藏:0      [点我收藏+]

标签:

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


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大时较好






排序算法白话实现

标签:

原文地址:http://blog.csdn.net/u013755250/article/details/50988349

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!